117 lines
3.9 KiB
Swift
117 lines
3.9 KiB
Swift
import AppKit
|
|
|
|
/// Central manager for Dock icon visibility.
|
|
/// Shows the Dock icon while any windows are visible, regardless of user preference.
|
|
final class DockIconManager: NSObject, @unchecked Sendable {
|
|
static let shared = DockIconManager()
|
|
|
|
private var windowsObservation: NSKeyValueObservation?
|
|
private let logger = Logger(subsystem: "com.clawdbot", category: "DockIconManager")
|
|
|
|
override private init() {
|
|
super.init()
|
|
self.setupObservers()
|
|
Task { @MainActor in
|
|
self.updateDockVisibility()
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
self.windowsObservation?.invalidate()
|
|
NotificationCenter.default.removeObserver(self)
|
|
}
|
|
|
|
func updateDockVisibility() {
|
|
Task { @MainActor in
|
|
guard NSApp != nil else {
|
|
self.logger.warning("NSApp not ready, skipping Dock visibility update")
|
|
return
|
|
}
|
|
|
|
let userWantsDockHidden = !UserDefaults.standard.bool(forKey: showDockIconKey)
|
|
let visibleWindows = NSApp?.windows.filter { window in
|
|
window.isVisible &&
|
|
window.frame.width > 1 &&
|
|
window.frame.height > 1 &&
|
|
!window.isKind(of: NSPanel.self) &&
|
|
"\(type(of: window))" != "NSPopupMenuWindow" &&
|
|
window.contentViewController != nil
|
|
} ?? []
|
|
|
|
let hasVisibleWindows = !visibleWindows.isEmpty
|
|
if !userWantsDockHidden || hasVisibleWindows {
|
|
NSApp?.setActivationPolicy(.regular)
|
|
} else {
|
|
NSApp?.setActivationPolicy(.accessory)
|
|
}
|
|
}
|
|
}
|
|
|
|
func temporarilyShowDock() {
|
|
Task { @MainActor in
|
|
guard NSApp != nil else {
|
|
self.logger.warning("NSApp not ready, cannot show Dock icon")
|
|
return
|
|
}
|
|
NSApp.setActivationPolicy(.regular)
|
|
}
|
|
}
|
|
|
|
private func setupObservers() {
|
|
Task { @MainActor in
|
|
guard let app = NSApp else {
|
|
self.logger.warning("NSApp not ready, delaying Dock observers")
|
|
try? await Task.sleep(for: .milliseconds(200))
|
|
self.setupObservers()
|
|
return
|
|
}
|
|
|
|
self.windowsObservation = app.observe(\.windows, options: [.new]) { [weak self] _, _ in
|
|
Task { @MainActor in
|
|
try? await Task.sleep(for: .milliseconds(50))
|
|
self?.updateDockVisibility()
|
|
}
|
|
}
|
|
|
|
NotificationCenter.default.addObserver(
|
|
self,
|
|
selector: #selector(self.windowVisibilityChanged),
|
|
name: NSWindow.didBecomeKeyNotification,
|
|
object: nil)
|
|
NotificationCenter.default.addObserver(
|
|
self,
|
|
selector: #selector(self.windowVisibilityChanged),
|
|
name: NSWindow.didResignKeyNotification,
|
|
object: nil)
|
|
NotificationCenter.default.addObserver(
|
|
self,
|
|
selector: #selector(self.windowVisibilityChanged),
|
|
name: NSWindow.willCloseNotification,
|
|
object: nil)
|
|
NotificationCenter.default.addObserver(
|
|
self,
|
|
selector: #selector(self.dockPreferenceChanged),
|
|
name: UserDefaults.didChangeNotification,
|
|
object: nil)
|
|
}
|
|
}
|
|
|
|
@objc
|
|
private func windowVisibilityChanged(_: Notification) {
|
|
Task { @MainActor in
|
|
self.updateDockVisibility()
|
|
}
|
|
}
|
|
|
|
@objc
|
|
private func dockPreferenceChanged(_ notification: Notification) {
|
|
guard let userDefaults = notification.object as? UserDefaults,
|
|
userDefaults == UserDefaults.standard
|
|
else { return }
|
|
|
|
Task { @MainActor in
|
|
self.updateDockVisibility()
|
|
}
|
|
}
|
|
}
|