mirror of
https://github.com/jayanezj/traccar-client-macos.git
synced 2026-11-05 00:00:45 +01:00
Store receiver and identity together in Keychain, with versioned tracking preferences, explicit authorization retry and stable Settings focus. Adopt free Apple Development locally and credential-free ad hoc CI. Run configuration and focus tests through both CI toolchains. Final decisions: https://github.com/jayanezj/traccar-client-macos/issues/3#issuecomment-6396087703
64 lines
2.4 KiB
Swift
64 lines
2.4 KiB
Swift
import AppKit
|
|
|
|
@MainActor
|
|
protocol FocusRestorationWindow: AnyObject {
|
|
var isVisible: Bool { get }
|
|
var isMiniaturized: Bool { get }
|
|
func restoreKeyWindow()
|
|
}
|
|
|
|
extension NSWindow: FocusRestorationWindow {
|
|
func restoreKeyWindow() { makeKeyAndOrderFront(nil) }
|
|
}
|
|
|
|
@MainActor
|
|
final class WindowFocusController {
|
|
// A workspace activation sends a reopen event. Its window is selected only after activation finishes.
|
|
private(set) static var pendingActivations = 0
|
|
|
|
weak var window: (any FocusRestorationWindow)?
|
|
private var restoration: Task<Void, Never>?
|
|
private let activate: @MainActor () async -> Bool
|
|
|
|
init(activate: @escaping @MainActor () async -> Bool = activateCurrentApplication) {
|
|
self.activate = activate
|
|
}
|
|
|
|
@discardableResult
|
|
func perform(_ action: () -> Void) -> Task<Void, Never>? {
|
|
restoration?.cancel()
|
|
let initiatingWindow = window
|
|
action()
|
|
guard let initiatingWindow, initiatingWindow.isVisible, !initiatingWindow.isMiniaturized else { return nil }
|
|
let activate = activate
|
|
restoration = Task { [weak initiatingWindow] in
|
|
guard !Task.isCancelled, let initiatingWindow,
|
|
initiatingWindow.isVisible, !initiatingWindow.isMiniaturized else { return }
|
|
Self.pendingActivations += 1
|
|
defer { Self.pendingActivations -= 1 }
|
|
guard await activate(), !Task.isCancelled,
|
|
initiatingWindow.isVisible, !initiatingWindow.isMiniaturized else { return }
|
|
initiatingWindow.restoreKeyWindow()
|
|
}
|
|
return restoration
|
|
}
|
|
|
|
private static func activateCurrentApplication() async -> Bool {
|
|
let configuration = NSWorkspace.OpenConfiguration()
|
|
configuration.activates = true
|
|
configuration.createsNewApplicationInstance = false
|
|
configuration.allowsRunningApplicationSubstitution = false
|
|
configuration.addsToRecentItems = false
|
|
configuration.promptsUserIfNeeded = false
|
|
do {
|
|
// Launch Services supplies activation context that a plain NSApp.activate() cannot guarantee.
|
|
let application = try await NSWorkspace.shared.openApplication(
|
|
at: Bundle.main.bundleURL, configuration: configuration
|
|
)
|
|
return application.processIdentifier == ProcessInfo.processInfo.processIdentifier
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
}
|