Add support for new plugins: DesktopMultiWindow, ScreenRetrieverWindows, and WindowManager

- Registered DesktopMultiWindowPlugin, ScreenRetrieverWindowsPluginCApi, and WindowManagerPlugin in generated_plugin_registrant.cc.
- Updated generated_plugins.cmake to include new plugins in the build configuration.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-08-09 20:46:55 +02:00
parent a121d65cf3
commit 4f4f7bffdf
82 changed files with 6380 additions and 16 deletions
+1
View File
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
+1
View File
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
@@ -5,6 +5,16 @@
import FlutterMacOS
import Foundation
import desktop_multi_window
import file_picker
import screen_retriever_macos
import shared_preferences_foundation
import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterMultiWindowPlugin.register(with: registry.registrar(forPlugin: "FlutterMultiWindowPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}
+42
View File
@@ -0,0 +1,42 @@
platform :osx, '10.15'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_macos_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end
+22
View File
@@ -0,0 +1,22 @@
PODS:
- desktop_multi_window (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
DEPENDENCIES:
- desktop_multi_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
EXTERNAL SOURCES:
desktop_multi_window:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos
FlutterMacOS:
:path: Flutter/ephemeral
SPEC CHECKSUMS:
desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009
COCOAPODS: 1.16.2
+99 -3
View File
@@ -21,6 +21,7 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
0471E69D9FD050B2CDB4A8BD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F54480D5EF2C501C76D7D9C /* Pods_RunnerTests.framework */; };
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
@@ -28,6 +29,7 @@
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
BE116268E3D0BBBABD7802EC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24D571491B1F25D3AE3936E5 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -61,11 +63,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
24D571491B1F25D3AE3936E5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* dartamp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dartamp.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* dartamp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dartamp.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@@ -77,9 +80,16 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
47EB8DE62CB982663CA7EF82 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
8B86A0CB44BEDB3AB807A17E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
8F54480D5EF2C501C76D7D9C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
9F1EFC27CD8FB3105B83500C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
BF25CD991CA9677301FB7240 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
C9DBFFD945993325E75720B3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
CF96BBFE89B26B4785BF743A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -87,6 +97,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0471E69D9FD050B2CDB4A8BD /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -95,6 +106,7 @@
buildActionMask = 2147483647;
files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
BE116268E3D0BBBABD7802EC /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -128,6 +140,7 @@
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
A8774750F57A977424BF3D95 /* Pods */,
);
sourceTree = "<group>";
};
@@ -176,9 +189,25 @@
path = Runner;
sourceTree = "<group>";
};
A8774750F57A977424BF3D95 /* Pods */ = {
isa = PBXGroup;
children = (
CF96BBFE89B26B4785BF743A /* Pods-Runner.debug.xcconfig */,
BF25CD991CA9677301FB7240 /* Pods-Runner.release.xcconfig */,
C9DBFFD945993325E75720B3 /* Pods-Runner.profile.xcconfig */,
8B86A0CB44BEDB3AB807A17E /* Pods-RunnerTests.debug.xcconfig */,
47EB8DE62CB982663CA7EF82 /* Pods-RunnerTests.release.xcconfig */,
9F1EFC27CD8FB3105B83500C /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
24D571491B1F25D3AE3936E5 /* Pods_Runner.framework */,
8F54480D5EF2C501C76D7D9C /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -190,6 +219,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
3181403DCEED235D9945450A /* [CP] Check Pods Manifest.lock */,
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
@@ -208,11 +238,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
FFA63217700E28C919120952 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
561B06D2121E64602532D415 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -268,7 +300,7 @@
);
mainGroup = 33CC10E42044A3C60003C045;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
projectDirPath = "";
@@ -301,6 +333,28 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3181403DCEED235D9945450A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -339,6 +393,45 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
561B06D2121E64602532D415 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
FFA63217700E28C919120952 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -390,6 +483,7 @@
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8B86A0CB44BEDB3AB807A17E /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@@ -404,6 +498,7 @@
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 47EB8DE62CB982663CA7EF82 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@@ -418,6 +513,7 @@
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9F1EFC27CD8FB3105B83500C /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@@ -712,7 +808,7 @@
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
+3
View File
@@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
+151
View File
@@ -3,6 +3,8 @@ import FlutterMacOS
@main
class AppDelegate: FlutterAppDelegate {
private let winampChannelName = "winamp"
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
@@ -10,4 +12,153 @@ class AppDelegate: FlutterAppDelegate {
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])
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
+45
View File
@@ -320,6 +320,51 @@
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="sep-window-1"/>
<menuItem title="Always on Top" keyEquivalent="t" keyEquivalentModifierMask="command" id="win-always-on-top">
<connections>
<action selector="toggleAlwaysOnTop:" target="Voe-Tx-rLC" id="act-toggle-always-on-top"/>
</connections>
</menuItem>
<menuItem title="Window Shade" id="win-window-shade">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleWindowShade:" target="Voe-Tx-rLC" id="act-toggle-window-shade"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="sep-window-2"/>
<menuItem title="Equalizer" keyEquivalent="e" keyEquivalentModifierMask="command" id="win-equalizer">
<connections>
<action selector="toggleEqualizer:" target="Voe-Tx-rLC" id="act-toggle-equalizer"/>
</connections>
</menuItem>
<menuItem title="Playlist" keyEquivalent="l" keyEquivalentModifierMask="command" id="win-playlist">
<connections>
<action selector="togglePlaylist:" target="Voe-Tx-rLC" id="act-toggle-playlist"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="sep-window-3"/>
<menuItem title="UI Scale" id="win-ui-scale">
<menu key="submenu" title="UI Scale" id="win-ui-scale-submenu">
<items>
<menuItem title="1.0x" id="win-ui-scale-1x">
<connections>
<action selector="setUIScale1x:" target="Voe-Tx-rLC" id="act-ui-scale-1x"/>
</connections>
</menuItem>
<menuItem title="2.0x" id="win-ui-scale-2x">
<connections>
<action selector="setUIScale2x:" target="Voe-Tx-rLC" id="act-ui-scale-2x"/>
</connections>
</menuItem>
<menuItem title="3.0x" id="win-ui-scale-3x">
<connections>
<action selector="setUIScale3x:" target="Voe-Tx-rLC" id="act-ui-scale-3x"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
+37
View File
@@ -1,5 +1,6 @@
import Cocoa
import FlutterMacOS
import desktop_multi_window
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
@@ -8,8 +9,44 @@ class MainFlutterWindow: NSWindow {
self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true)
// Transparent, frameless, borderless window so the classic Winamp
// windows float over the desktop (see windowManager options in Dart).
// Borderless style mask: square corners (no system rounded corners),
// no title bar, so the skin bitmap can fill the window exactly.
styleMask = [.borderless]
isOpaque = false
backgroundColor = .clear
hasShadow = false
flutterViewController.backgroundColor = .clear
// Borderless windows aren't movable by default, which blocks
// `windowManager.startDragging()` (it calls `performDrag(with:)`).
// This flag makes the window movable; it never auto-drags because
// FlutterView handles all mouse events.
isMovableByWindowBackground = true
RegisterGeneratedPlugins(registry: flutterViewController)
// Sub-windows (Equalizer/Playlist) run their own Flutter engines; register
// every plugin (window_manager, shared_preferences, ...) for them so the
// plugins work inside sub-windows too.
FlutterMultiWindowPlugin.setOnWindowCreatedCallback { controller in
RegisterGeneratedPlugins(registry: controller)
// Match the main window's borderless look: window_manager's
// `setAsFrameless()` keeps the `.titled` style mask (rounded corners);
// replace it with `.borderless` so the skin bitmap fills the window with
// square corners. Also make it movable so startDragging() works.
if let window = controller.view.window {
window.styleMask = [.borderless]
window.isOpaque = false
window.backgroundColor = .clear
window.hasShadow = false
window.isMovableByWindowBackground = true
controller.backgroundColor = .clear
}
}
super.awakeFromNib()
}
}