feat: update dependencies and integrate skin manager
- Added `dartamp_skin` as a local dependency in `pubspec.yaml`. - Updated `path_provider` to version ^2.1.5. - Modified `test_driver/app.dart` to initialize `SkinManager` and pass it to `DartampApp`. - Updated `generated_plugins.cmake` to include `jni` in the FFI plugin list. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
+77
-39
@@ -1,6 +1,9 @@
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
import "package:dartamp_skin/dartamp_skin.dart";
|
||||
import "package:desktop_multi_window/desktop_multi_window.dart";
|
||||
import "package:file_picker/file_picker.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:window_manager/window_manager.dart";
|
||||
@@ -8,8 +11,7 @@ import "package:window_manager/window_manager.dart";
|
||||
import "src/common_widgets.dart";
|
||||
import "src/main_window/main_window.dart";
|
||||
import "src/player/player_state.dart";
|
||||
import "src/skin/classic_skin.dart";
|
||||
import "src/skin/skin_loader.dart";
|
||||
import "src/skin/skin_manager.dart";
|
||||
import "src/windows/sub_window_app.dart";
|
||||
import "src/windows/sub_window_channel.dart";
|
||||
import "src/windows/window_args.dart";
|
||||
@@ -35,6 +37,7 @@ Future<void> main(List<String> args) async {
|
||||
type: subWindowArgs.type,
|
||||
initialShade: subWindowArgs.shade,
|
||||
initialAlwaysOnTop: subWindowArgs.alwaysOnTop,
|
||||
initialSkinName: subWindowArgs.skinName,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -46,6 +49,7 @@ Future<void> main(List<String> args) async {
|
||||
final eqOpen = await PlayerState.loadEqOpen();
|
||||
final plOpen = await PlayerState.loadPlOpen();
|
||||
final uiScale = await PlayerState.loadUiScale();
|
||||
final skinManager = await SkinManager.load();
|
||||
await windowManager.ensureInitialized();
|
||||
runApp(
|
||||
DartampApp(
|
||||
@@ -54,6 +58,7 @@ Future<void> main(List<String> args) async {
|
||||
initialEqOpen: eqOpen,
|
||||
initialPlOpen: plOpen,
|
||||
initialUiScale: uiScale,
|
||||
skinManager: skinManager,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -75,6 +80,7 @@ class DartampApp extends StatelessWidget {
|
||||
required this.initialEqOpen,
|
||||
required this.initialPlOpen,
|
||||
required this.initialUiScale,
|
||||
required this.skinManager,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -83,6 +89,7 @@ class DartampApp extends StatelessWidget {
|
||||
final bool initialEqOpen;
|
||||
final bool initialPlOpen;
|
||||
final double initialUiScale;
|
||||
final SkinManager skinManager;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -94,6 +101,7 @@ class DartampApp extends StatelessWidget {
|
||||
initialEqOpen: initialEqOpen,
|
||||
initialPlOpen: initialPlOpen,
|
||||
initialUiScale: initialUiScale,
|
||||
skinManager: skinManager,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -106,6 +114,7 @@ class WinampRoot extends StatefulWidget {
|
||||
required this.initialEqOpen,
|
||||
required this.initialPlOpen,
|
||||
required this.initialUiScale,
|
||||
required this.skinManager,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -114,6 +123,7 @@ class WinampRoot extends StatefulWidget {
|
||||
final bool initialEqOpen;
|
||||
final bool initialPlOpen;
|
||||
final double initialUiScale;
|
||||
final SkinManager skinManager;
|
||||
|
||||
@override
|
||||
State<WinampRoot> createState() => _WinampRootState();
|
||||
@@ -121,12 +131,14 @@ class WinampRoot extends StatefulWidget {
|
||||
|
||||
class _WinampRootState extends State<WinampRoot> {
|
||||
late final PlayerState _player;
|
||||
late final SkinManager _skins;
|
||||
bool _windowReady = false;
|
||||
bool _wasShade = false;
|
||||
double _wasScale = 1.0;
|
||||
bool _wasAlwaysOnTop = false;
|
||||
bool _wasEqOpen = false;
|
||||
bool _wasPlOpen = false;
|
||||
String _wasSkinName = baseSkinName;
|
||||
WindowController? _eqController;
|
||||
WindowController? _plController;
|
||||
StreamSubscription<void>? _windowsSubscription;
|
||||
@@ -141,12 +153,15 @@ class _WinampRootState extends State<WinampRoot> {
|
||||
plOpen: widget.initialPlOpen,
|
||||
uiScale: widget.initialUiScale,
|
||||
);
|
||||
_skins = widget.skinManager;
|
||||
_wasShade = widget.initialWindowShade;
|
||||
_wasScale = widget.initialUiScale;
|
||||
_wasAlwaysOnTop = widget.initialAlwaysOnTop;
|
||||
_wasEqOpen = widget.initialEqOpen;
|
||||
_wasPlOpen = widget.initialPlOpen;
|
||||
_wasSkinName = _skins.active.name;
|
||||
_player.addListener(_onPlayerChanged);
|
||||
_skins.addListener(_onSkinChanged);
|
||||
|
||||
// Set up method channel for macOS menu actions
|
||||
_winampChannel.setMethodCallHandler(_handleMethodCall);
|
||||
@@ -171,6 +186,7 @@ class _WinampRootState extends State<WinampRoot> {
|
||||
void dispose() {
|
||||
_windowsSubscription?.cancel();
|
||||
_player.removeListener(_onPlayerChanged);
|
||||
_skins.removeListener(_onSkinChanged);
|
||||
_player.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -249,6 +265,30 @@ class _WinampRootState extends State<WinampRoot> {
|
||||
_player.setPlOpen(!_player.plOpen);
|
||||
}
|
||||
|
||||
void _onSkinChanged() {
|
||||
final name = _skins.active.name;
|
||||
if (name == _wasSkinName) return;
|
||||
_wasSkinName = name;
|
||||
_eqController?.invokeMethod("setSkin", name);
|
||||
_plController?.invokeMethod("setSkin", name);
|
||||
}
|
||||
|
||||
Future<void> _importSkin() async {
|
||||
final result = await FilePicker.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: const ["wsz", "zip"],
|
||||
dialogTitle: "Load Winamp Skin",
|
||||
);
|
||||
final path = result?.files.single.path;
|
||||
if (path == null) return;
|
||||
try {
|
||||
final bytes = await File(path).readAsBytes();
|
||||
await _skins.importArchive(bytes, fileName: path.split("/").last);
|
||||
} catch (_) {
|
||||
// ignore: a failed import leaves the active skin unchanged
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openSubWindow(SubWindowType type) async {
|
||||
final isEq = type == SubWindowType.eq;
|
||||
var controller = isEq ? _eqController : _plController;
|
||||
@@ -264,6 +304,7 @@ class _WinampRootState extends State<WinampRoot> {
|
||||
shade: isEq ? _player.eqShade : _player.plShade,
|
||||
alwaysOnTop: _player.alwaysOnTop,
|
||||
uiScale: _player.uiScale,
|
||||
skinName: _skins.active.name,
|
||||
).encode(),
|
||||
hiddenAtLaunch: true,
|
||||
),
|
||||
@@ -329,6 +370,8 @@ class _WinampRootState extends State<WinampRoot> {
|
||||
case "setUIScale":
|
||||
final arguments = call.arguments as Map;
|
||||
_player.setUiScale((arguments["scale"] as num).toDouble());
|
||||
case "importSkin":
|
||||
await _importSkin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,45 +399,40 @@ class _WinampRootState extends State<WinampRoot> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<ClassicSkin>(
|
||||
future: loadBaseSkin(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return ColoredBox(
|
||||
color: const Color(0xFF00FF00),
|
||||
child: Center(child: Text("Skin load failed: ${snapshot.error}")),
|
||||
);
|
||||
}
|
||||
if (!snapshot.hasData) {
|
||||
return const ColoredBox(color: Colors.black);
|
||||
}
|
||||
return AnimatedBuilder(
|
||||
animation: _player,
|
||||
builder: (context, _) {
|
||||
final scale = _player.uiScale;
|
||||
final height =
|
||||
(_player.windowShade ? _shadeSize : _mainSize).height;
|
||||
return ColoredBox(
|
||||
color: const Color(0xFF000000),
|
||||
child: DragSurface(
|
||||
draggable: _windowReady,
|
||||
onWindowShadeToggle: _player.toggleWindowShade,
|
||||
child: SizedBox(
|
||||
width: _mainSize.width * scale,
|
||||
height: height * scale,
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
alignment: Alignment.topLeft,
|
||||
filterQuality: FilterQuality.none,
|
||||
child: MainWindowView(
|
||||
skin: snapshot.data!,
|
||||
player: _player,
|
||||
),
|
||||
),
|
||||
final skin = _skins.skin;
|
||||
if (skin == null) {
|
||||
return const ColoredBox(color: Colors.black);
|
||||
}
|
||||
if (_skins.lastError != null) {
|
||||
return ColoredBox(
|
||||
color: const Color(0xFF00FF00),
|
||||
child: Center(child: Text("Skin load failed: ${_skins.lastError}")),
|
||||
);
|
||||
}
|
||||
return AnimatedBuilder(
|
||||
animation: Listenable.merge([_player, _skins]),
|
||||
builder: (context, _) {
|
||||
final scale = _player.uiScale;
|
||||
final height = (_player.windowShade ? _shadeSize : _mainSize).height;
|
||||
return ColoredBox(
|
||||
color: const Color(0xFF000000),
|
||||
child: DragSurface(
|
||||
draggable: _windowReady,
|
||||
onWindowShadeToggle: _player.toggleWindowShade,
|
||||
child: SizedBox(
|
||||
width: _mainSize.width * scale,
|
||||
height: height * scale,
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
alignment: Alignment.topLeft,
|
||||
filterQuality: FilterQuality.none,
|
||||
child: MainWindowView(
|
||||
skin: skin,
|
||||
player: _player,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user