| | |
| | | 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") |
| | | } |
| | |
| | | self.parentWindow = parentWindow |
| | | } |
| | | |
| | | let shouldOrderForInput = shouldOrderPanelForInput(panel) |
| | | if panel.frame != frame { |
| | | panel.setFrame(frame, display: true) |
| | | } |
| | | if shouldOrderForInput { |
| | | NSApp.activate(ignoringOtherApps: true) |
| | | panel.makeKeyAndOrderFront(nil) |
| | | panel.orderFrontRegardless() |
| | | } |
| | | } |
| | | |
| | | func closePanel() { |
| | |
| | | self.panel = panel |
| | | self.hostingView = hostingView |
| | | return panel |
| | | } |
| | | |
| | | private func shouldOrderPanelForInput(_ panel: QuickSearchPanel) -> Bool { |
| | | !panel.isVisible || !panel.isKeyWindow || NSApp.keyWindow !== panel |
| | | } |
| | | |
| | | private func scheduleDeferredApply( |
| | |
| | | } |
| | | |
| | | 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 |
| | |
| | | guard let field, |
| | | let window = field.window, |
| | | window.isVisible, |
| | | window.firstResponder !== field |
| | | field.currentEditor() == nil |
| | | else { return } |
| | | if !window.isKeyWindow || NSApp.keyWindow !== window { |
| | | NSApp.activate(ignoringOtherApps: true) |
| | | window.makeKeyAndOrderFront(nil) |
| | | window.orderFrontRegardless() |
| | | } |
| | | window.makeFirstResponder(field) |
| | | } |
| | | } |
| | |
| | | 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) |
| | |
| | | |
| | | 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) |