@@ -15,13 +15,35 @@ struct LauncherTextField: NSViewRepresentable {
1515 class CustomTextField : NSTextField {
1616 var cursorColor : NSColor ?
1717
18+ private func configureEditorIfNeeded( ) {
19+ guard let textView = currentEditor ( ) as? NSTextView else { return }
20+ if let color = cursorColor {
21+ textView. insertionPointColor = color
22+ }
23+ textView. isHorizontallyResizable = true
24+ textView. isVerticallyResizable = false
25+ textView. textContainerInset = . zero
26+ textView. textContainer? . widthTracksTextView = false
27+ textView. textContainer? . containerSize = NSSize (
28+ width: . greatestFiniteMagnitude,
29+ height: bounds. height
30+ )
31+ textView. textContainer? . lineBreakMode = . byClipping
32+ textView. textContainer? . maximumNumberOfLines = 1
33+ }
34+
1835 override func becomeFirstResponder( ) -> Bool {
1936 let didBecome = super. becomeFirstResponder ( )
20- if didBecome, let textView = currentEditor ( ) as? NSTextView , let color = cursorColor {
21- textView . insertionPointColor = color
37+ if didBecome {
38+ configureEditorIfNeeded ( )
2239 }
2340 return didBecome
2441 }
42+
43+ override func textDidBeginEditing( _ notification: Notification ) {
44+ super. textDidBeginEditing ( notification)
45+ configureEditorIfNeeded ( )
46+ }
2547 }
2648
2749 func makeCoordinator( ) -> Coordinator {
@@ -37,33 +59,56 @@ struct LauncherTextField: NSViewRepresentable {
3759 textField. focusRingType = . none
3860 textField. drawsBackground = false
3961 textField. placeholderString = placeholder
62+ textField. lineBreakMode = . byClipping
63+ textField. maximumNumberOfLines = 1
64+ textField. usesSingleLineMode = true
65+ textField. cell? . wraps = false
66+ textField. cell? . isScrollable = true
4067 if let textColor {
4168 textField. textColor = NSColor ( textColor)
4269 }
4370 return textField
4471 }
4572
4673 func updateNSView( _ nsView: CustomTextField , context: Context ) {
47- nsView. stringValue = text
74+ if nsView. stringValue != text {
75+ // Prevent the AppKit delegate callback from bouncing this write
76+ // straight back into SwiftUI during the same update pass.
77+ context. coordinator. isProgrammaticUpdate = true
78+ nsView. stringValue = text
79+ context. coordinator. isProgrammaticUpdate = false
80+ }
4881 nsView. cursorColor = NSColor ( cursorColor)
4982 nsView. placeholderString = placeholder
5083 if let textColor {
5184 nsView. textColor = NSColor ( textColor)
5285 }
5386 if let textView = nsView. currentEditor ( ) as? NSTextView {
5487 textView. insertionPointColor = nsView. cursorColor
88+ textView. isHorizontallyResizable = true
89+ textView. isVerticallyResizable = false
90+ textView. textContainerInset = . zero
91+ textView. textContainer? . widthTracksTextView = false
92+ textView. textContainer? . containerSize = NSSize (
93+ width: . greatestFiniteMagnitude,
94+ height: nsView. bounds. height
95+ )
96+ textView. textContainer? . lineBreakMode = . byClipping
97+ textView. textContainer? . maximumNumberOfLines = 1
5598 }
5699 }
57100
58101 class Coordinator : NSObject , NSTextFieldDelegate {
59102 var parent : LauncherTextField
103+ var isProgrammaticUpdate = false
60104
61105 init ( _ parent: LauncherTextField ) {
62106 self . parent = parent
63107 }
64108
65109 func controlTextDidChange( _ obj: Notification ) {
66- if let textField = obj. object as? NSTextField {
110+ guard !isProgrammaticUpdate else { return }
111+ if let textField = obj. object as? NSTextField , parent. text != textField. stringValue {
67112 parent. text = textField. stringValue
68113 }
69114 }
0 commit comments