diff --git a/Ice/Main/AppDelegate.swift b/Ice/Main/AppDelegate.swift index 935efcc0..e3911a30 100644 --- a/Ice/Main/AppDelegate.swift +++ b/Ice/Main/AppDelegate.swift @@ -33,6 +33,18 @@ final class AppDelegate: NSObject, NSApplicationDelegate { return } + // Assign and close the various windows. + let windowAssignments: KeyValuePairs = [ + Constants.settingsWindowID: appState.assignSettingsWindow, + Constants.permissionsWindowID: appState.assignPermissionsWindow, + ] + for (identifier, assign) in windowAssignments { + if let window = NSApp.window(withIdentifier: identifier) { + assign(window) + window.close() + } + } + // Hide the main menu to make more space in the menu bar. if let mainMenu = NSApp.mainMenu { for item in mainMenu.items { @@ -40,24 +52,17 @@ final class AppDelegate: NSObject, NSApplicationDelegate { } } - // Temporary hack to make sure the windows are retained on Sequoia: - // Let them open, wait a bit, then close them. - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - appState.settingsWindow?.close() - appState.permissionsWindow?.close() - - if !appState.isPreview { - // If we have the required permissions, set up the shared app state. - // Otherwise, open the permissions window. - if appState.permissionsManager.hasPermission { - appState.performSetup() - } else if let permissionsWindow = appState.permissionsWindow { - appState.activate(withPolicy: .regular) - permissionsWindow.center() - permissionsWindow.makeKeyAndOrderFront(nil) - } else { - Logger.appDelegate.error("Failed to open permissions window") - } + if !appState.isPreview { + // If we have the required permissions, set up the shared app state. + // Otherwise, open the permissions window. + if appState.permissionsManager.hasAllPermissions { + appState.performSetup() + } else if let permissionsWindow = appState.permissionsWindow { + appState.activate(withPolicy: .regular) + permissionsWindow.center() + permissionsWindow.makeKeyAndOrderFront(nil) + } else { + Logger.appDelegate.error("Failed to open permissions window") } } } diff --git a/Ice/Main/AppState.swift b/Ice/Main/AppState.swift index de8fd8e7..a18fa345 100644 --- a/Ice/Main/AppState.swift +++ b/Ice/Main/AppState.swift @@ -45,6 +45,12 @@ final class AppState: ObservableObject { /// The app's delegate. private(set) weak var appDelegate: AppDelegate? + /// The window that contains the settings interface. + private(set) weak var settingsWindow: NSWindow? + + /// The window that contains the permissions interface. + private(set) weak var permissionsWindow: NSWindow? + /// A Boolean value that indicates whether the "ShowOnHover" feature is prevented. private(set) var isShowOnHoverPrevented = false @@ -62,16 +68,6 @@ final class AppState: ObservableObject { #endif }() - /// The window that contains the settings interface. - var settingsWindow: NSWindow? { - NSApp.window(withIdentifier: Constants.settingsWindowID) - } - - /// The window that contains the permissions interface. - var permissionsWindow: NSWindow? { - NSApp.window(withIdentifier: Constants.permissionsWindowID) - } - /// A Boolean value that indicates whether the application can set the cursor /// in the background. var setsCursorInBackground: Bool { @@ -190,6 +186,24 @@ final class AppState: ObservableObject { self.appDelegate = appDelegate } + /// Assigns the settings window to the app state. + func assignSettingsWindow(_ settingsWindow: NSWindow) { + guard self.settingsWindow == nil else { + Logger.appState.warning("Multiple attempts made to assign settings window") + return + } + self.settingsWindow = settingsWindow + } + + /// Assigns the permissions window to the app state. + func assignPermissionsWindow(_ permissionsWindow: NSWindow) { + guard self.permissionsWindow == nil else { + Logger.appState.warning("Multiple attempts made to assign permissions window") + return + } + self.permissionsWindow = permissionsWindow + } + /// Activates the app and sets its activation policy to the given value. func activate(withPolicy policy: NSApplication.ActivationPolicy) { // Store whether the app has previously activated inside an internal diff --git a/Ice/Permissions/PermissionsManager.swift b/Ice/Permissions/PermissionsManager.swift index fd34df0a..7910051f 100644 --- a/Ice/Permissions/PermissionsManager.swift +++ b/Ice/Permissions/PermissionsManager.swift @@ -9,7 +9,7 @@ import Combine @MainActor final class PermissionsManager: ObservableObject { /// A Boolean value that indicates whether the app has been granted all permissions. - @Published var hasPermission: Bool = false + @Published var hasAllPermissions: Bool = false let accessibilityPermission = AccessibilityPermission() @@ -30,7 +30,7 @@ final class PermissionsManager: ObservableObject { accessibilityPermission.$hasPermission .combineLatest(screenRecordingPermission.$hasPermission) .sink { [weak self] hasPermission1, hasPermission2 in - self?.hasPermission = hasPermission1 && hasPermission2 + self?.hasAllPermissions = hasPermission1 && hasPermission2 } .store(in: &c) diff --git a/Ice/Permissions/PermissionsView.swift b/Ice/Permissions/PermissionsView.swift index 91fc6520..e868dd35 100644 --- a/Ice/Permissions/PermissionsView.swift +++ b/Ice/Permissions/PermissionsView.swift @@ -109,7 +109,7 @@ struct PermissionsView: View { Text("Continue") .frame(maxWidth: .infinity) } - .disabled(!permissionsManager.hasPermission) + .disabled(!permissionsManager.hasAllPermissions) } @ViewBuilder diff --git a/Ice/Permissions/PermissionsWindow.swift b/Ice/Permissions/PermissionsWindow.swift index 957366f6..2b23901a 100644 --- a/Ice/Permissions/PermissionsWindow.swift +++ b/Ice/Permissions/PermissionsWindow.swift @@ -12,44 +12,12 @@ struct PermissionsWindow: Scene { self.permissionsManager = appState.permissionsManager } - var body: some Scene { - permissionsWindow - .windowResizability(.contentSize) - .windowStyle(.hiddenTitleBar) - .environmentObject(permissionsManager) - } - - private var permissionsWindow: some Scene { - if #available(macOS 15.0, *) { - return PermissionsWindowMacOS15() - } else { - return PermissionsWindowMacOS14() - } - } -} - -@available(macOS 14.0, *) -private struct PermissionsWindowMacOS14: Scene { - var body: some Scene { - Window(Constants.permissionsWindowTitle, id: Constants.permissionsWindowID) { - PermissionsView() - } - } -} - -@available(macOS 15.0, *) -private struct PermissionsWindowMacOS15: Scene { - @Environment(\.dismissWindow) private var dismissWindow - @State private var launchBehavior: SceneLaunchBehavior = .presented - var body: some Scene { Window(Constants.permissionsWindowTitle, id: Constants.permissionsWindowID) { PermissionsView() - .once { - dismissWindow(id: Constants.permissionsWindowID) - launchBehavior = .suppressed // Keep the window from reopening. - } } - .defaultLaunchBehavior(launchBehavior) + .windowResizability(.contentSize) + .windowStyle(.hiddenTitleBar) + .environmentObject(permissionsManager) } } diff --git a/Ice/Settings/SettingsWindow.swift b/Ice/Settings/SettingsWindow.swift index 003c97fa..fdbd93fb 100644 --- a/Ice/Settings/SettingsWindow.swift +++ b/Ice/Settings/SettingsWindow.swift @@ -10,30 +10,18 @@ struct SettingsWindow: Scene { @Environment(\.openWindow) private var openWindow var body: some Scene { - settingsWindow - .commandsRemoved() - .windowResizability(.contentSize) - .defaultSize(width: 900, height: 625) - .environmentObject(appState) - .environmentObject(appState.navigationState) - } - - private var settingsWindow: some Scene { - if #available(macOS 15.0, *) { - return window - .defaultLaunchBehavior(.presented) - } else { - return window - } - } - - @SceneBuilder - private var window: some Scene { Window(Constants.settingsWindowTitle, id: Constants.settingsWindowID) { SettingsView() - .once { - openWindow(id: Constants.permissionsWindowID) + .onAppear { + if !appState.permissionsManager.hasAllPermissions { + openWindow(id: Constants.permissionsWindowID) + } } } + .commandsRemoved() + .windowResizability(.contentSize) + .defaultSize(width: 900, height: 625) + .environmentObject(appState) + .environmentObject(appState.navigationState) } }