| | |
| | | |
| | | final class AppDelegate: NSObject, NSApplicationDelegate { |
| | | private static let showDockIconKey = "showDockIcon" |
| | | private static let statusItemAutosaveName = "com.apptag.launcher.statusItem" |
| | | private static let statusItemButtonIdentifier = NSUserInterfaceItemIdentifier("TagLauncherStatusItemButton") |
| | | private static let statusItemAccessibilityLabel = "TagLauncher" |
| | | private static let showAppListMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherShowAppListMenuItem") |
| | | private static let showAppListShortcutGlyphs = "⌥⇧␣" |
| | | private static let overlayDefaultLevel = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow))) |
| | |
| | | // MARK: - Menu Bar |
| | | |
| | | private func setupMenuBar() { |
| | | removeMenuBarItem() |
| | | statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) |
| | | if statusItem == nil { |
| | | statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) |
| | | } else { |
| | | statusItem?.length = NSStatusItem.squareLength |
| | | } |
| | | guard let statusItem else { return } |
| | | statusItem.autosaveName = Self.statusItemAutosaveName |
| | | statusItem.isVisible = true |
| | | |
| | | if let button = statusItem.button { |
| | | button.identifier = Self.statusItemButtonIdentifier |
| | | button.setAccessibilityIdentifier(Self.statusItemAutosaveName) |
| | | button.setAccessibilityLabel(Self.statusItemAccessibilityLabel) |
| | | button.image = makeMenuBarIcon() |
| | | button.imageScaling = .scaleProportionallyDown |
| | | button.imagePosition = .imageOnly |
| | |
| | | NSMouseInRect(mousePoint, $0.frame, false) |
| | | }) ?? NSScreen.main ?? NSScreen.screens.first else { return } |
| | | |
| | | overlayWindow?.orderOut(nil) |
| | | overlayWindow = makeOverlayWindow(on: screen) |
| | | overlayWindow?.setFrame(screen.frame, display: true) |
| | | overlayWindow?.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel |
| | | let window: NSWindow |
| | | if let existingWindow = overlayWindow { |
| | | window = existingWindow |
| | | } else { |
| | | window = makeOverlayWindow(on: screen) |
| | | overlayWindow = window |
| | | } |
| | | |
| | | window.setFrame(screen.frame, display: true) |
| | | window.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel |
| | | |
| | | installOverlayKeyMonitor() |
| | | |
| | | overlayWindow?.makeKeyAndOrderFront(nil) |
| | | overlayWindow?.orderFrontRegardless() |
| | | window.makeKeyAndOrderFront(nil) |
| | | window.orderFrontRegardless() |
| | | NotificationCenter.default.post(name: .tagLauncherOverlayDidShow, object: nil) |
| | | } |
| | | |
| | | private func installOverlayKeyMonitor() { |
| | |
| | | } |
| | | overlayWindow?.orderOut(nil) |
| | | removeOverlayKeyMonitor() |
| | | NotificationCenter.default.post(name: .tagLauncherOverlayDidHide, object: nil) |
| | | if force { |
| | | overlayWindow = nil |
| | | } |
| | |
| | | |
| | | final class DismissibleHostingView<Content: View>: NSHostingView<Content> { |
| | | private let onBackdropTap: () -> Void |
| | | private var suppressBackdropDismiss = false |
| | | private var modalInteractionObserver: NSObjectProtocol? |
| | | |
| | | @MainActor required init(rootView: Content) { |
| | | self.onBackdropTap = {} |
| | | super.init(rootView: rootView) |
| | | observeModalInteractionChanges() |
| | | } |
| | | |
| | | init(rootView: Content, onBackdropTap: @escaping () -> Void) { |
| | | self.onBackdropTap = onBackdropTap |
| | | super.init(rootView: rootView) |
| | | observeModalInteractionChanges() |
| | | } |
| | | |
| | | deinit { |
| | | if let modalInteractionObserver { |
| | | NotificationCenter.default.removeObserver(modalInteractionObserver) |
| | | } |
| | | } |
| | | |
| | | @available(*, unavailable) |
| | |
| | | return |
| | | } |
| | | if hit == self { |
| | | if suppressBackdropDismiss { |
| | | super.mouseDown(with: event) |
| | | return |
| | | } |
| | | onBackdropTap() |
| | | return |
| | | } |
| | |
| | | super.mouseDown(with: event) |
| | | } |
| | | |
| | | private func observeModalInteractionChanges() { |
| | | modalInteractionObserver = NotificationCenter.default.addObserver( |
| | | forName: .tagLauncherModalInteractionChanged, |
| | | object: nil, |
| | | queue: .main |
| | | ) { [weak self] notification in |
| | | self?.suppressBackdropDismiss = (notification.userInfo?["active"] as? Bool) ?? false |
| | | } |
| | | } |
| | | |
| | | private func findTextFieldContainer(in view: NSView) -> TextFieldContainer? { |
| | | if let container = view as? TextFieldContainer { return container } |
| | | for sub in view.subviews { |