feat: select needle when reading from pasteboard

This commit is contained in:
Nolin McFarland
2026-05-17 11:26:32 -04:00
parent 8fa42c6ec0
commit 69cab3d808
4 changed files with 68 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
import Foundation
import GhosttyKit
import SwiftUI
extension Ghostty {
class OSSurfaceView: OSView, ObservableObject {
@@ -125,6 +126,9 @@ extension Ghostty.OSSurfaceView {
@Published var selected: UInt?
@Published var total: UInt?
/// The range of the needle's text selection in the find bar.
@Published var needleSelection: Range<String.Index>?
init(
from startSearch: Ghostty.Action.StartSearch,
pasteboard: OSPasteboard = OSPasteboard(name: .find)
@@ -142,6 +146,7 @@ extension Ghostty.OSSurfaceView {
let pasteboardNeedle = pasteboard.string(forType: .string)
if let pasteboardNeedle, pasteboardNeedle != needle {
needle = pasteboardNeedle
needleSelection = needle.startIndex..<needle.endIndex
}
}

View File

@@ -377,7 +377,11 @@ extension Ghostty {
var body: some View {
GeometryReader { geo in
HStack(spacing: 4) {
TextField("Search", text: $searchState.needle)
BackportSelectionTextField(
"Search",
text: $searchState.needle,
selection: $searchState.needleSelection
)
.textFieldStyle(.plain)
.frame(width: 180)
.padding(.leading, 8)

View File

@@ -131,3 +131,49 @@ enum BackportNSGlassStyle {
}
#endif
}
/// Backported `TextField` that supports text selection on macOS 15 and up. The `selection` has no
/// effect on versions below macOS 15.
struct BackportSelectionTextField: View {
private let titleKey: LocalizedStringKey
@Binding private var text: String
@Binding private var textSelection: Range<String.Index>?
init(
_ titleKey: LocalizedStringKey,
text: Binding<String>,
selection: Binding<Range<String.Index>?>
) {
self.titleKey = titleKey
self._text = text
self._textSelection = selection
}
var body: some View {
if #available(macOS 15, *) {
TextField(
titleKey,
text: _text,
selection: Binding(
get: {
if let textSelection {
TextSelection(range: textSelection)
} else {
nil
}
},
set: { selection in
if let selection,
case .selection(let range) = selection.indices {
self.textSelection = range
} else {
self.textSelection = nil
}
}
)
)
} else {
TextField(titleKey, text: _text)
}
}
}

View File

@@ -84,4 +84,16 @@ import Testing
sut.readPasteboardNeedle()
#expect(sut.needle == "pb")
}
@Test func readPasteboardNeedle_setsNeedleSelectionRange() {
let sut = SearchState(
from: StartSearch(c: .init(needle: nil)),
pasteboard: pasteboard
)
sut.needle = "sut"
sut.readPasteboardNeedle()
let expected = "pb".startIndex..<"pb".endIndex
#expect(sut.needleSelection == expected)
}
}