From 44dd0528c81fc3ac78efbec5bb628f45a4edcf95 Mon Sep 17 00:00:00 2001 From: Maximilian Bauer Date: Wed, 27 Nov 2024 23:52:27 +0100 Subject: [PATCH] macOS: rework window menu --- Amperfy/AppDelegate.swift | 82 +++++++++++++++++++-------- Amperfy/MiniPlayerSceneDelegate.swift | 16 ++++-- Amperfy/SceneDelegate.swift | 6 ++ 3 files changed, 74 insertions(+), 30 deletions(-) diff --git a/Amperfy/AppDelegate.swift b/Amperfy/AppDelegate.swift index af49d3db..0a8d6f18 100644 --- a/Amperfy/AppDelegate.swift +++ b/Amperfy/AppDelegate.swift @@ -378,6 +378,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return nil } +#if targetEnvironment(macCatalyst) + var isMainOrMiniPlayerPlayerOpen: Bool { + return isMainWindowOpen || isShowingMiniPlayer + } + + var isMainWindowOpen: Bool { + return !UIApplication.shared.connectedScenes + .flatMap { ($0 as? UIWindowScene)?.windows ?? [] } + .filter { ($0.rootViewController as? SplitVC) != nil } + .isEmpty + } + func closeMainWindow() { // Close all main sessions (this might be more than one with multiple tabs open) UIApplication.shared.connectedScenes @@ -395,25 +407,60 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let defaultActivity = NSUserActivity(activityType: defaultWindowActivityType) UIApplication.shared.requestSceneSessionActivation(nil, userActivity: defaultActivity, options: nil, errorHandler: nil) } - - #if targetEnvironment(macCatalyst) + + public func rebuildMainMenu() { + UIMenuSystem.main.setNeedsRebuild() + } override func buildMenu(with builder: any UIMenuBuilder) { super.buildMenu(with: builder) guard builder.system == .main else { return } - // Add a settings menu - let settingsMenu = UIMenu(options: .displayInline, children: [ - UIKeyCommand(title: "Settingsā€¦", action: #selector(showSettings), input: ",", modifierFlags: .command) + // Add File menu + let fileMenu = UIMenu(title: "File", children: [ + UIMenu(options: .displayInline, children: [ + UIAction(title: "Open Player Window", attributes: self.isMainOrMiniPlayerPlayerOpen ? .disabled : []) { _ in + self.openMainWindow() + }, + UIAction(title: "Close Player Window", attributes: !self.isMainOrMiniPlayerPlayerOpen ? .disabled : []) { _ in + if self.isMainWindowOpen { + self.closeMainWindow() + } else if self.isShowingMiniPlayer { + self.closeMiniPlayer() + } + } + ]), + UIMenu(options: .displayInline, children: [ + UIAction(title: "Switch Library/Mini Player") { _ in + if self.isShowingMiniPlayer { + self.closeMiniPlayer() + self.openMainWindow() + } else { + self.closeMainWindow() + self.showMiniPlayer() + } + } + ]), + // Add a settings menu + UIMenu(options: .displayInline, children: [ + UIKeyCommand(title: "Open Settings", action: #selector(showSettings), input: ",", modifierFlags: .command) + ]) ]) - builder.insertSibling(settingsMenu, afterMenu: .about) + builder.insertSibling(fileMenu, beforeMenu: .view) + // Add media controls builder.insertSibling(buildControlsMenu(), afterMenu: .view) - // Remove "new window" and toolbar options + // Remove not needed default menu items builder.remove(menu: .toolbar) - builder.remove(menu: .newScene) + builder.remove(menu: .file) + builder.remove(menu: .edit) + builder.remove(menu: .format) + builder.remove(menu: .font) + builder.remove(menu: .text) + builder.remove(menu: .services) + builder.remove(menu: .help) if (self.focusedWindowTitle == windowSettingsTitle) || (self.focusedWindowTitle == windowMiniPlayerTitle) { // Do any settings specific menu setup here @@ -499,25 +546,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } let section3 = [ - UIAction(title: self.player.playerMode.nextMode.description) { _ in + UIAction(title: "Switch Music/Podcast mode") { _ in self.player.setPlayerMode(self.player.playerMode.nextMode) } ] - let section4 = [ - UIAction(title: self.isShowingMiniPlayer ? "Main Window" : "Mini Player") { _ in - if self.isShowingMiniPlayer { - self.closeMiniPlayer() - // Is automatically opened - //self.openMainWindow() - } else { - self.closeMainWindow() - self.showMiniPlayer() - } - } - ] - - let sections: [[UIMenuElement]] = [section1, section2, section3, section4] + let sections: [[UIMenuElement]] = [section1, section2, section3] return UIMenu(title: "Controls", children: sections.reduce([], { (result, section) in result + [UIMenu(options: .displayInline)] + section @@ -547,7 +581,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UIApplication.shared.requestSceneSessionDestruction($0.session, options: options, errorHandler: nil) } } - #endif +#endif } #if targetEnvironment(macCatalyst) diff --git a/Amperfy/MiniPlayerSceneDelegate.swift b/Amperfy/MiniPlayerSceneDelegate.swift index 9e99913f..8bec6b41 100644 --- a/Amperfy/MiniPlayerSceneDelegate.swift +++ b/Amperfy/MiniPlayerSceneDelegate.swift @@ -46,8 +46,7 @@ class MiniPlayerSceneDelegate: UIResponder, UIWindowSceneDelegate { self.window = UIWindow(windowScene: windowScene) self.window?.backgroundColor = .secondarySystemBackground - #if targetEnvironment(macCatalyst) - +#if targetEnvironment(macCatalyst) windowScene.title = windowMiniPlayerTitle if let titleBar = windowScene.titlebar { @@ -73,13 +72,14 @@ class MiniPlayerSceneDelegate: UIResponder, UIWindowSceneDelegate { windowScene.sizeRestrictions?.allowsFullScreen = false } windowScene.sizeRestrictions?.minimumSize = minSize - - #endif +#endif self.window?.rootViewController = PopupPlayerVC() self.window?.makeKeyAndVisible() +#if targetEnvironment(macCatalyst) self.appDelegate.closeMainWindow() +#endif } /** Called when the user activates your application by selecting a shortcut on the Home Screen, @@ -98,14 +98,18 @@ class MiniPlayerSceneDelegate: UIResponder, UIWindowSceneDelegate { // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). os_log("MiniPlayer: sceneDidDisconnect", log: self.log, type: .info) - - self.appDelegate.openMainWindow() +#if targetEnvironment(macCatalyst) + appDelegate.rebuildMainMenu() +#endif } func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. os_log("MiniPlayer: sceneDidBecomeActive", log: self.log, type: .info) +#if targetEnvironment(macCatalyst) + appDelegate.rebuildMainMenu() +#endif } func sceneWillResignActive(_ scene: UIScene) { diff --git a/Amperfy/SceneDelegate.swift b/Amperfy/SceneDelegate.swift index 533e2338..789ca01a 100644 --- a/Amperfy/SceneDelegate.swift +++ b/Amperfy/SceneDelegate.swift @@ -120,6 +120,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). os_log("sceneDidDisconnect", log: self.log, type: .info) +#if targetEnvironment(macCatalyst) + appDelegate.rebuildMainMenu() +#endif } func sceneDidBecomeActive(_ scene: UIScene) { @@ -127,6 +130,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. os_log("sceneDidBecomeActive", log: self.log, type: .info) appDelegate.quickActionsManager.handleSavedShortCutItemIfSaved() + #if targetEnvironment(macCatalyst) + appDelegate.rebuildMainMenu() + #endif } func sceneWillResignActive(_ scene: UIScene) {