| | |
| | | private var quickSearchHotkeyRef: EventHotKeyRef? |
| | | private var hotkeyEventHandlerInstalled = false |
| | | private var isQuickSearchOpen = false |
| | | private var quickSearchShouldHideOverlayOnClose = false |
| | | private var quickSearchOnlyOverlaySession = false |
| | | private var lastQuickSearchHotkeyAt = Date.distantPast |
| | | private var isModalInteractionActive = false |
| | | private var isInEditMode = false // Suppress auto-dismiss during editing |
| | | private var isEditingAppNote = false |
| | |
| | | }, |
| | | removeQuickSearchMouseMonitor: { [weak self] in |
| | | self?.removeQuickSearchExternalMouseMonitor() |
| | | self?.quickSearchShouldHideOverlayOnClose = false |
| | | self?.quickSearchOnlyOverlaySession = false |
| | | }, |
| | | detachSettingsWindow: { [weak self] window in |
| | | self?.detachSettingsWindow(window) |
| | |
| | | preferredScreen: NSScreen? = nil, |
| | | stagedForAllSpaces: Bool = false |
| | | ) { |
| | | if initialQuickSearchSource == QuickSearchOpenSource.globalHidden { |
| | | isQuickSearchOpen = true |
| | | quickSearchShouldHideOverlayOnClose = true |
| | | quickSearchOnlyOverlaySession = true |
| | | } |
| | | overlayController.show( |
| | | initialQuickSearchSource: initialQuickSearchSource, |
| | | preferredScreen: preferredScreen, |
| | |
| | | |
| | | @discardableResult |
| | | private func handleOverlayKeyEvent(_ event: NSEvent) -> Bool { |
| | | guard event.type == .keyDown, |
| | | shouldHandleOverlayKeyEvent(event) |
| | | else { return false } |
| | | guard event.type == .keyDown else { return false } |
| | | |
| | | if event.keyCode == UInt16(kVK_Escape), |
| | | isQuickSearchOpen || quickSearchOnlyOverlaySession { |
| | | return handleOverlayEscapeKey() |
| | | } |
| | | |
| | | guard shouldHandleOverlayKeyEvent(event) else { return false } |
| | | |
| | | if event.keyCode == UInt16(kVK_Escape) { |
| | | return handleOverlayEscapeKey() |
| | |
| | | } |
| | | |
| | | private func handleOverlayEscapeKey() -> Bool { |
| | | if isQuickSearchOpen { |
| | | if isQuickSearchOpen || quickSearchOnlyOverlaySession { |
| | | let shouldHideOverlayAfterDismiss = quickSearchShouldHideOverlayOnClose || quickSearchOnlyOverlaySession |
| | | isQuickSearchOpen = false |
| | | removeQuickSearchExternalMouseMonitor() |
| | | updateOverlayLevelForTextInput() |
| | | NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) |
| | | if shouldHideOverlayAfterDismiss { |
| | | quickSearchShouldHideOverlayOnClose = false |
| | | quickSearchOnlyOverlaySession = false |
| | | DispatchQueue.main.async { [weak self] in |
| | | self?.hideOverlay(force: true) |
| | | } |
| | | } |
| | | return true |
| | | } |
| | | guard !isSettingsVisible, |
| | |
| | | |
| | | private func requestQuickSearch(source: String) { |
| | | isQuickSearchOpen = true |
| | | quickSearchShouldHideOverlayOnClose = quickSearchShouldHideOverlayOnClose |
| | | || source == QuickSearchOpenSource.globalHidden |
| | | quickSearchOnlyOverlaySession = quickSearchOnlyOverlaySession |
| | | || source == QuickSearchOpenSource.globalHidden |
| | | promoteOverlayToForegroundInput() |
| | | updateOverlayLevelForTextInput() |
| | | installQuickSearchExternalMouseMonitor() |
| | |
| | | } |
| | | |
| | | private func showQuickSearchFromGlobalHotkey() { |
| | | let now = Date() |
| | | if now.timeIntervalSince(lastQuickSearchHotkeyAt) < 0.28 { |
| | | promoteOverlayToForegroundInput() |
| | | updateOverlayLevelForTextInput() |
| | | return |
| | | } |
| | | lastQuickSearchHotkeyAt = now |
| | | |
| | | if overlayWindow?.isVisible == true, isQuickSearchOpen || quickSearchOnlyOverlaySession { |
| | | promoteOverlayToForegroundInput() |
| | | updateOverlayLevelForTextInput() |
| | | return |
| | | } |
| | | if overlayWindow?.isVisible == true { |
| | | refreshLauncherChromeState( |
| | | activate: !overlayAvoidsSpaceSwitch, |
| | |
| | | requestQuickSearch(source: QuickSearchOpenSource.globalVisible) |
| | | return |
| | | } |
| | | isQuickSearchOpen = true |
| | | quickSearchShouldHideOverlayOnClose = true |
| | | quickSearchOnlyOverlaySession = true |
| | | showOverlay(initialQuickSearchSource: QuickSearchOpenSource.globalHidden) |
| | | } |
| | | |
| | |
| | | queue: .main |
| | | ) { [weak self] notification in |
| | | guard let self else { return } |
| | | self.isQuickSearchOpen = (notification.userInfo?["active"] as? Bool) ?? false |
| | | let active = (notification.userInfo?["active"] as? Bool) ?? false |
| | | let hideOverlayOnClose = (notification.userInfo?["hideOverlayOnClose"] as? Bool) ?? false |
| | | self.isQuickSearchOpen = active |
| | | if active, hideOverlayOnClose { |
| | | self.quickSearchShouldHideOverlayOnClose = true |
| | | self.quickSearchOnlyOverlaySession = true |
| | | } |
| | | if self.isQuickSearchOpen { |
| | | self.promoteOverlayToForegroundInput() |
| | | } |
| | |
| | | self.installQuickSearchExternalMouseMonitor() |
| | | } else { |
| | | self.removeQuickSearchExternalMouseMonitor() |
| | | if hideOverlayOnClose || self.quickSearchShouldHideOverlayOnClose { |
| | | self.quickSearchShouldHideOverlayOnClose = false |
| | | self.quickSearchOnlyOverlaySession = false |
| | | self.hideOverlay(force: true) |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | isQuickSearchOpen = false |
| | | removeQuickSearchExternalMouseMonitor() |
| | | updateOverlayLevelForTextInput() |
| | | quickSearchShouldHideOverlayOnClose = false |
| | | quickSearchOnlyOverlaySession = false |
| | | NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) |
| | | } |
| | | |