| Apptag/ApptagApp.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/ContentView.swift | ●●●●● patch | view | raw | blame | history | |
| Apptag/QuickSearch.swift | ●●●●● patch | view | raw | blame | history |
Apptag/ApptagApp.swift
@@ -51,6 +51,9 @@ 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 @@ -103,6 +106,8 @@ }, removeQuickSearchMouseMonitor: { [weak self] in self?.removeQuickSearchExternalMouseMonitor() self?.quickSearchShouldHideOverlayOnClose = false self?.quickSearchOnlyOverlaySession = false }, detachSettingsWindow: { [weak self] window in self?.detachSettingsWindow(window) @@ -843,6 +848,11 @@ preferredScreen: NSScreen? = nil, stagedForAllSpaces: Bool = false ) { if initialQuickSearchSource == QuickSearchOpenSource.globalHidden { isQuickSearchOpen = true quickSearchShouldHideOverlayOnClose = true quickSearchOnlyOverlaySession = true } overlayController.show( initialQuickSearchSource: initialQuickSearchSource, preferredScreen: preferredScreen, @@ -863,9 +873,14 @@ @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() @@ -878,11 +893,19 @@ } 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, @@ -895,6 +918,10 @@ private func requestQuickSearch(source: String) { isQuickSearchOpen = true quickSearchShouldHideOverlayOnClose = quickSearchShouldHideOverlayOnClose || source == QuickSearchOpenSource.globalHidden quickSearchOnlyOverlaySession = quickSearchOnlyOverlaySession || source == QuickSearchOpenSource.globalHidden promoteOverlayToForegroundInput() updateOverlayLevelForTextInput() installQuickSearchExternalMouseMonitor() @@ -1038,6 +1065,19 @@ } 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, @@ -1046,6 +1086,9 @@ requestQuickSearch(source: QuickSearchOpenSource.globalVisible) return } isQuickSearchOpen = true quickSearchShouldHideOverlayOnClose = true quickSearchOnlyOverlaySession = true showOverlay(initialQuickSearchSource: QuickSearchOpenSource.globalHidden) } @@ -1308,7 +1351,13 @@ 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() } @@ -1317,6 +1366,11 @@ self.installQuickSearchExternalMouseMonitor() } else { self.removeQuickSearchExternalMouseMonitor() if hideOverlayOnClose || self.quickSearchShouldHideOverlayOnClose { self.quickSearchShouldHideOverlayOnClose = false self.quickSearchOnlyOverlaySession = false self.hideOverlay(force: true) } } } @@ -1447,6 +1501,8 @@ isQuickSearchOpen = false removeQuickSearchExternalMouseMonitor() updateOverlayLevelForTextInput() quickSearchShouldHideOverlayOnClose = false quickSearchOnlyOverlaySession = false NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil) } Apptag/ContentView.swift
@@ -387,6 +387,7 @@ @State private var quickSearchFocusToken = 0 @State private var quickSearchSelectionScrollToken = 0 @State private var quickSearchCloseHidesOverlay = false @State private var quickSearchOnlySession = false @State private var quickSearchErrorMessage: String? = nil @State private var initialQuickSearchConsumed = false @@ -394,8 +395,10 @@ self.hideOverlay = hideOverlay self.initialQuickSearchSource = initialQuickSearchSource let startsInQuickSearch = initialQuickSearchSource != nil let startsInQuickSearchOnlySession = initialQuickSearchSource == QuickSearchOpenSource.globalHidden _quickSearchVisible = State(initialValue: startsInQuickSearch) _quickSearchCloseHidesOverlay = State(initialValue: initialQuickSearchSource == QuickSearchOpenSource.globalHidden) _quickSearchCloseHidesOverlay = State(initialValue: startsInQuickSearchOnlySession) _quickSearchOnlySession = State(initialValue: startsInQuickSearchOnlySession) _quickSearchFocusToken = State(initialValue: startsInQuickSearch ? 1 : 0) } @@ -447,7 +450,10 @@ } private var shouldRenderAppGridBehindQuickSearch: Bool { !quickSearchVisible || !quickSearchCloseHidesOverlay if quickSearchOnlySession { return false } return !quickSearchVisible || !quickSearchCloseHidesOverlay } var body: some View { @@ -527,7 +533,10 @@ refreshAppsIfNeeded() if let initialQuickSearchSource, !initialQuickSearchConsumed { initialQuickSearchConsumed = true quickSearchCloseHidesOverlay = initialQuickSearchSource == QuickSearchOpenSource.globalHidden if initialQuickSearchSource == QuickSearchOpenSource.globalHidden { quickSearchCloseHidesOverlay = true quickSearchOnlySession = true } if !quickSearchVisible { quickSearchVisible = true quickSearchFocusToken &+= 1 @@ -536,7 +545,10 @@ NotificationCenter.default.post( name: .tagLauncherQuickSearchVisibilityChanged, object: nil, userInfo: ["active": true] userInfo: [ "active": true, "hideOverlayOnClose": quickSearchCloseHidesOverlay ] ) } } @@ -548,6 +560,8 @@ .onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidHide)) { _ in resetTransientDragState() closeQuickSearch(notify: true, hideOverlayIfNeeded: false) quickSearchOnlySession = false quickSearchCloseHidesOverlay = false } .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchRequested)) { notification in let source = notification.userInfo?["source"] as? String ?? QuickSearchOpenSource.mainOverlay @@ -705,7 +719,11 @@ private func openQuickSearch(source: String) { guard canOpenQuickSearch else { return } dismissAppBubble() quickSearchCloseHidesOverlay = source == QuickSearchOpenSource.globalHidden if source == QuickSearchOpenSource.globalHidden { quickSearchOnlySession = true } quickSearchCloseHidesOverlay = quickSearchCloseHidesOverlay || source == QuickSearchOpenSource.globalHidden quickSearchVisible = true quickSearchQuery = "" quickSearchManualSelection = false @@ -715,7 +733,10 @@ NotificationCenter.default.post( name: .tagLauncherQuickSearchVisibilityChanged, object: nil, userInfo: ["active": true] userInfo: [ "active": true, "hideOverlayOnClose": quickSearchCloseHidesOverlay ] ) } @@ -736,16 +757,20 @@ quickSearchSelectedID = nil quickSearchManualSelection = false quickSearchErrorMessage = nil quickSearchCloseHidesOverlay = false if notify { NotificationCenter.default.post( name: .tagLauncherQuickSearchVisibilityChanged, object: nil, userInfo: ["active": false] userInfo: [ "active": false, "hideOverlayOnClose": shouldHideOverlay ] ) } if shouldHideOverlay { hideOverlay() } else { quickSearchCloseHidesOverlay = false } } Apptag/QuickSearch.swift
@@ -1524,11 +1524,17 @@ } private func requestFocus(_ field: QuickSearchNativeTextField) { DispatchQueue.main.async { [weak field] in for delay in [0.0, 0.03, 0.08, 0.16] { DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak field] in guard let field, field.window?.firstResponder !== field let window = field.window, window.isVisible, window.firstResponder !== field else { return } field.window?.makeFirstResponder(field) window.makeKeyAndOrderFront(nil) window.orderFrontRegardless() window.makeFirstResponder(field) } } } @@ -1578,6 +1584,8 @@ private final class QuickSearchNativeTextField: NSTextField { var onCommand: ((QuickSearchCommand) -> Void)? override var acceptsFirstResponder: Bool { true } override func keyDown(with event: NSEvent) { switch Int(event.keyCode) { case kVK_UpArrow: