Skip to content

Commit 172c005

Browse files
committed
fix: avoid SwiftUI publishes when exiting url bar edit mode
1 parent cc20544 commit 172c005

4 files changed

Lines changed: 76 additions & 20 deletions

File tree

ora/Features/Browser/URLBar/URLBar.swift

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ struct URLBar: View {
8383

8484
private func setupInlineLauncher() {
8585
suppressInitialSearch = true
86-
if let tab = tabManager.activeTab {
86+
if launcherInput.isEmpty, let tab = tabManager.activeTab {
8787
launcherInput = tab.url.absoluteString
8888
}
8989
launcherViewModel.searchEngineService.setTheme(theme)
@@ -119,18 +119,26 @@ struct URLBar: View {
119119
}
120120

121121
private func cleanupInlineLauncher() {
122-
isLauncherFocused = false
123-
launcherInput = ""
124-
launcherViewModel.suggestions = []
125122
if let monitor = mouseMonitor {
126123
NSEvent.removeMonitor(monitor)
127124
mouseMonitor = nil
128125
}
126+
DispatchQueue.main.async {
127+
guard !appState.isURLBarEditing else { return }
128+
isLauncherFocused = false
129+
mouseHasMoved = false
130+
suppressInitialSearch = false
131+
launcherInput = ""
132+
launcherViewModel.reset()
133+
}
129134
}
130135

131136
private func dismissEditing() {
132-
withAnimation(.easeOut(duration: 0.2)) {
133-
appState.isURLBarEditing = false
137+
DispatchQueue.main.async {
138+
guard appState.isURLBarEditing else { return }
139+
withAnimation(.easeOut(duration: 0.2)) {
140+
appState.isURLBarEditing = false
141+
}
134142
}
135143
}
136144

@@ -256,13 +264,6 @@ struct URLBar: View {
256264
.onChange(of: tabManager.activeTab?.id) { _, _ in
257265
if isEditing { dismissEditing() }
258266
}
259-
.onChange(of: appState.isURLBarEditing) { _, newValue in
260-
if newValue {
261-
setupInlineLauncher()
262-
} else {
263-
cleanupInlineLauncher()
264-
}
265-
}
266267
.onChange(of: appState.showLauncher) { _, newValue in
267268
// Dismiss URL bar editing if the center launcher is opened
268269
if newValue, isEditing { dismissEditing() }
@@ -394,6 +395,12 @@ struct URLBar: View {
394395
return .handled
395396
}
396397
}
398+
.onAppear {
399+
setupInlineLauncher()
400+
}
401+
.onDisappear {
402+
cleanupInlineLauncher()
403+
}
397404
.frame(height: 30)
398405
.padding(.horizontal, 8)
399406
.frame(maxWidth: .infinity, alignment: .leading)

ora/Features/Browser/Views/BrowserWebContentView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ struct BrowserWebContentView: View {
3535
.frame(maxWidth: .infinity, maxHeight: .infinity)
3636
.contentShape(Rectangle())
3737
.onTapGesture {
38-
appState.isURLBarEditing = false
38+
DispatchQueue.main.async {
39+
appState.isURLBarEditing = false
40+
}
3941
}
4042
.transition(.opacity)
4143
}

ora/Features/Launcher/Main/LauncherTextField.swift

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

ora/Features/Launcher/Suggestions/LauncherSuggestionItem.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,10 @@ struct LauncherSuggestionItem: View {
132132
.clipShape(ConditionallyConcentricRectangle(cornerRadius: 12, style: .continuous))
133133
.onTapGesture {
134134
suggestion.action()
135-
appState.showLauncher = false
136-
appState.isURLBarEditing = false
135+
DispatchQueue.main.async {
136+
appState.showLauncher = false
137+
appState.isURLBarEditing = false
138+
}
137139
}
138140
.onHover { hover in
139141
if hover, mouseHasMoved {

0 commit comments

Comments
 (0)