@@ -20,6 +20,7 @@ struct FilterValueTextField: NSViewRepresentable {
2020 var completionSource : FilterCompletionSource = . staticValues( [ ] )
2121 var allowsMultiLine : Bool = false
2222 var onSubmit : ( ) -> Void = { }
23+ var onCancel : ( ) -> Void = { }
2324
2425 static func suggestions( for input: String , in completions: [ String ] ) -> [ String ] {
2526 guard !input. isEmpty else { return [ ] }
@@ -75,12 +76,14 @@ struct FilterValueTextField: NSViewRepresentable {
7576 textField. lineBreakMode = . byTruncatingTail
7677 }
7778
79+ textField. owner = context. coordinator
7880 context. coordinator. textField = textField
7981 context. coordinator. text = $text
8082 context. coordinator. focusedId = $focusedId
8183 context. coordinator. identity = identity
8284 context. coordinator. completionSource = completionSource
8385 context. coordinator. onSubmit = onSubmit
86+ context. coordinator. onCancel = onCancel
8487
8588 return textField
8689 }
@@ -91,6 +94,7 @@ struct FilterValueTextField: NSViewRepresentable {
9194 context. coordinator. identity = identity
9295 context. coordinator. completionSource = completionSource
9396 context. coordinator. onSubmit = onSubmit
97+ context. coordinator. onCancel = onCancel
9498 context. coordinator. textField = textField
9599
96100 textField. placeholderString = placeholder
@@ -101,18 +105,7 @@ struct FilterValueTextField: NSViewRepresentable {
101105 textField. stringValue = text
102106 }
103107
104- if focusedId == identity {
105- let binding = $focusedId
106- let pendingId = identity
107- DispatchQueue . main. async {
108- guard let window = textField. window,
109- binding. wrappedValue == pendingId else { return }
110- if window. firstResponder !== textField. currentEditor ( ) {
111- window. makeFirstResponder ( textField)
112- }
113- binding. wrappedValue = nil
114- }
115- }
108+ context. coordinator. focusIfRequested ( )
116109 }
117110
118111 func makeCoordinator( ) -> Coordinator {
@@ -121,7 +114,8 @@ struct FilterValueTextField: NSViewRepresentable {
121114 focusedId: $focusedId,
122115 identity: identity,
123116 completionSource: completionSource,
124- onSubmit: onSubmit
117+ onSubmit: onSubmit,
118+ onCancel: onCancel
125119 )
126120 }
127121
@@ -132,6 +126,7 @@ struct FilterValueTextField: NSViewRepresentable {
132126 var identity : UUID
133127 var completionSource : FilterCompletionSource
134128 var onSubmit : ( ) -> Void
129+ var onCancel : ( ) -> Void
135130 weak var textField : NSTextField ?
136131
137132 private let suggestionState = SuggestionState ( )
@@ -151,13 +146,36 @@ struct FilterValueTextField: NSViewRepresentable {
151146 focusedId: Binding < UUID ? > ,
152147 identity: UUID ,
153148 completionSource: FilterCompletionSource ,
154- onSubmit: @escaping ( ) -> Void
149+ onSubmit: @escaping ( ) -> Void ,
150+ onCancel: @escaping ( ) -> Void
155151 ) {
156152 self . text = text
157153 self . focusedId = focusedId
158154 self . identity = identity
159155 self . completionSource = completionSource
160156 self . onSubmit = onSubmit
157+ self . onCancel = onCancel
158+ }
159+
160+ func focusIfRequested( ) {
161+ guard focusedId. wrappedValue == identity,
162+ let textField,
163+ let window = textField. window else { return }
164+ if window. firstResponder !== textField. currentEditor ( ) {
165+ window. makeFirstResponder ( textField)
166+ }
167+ }
168+
169+ func handleBecameFirstResponder( ) {
170+ if focusedId. wrappedValue != identity {
171+ focusedId. wrappedValue = identity
172+ }
173+ }
174+
175+ func handleResignedFirstResponder( ) {
176+ if focusedId. wrappedValue == identity {
177+ focusedId. wrappedValue = nil
178+ }
161179 }
162180
163181 deinit {
@@ -195,7 +213,11 @@ struct FilterValueTextField: NSViewRepresentable {
195213 return true
196214 }
197215 if commandSelector == #selector( NSResponder . cancelOperation ( _: ) ) {
198- dismissSuggestions ( )
216+ if suggestionPopover != nil {
217+ dismissSuggestions ( )
218+ return true
219+ }
220+ onCancel ( )
199221 return true
200222 }
201223 return false
@@ -391,6 +413,13 @@ struct FilterValueTextField: NSViewRepresentable {
391413 }
392414
393415 private final class SubstitutionDisabledTextField : NSTextField {
416+ weak var owner : Coordinator ?
417+
418+ override func viewDidMoveToWindow( ) {
419+ super. viewDidMoveToWindow ( )
420+ owner? . focusIfRequested ( )
421+ }
422+
394423 override func becomeFirstResponder( ) -> Bool {
395424 let result = super. becomeFirstResponder ( )
396425 if result, let editor = currentEditor ( ) as? NSTextView {
@@ -399,6 +428,17 @@ struct FilterValueTextField: NSViewRepresentable {
399428 editor. isAutomaticTextReplacementEnabled = false
400429 editor. isAutomaticSpellingCorrectionEnabled = false
401430 }
431+ if result {
432+ owner? . handleBecameFirstResponder ( )
433+ }
434+ return result
435+ }
436+
437+ override func resignFirstResponder( ) -> Bool {
438+ let result = super. resignFirstResponder ( )
439+ if result {
440+ owner? . handleResignedFirstResponder ( )
441+ }
402442 return result
403443 }
404444 }
@@ -438,6 +478,11 @@ struct FilterValueTextField: NSViewRepresentable {
438478 . contentShape ( Rectangle ( ) )
439479 . onTapGesture { onSelect ( item) }
440480 . id ( index)
481+ . accessibilityElement ( children: . ignore)
482+ . accessibilityLabel ( item. label)
483+ . accessibilityAddTraits (
484+ state. selectedIndex == index ? [ . isButton, . isSelected] : . isButton
485+ )
441486 }
442487 }
443488 . padding ( 4 )
0 commit comments