Files
dartamp/macos/Runner/AppDelegate.swift
T
hans 2f5edfc512 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>
2026-08-09 21:14:54 +02:00

169 lines
5.2 KiB
Swift

import Cocoa
import FlutterMacOS
@main
class AppDelegate: FlutterAppDelegate {
private let winampChannelName = "winamp"
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
override func applicationWillFinishLaunching(_ notification: Notification) {
super.applicationWillFinishLaunching(notification)
// Register the channel handler eagerly so Dart's startup menu-state sync
// is not dropped. `applicationDidFinishLaunching` is never delivered in
// this Flutter version, so this is the earliest reliable point. The
// FlutterViewController is already attached here, but poll briefly as a
// fallback for other Flutter versions where it may not be yet.
registerWinampChannelWhenReady()
}
private func registerWinampChannelWhenReady() {
guard winampChannel != nil else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
self?.registerWinampChannelWhenReady()
}
return
}
}
private var flutterController: FlutterViewController? {
return mainFlutterWindow?.contentViewController as? FlutterViewController
}
/// Lazily-created channel back to Dart. The handler is registered once so
/// Dart can report state (always-on-top, EQ/PL open, UI scale) to update
/// the matching menu checkmarks.
private var winampChannel: FlutterMethodChannel? {
if _winampChannel == nil, let controller = flutterController {
let channel = FlutterMethodChannel(
name: winampChannelName,
binaryMessenger: controller.engine.binaryMessenger
)
channel.setMethodCallHandler(handleMethodCall)
_winampChannel = channel
}
return _winampChannel
}
private var _winampChannel: FlutterMethodChannel?
private func handleMethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "setAlwaysOnTop":
if let args = call.arguments as? [String: Any],
let on = args["isAlwaysOnTop"] as? Bool {
setMenuItemState(id: "win-always-on-top", title: "Always on Top", on: on)
}
result(nil)
case "setWindowShade":
if let args = call.arguments as? [String: Any], let on = args["on"] as? Bool {
setMenuItemState(id: "win-window-shade", title: "Window Shade", on: on)
}
result(nil)
case "setEqOpen":
if let args = call.arguments as? [String: Any], let open = args["open"] as? Bool {
setMenuItemState(id: "win-equalizer", title: "Equalizer", on: open)
}
result(nil)
case "setPlOpen":
if let args = call.arguments as? [String: Any], let open = args["open"] as? Bool {
setMenuItemState(id: "win-playlist", title: "Playlist", on: open)
}
result(nil)
case "setUiScale":
if let args = call.arguments as? [String: Any], let scale = args["scale"] as? Double {
setUiScaleMenuState(scale)
}
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
private func setMenuItemState(id: String, title: String, on: Bool) {
menuItem(id: id, title: title)?.state = on ? .on : .off
}
private func setUiScaleMenuState(_ scale: Double) {
let targetId: String
let targetTitle: String
if scale == 1.0 {
targetId = "win-ui-scale-1x"
targetTitle = "1.0x"
} else if scale == 2.0 {
targetId = "win-ui-scale-2x"
targetTitle = "2.0x"
} else {
targetId = "win-ui-scale-3x"
targetTitle = "3.0x"
}
for (id, title) in [
("win-ui-scale-1x", "1.0x"),
("win-ui-scale-2x", "2.0x"),
("win-ui-scale-3x", "3.0x"),
] {
setMenuItemState(id: id, title: title, on: id == targetId && title == targetTitle)
}
}
private func menuItem(id: String, title: String) -> NSMenuItem? {
guard let menu = NSApplication.shared.mainMenu else { return nil }
return findMenuItem(id: id, title: title, in: menu)
}
private func findMenuItem(id: String, title: String, in menu: NSMenu) -> NSMenuItem? {
for item in menu.items {
if item.identifier?.rawValue == id || item.title == title {
return item
}
if let submenu = item.submenu,
let found = findMenuItem(id: id, title: title, in: submenu) {
return found
}
}
return nil
}
private func send(_ method: String, _ arguments: Any? = nil) {
winampChannel?.invokeMethod(method, arguments: arguments)
}
@IBAction func toggleAlwaysOnTop(_ sender: NSMenuItem) {
send("toggleAlwaysOnTop")
}
@IBAction func toggleWindowShade(_ sender: NSMenuItem) {
send("toggleWindowShade")
}
@IBAction func toggleEqualizer(_ sender: NSMenuItem) {
send("toggleEq")
}
@IBAction func togglePlaylist(_ sender: NSMenuItem) {
send("togglePlaylist")
}
@IBAction func setUIScale1x(_ sender: NSMenuItem) {
send("setUIScale", ["scale": 1.0])
}
@IBAction func setUIScale2x(_ sender: NSMenuItem) {
send("setUIScale", ["scale": 2.0])
}
@IBAction func setUIScale3x(_ sender: NSMenuItem) {
send("setUIScale", ["scale": 3.0])
}
@IBAction func importSkin(_ sender: NSMenuItem) {
send("importSkin")
}
}