From b2a06ec5ebc86c0241e635782ae5a032ab5aad0a Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 02 Jul 2026 02:43:51 +0800
Subject: [PATCH] Prepare TagLauncher 8.3.2 App Store candidate

---
 src/Apptag/QuickSearch.swift |   67 +++++++++++++++++++++++++++++----
 1 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/src/Apptag/QuickSearch.swift b/src/Apptag/QuickSearch.swift
index c7b25eb..f4365c4 100644
--- a/src/Apptag/QuickSearch.swift
+++ b/src/Apptag/QuickSearch.swift
@@ -6,6 +6,7 @@
     static let tagLauncherQuickSearchRequested = Notification.Name("TagLauncherQuickSearchRequested")
     static let tagLauncherQuickSearchDismissRequested = Notification.Name("TagLauncherQuickSearchDismissRequested")
     static let tagLauncherQuickSearchVisibilityChanged = Notification.Name("TagLauncherQuickSearchVisibilityChanged")
+    static let tagLauncherQuickSearchCompositionChanged = Notification.Name("TagLauncherQuickSearchCompositionChanged")
     static let tagLauncherHotkeyRegistrationChanged = Notification.Name("TagLauncherHotkeyRegistrationChanged")
     static let tagLauncherProEntitlementChanged = Notification.Name("TagLauncherProEntitlementChanged")
 }
@@ -849,12 +850,15 @@
                 self.parentWindow = parentWindow
             }
 
+            let shouldOrderForInput = shouldOrderPanelForInput(panel)
             if panel.frame != frame {
                 panel.setFrame(frame, display: true)
             }
-            NSApp.activate(ignoringOtherApps: true)
-            panel.makeKeyAndOrderFront(nil)
-            panel.orderFrontRegardless()
+            if shouldOrderForInput {
+                NSApp.activate(ignoringOtherApps: true)
+                panel.makeKeyAndOrderFront(nil)
+                panel.orderFrontRegardless()
+            }
         }
 
         func closePanel() {
@@ -899,6 +903,10 @@
             self.panel = panel
             self.hostingView = hostingView
             return panel
+        }
+
+        private func shouldOrderPanelForInput(_ panel: QuickSearchPanel) -> Bool {
+            !panel.isVisible || !panel.isKeyWindow || NSApp.keyWindow !== panel
         }
 
         private func scheduleDeferredApply(
@@ -1651,9 +1659,10 @@
     }
 
     func updateNSView(_ field: QuickSearchNativeTextField, context: Context) {
-        if field.stringValue != text {
+        if !field.isComposingText, field.stringValue != text {
             field.stringValue = text
         }
+        field.publishCompositionStateIfNeeded()
         field.placeholderString = placeholder
         field.onCommand = onCommand
         field.target = context.coordinator
@@ -1671,10 +1680,13 @@
                 guard let field,
                       let window = field.window,
                       window.isVisible,
-                      window.firstResponder !== field
+                      field.currentEditor() == nil
                 else { return }
-                window.makeKeyAndOrderFront(nil)
-                window.orderFrontRegardless()
+                if !window.isKeyWindow || NSApp.keyWindow !== window {
+                    NSApp.activate(ignoringOtherApps: true)
+                    window.makeKeyAndOrderFront(nil)
+                    window.orderFrontRegardless()
+                }
                 window.makeFirstResponder(field)
             }
         }
@@ -1688,22 +1700,36 @@
         var text: Binding<String>
         var lastFocusToken = 0
         var onCommand: ((QuickSearchCommand) -> Void)?
-        weak var field: NSTextField?
+        weak var field: QuickSearchNativeTextField?
 
         init(text: Binding<String>) {
             self.text = text
         }
 
         func controlTextDidChange(_ obj: Notification) {
-            guard let field = obj.object as? NSTextField else { return }
+            guard let field = obj.object as? QuickSearchNativeTextField else { return }
+            field.publishCompositionStateIfNeeded()
+            if !field.isComposingText {
+                text.wrappedValue = field.stringValue
+            }
+        }
+
+        func controlTextDidEndEditing(_ obj: Notification) {
+            guard let field = obj.object as? QuickSearchNativeTextField else { return }
+            field.publishCompositionState(active: false, force: true)
             text.wrappedValue = field.stringValue
         }
 
         @objc func submitAction(_ sender: Any?) {
+            guard field?.isComposingText != true else { return }
             onCommand?(.submit)
         }
 
         func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
+            if textView.hasMarkedText() {
+                (control as? QuickSearchNativeTextField)?.publishCompositionState(active: true)
+                return false
+            }
             switch commandSelector {
             case #selector(NSResponder.moveUp(_:)):
                 onCommand?(.moveUp)
@@ -1729,10 +1755,33 @@
 
 private final class QuickSearchNativeTextField: NSTextField {
     var onCommand: ((QuickSearchCommand) -> Void)?
+    private var lastPublishedCompositionActive = false
 
     override var acceptsFirstResponder: Bool { true }
 
+    var isComposingText: Bool {
+        (currentEditor() as? NSTextView)?.hasMarkedText() == true
+    }
+
+    func publishCompositionStateIfNeeded() {
+        publishCompositionState(active: isComposingText)
+    }
+
+    func publishCompositionState(active: Bool, force: Bool = false) {
+        guard force || active != lastPublishedCompositionActive else { return }
+        lastPublishedCompositionActive = active
+        NotificationCenter.default.post(
+            name: .tagLauncherQuickSearchCompositionChanged,
+            object: self,
+            userInfo: ["active": active]
+        )
+    }
+
     override func keyDown(with event: NSEvent) {
+        guard !isComposingText else {
+            super.keyDown(with: event)
+            return
+        }
         switch Int(event.keyCode) {
         case kVK_UpArrow:
             onCommand?(.moveUp)

--
Gitblit v1.9.3