macos: hook up start_search apprt action to open search
parent
aeaa8d4ead
commit
bc44b187d6
|
|
@ -115,6 +115,18 @@ extension Ghostty.Action {
|
||||||
len = c.len
|
len = c.len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StartSearch {
|
||||||
|
let needle: String?
|
||||||
|
|
||||||
|
init(c: ghostty_action_start_search_s) {
|
||||||
|
if let needleCString = c.needle {
|
||||||
|
self.needle = String(cString: needleCString)
|
||||||
|
} else {
|
||||||
|
self.needle = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Putting the initializer in an extension preserves the automatic one.
|
// Putting the initializer in an extension preserves the automatic one.
|
||||||
|
|
|
||||||
|
|
@ -606,6 +606,9 @@ extension Ghostty {
|
||||||
case GHOSTTY_ACTION_CLOSE_ALL_WINDOWS:
|
case GHOSTTY_ACTION_CLOSE_ALL_WINDOWS:
|
||||||
closeAllWindows(app, target: target)
|
closeAllWindows(app, target: target)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_START_SEARCH:
|
||||||
|
startSearch(app, target: target, v: action.action.start_search)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW:
|
case GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW:
|
||||||
fallthrough
|
fallthrough
|
||||||
case GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS:
|
case GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS:
|
||||||
|
|
@ -1641,6 +1644,29 @@ extension Ghostty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func startSearch(
|
||||||
|
_ app: ghostty_app_t,
|
||||||
|
target: ghostty_target_s,
|
||||||
|
v: ghostty_action_start_search_s) {
|
||||||
|
switch (target.tag) {
|
||||||
|
case GHOSTTY_TARGET_APP:
|
||||||
|
Ghostty.logger.warning("start_search does nothing with an app target")
|
||||||
|
return
|
||||||
|
|
||||||
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
|
guard let surface = target.target.surface else { return }
|
||||||
|
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||||
|
|
||||||
|
let startSearch = Ghostty.Action.StartSearch(c: v)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
surfaceView.searchState = Ghostty.SurfaceView.SearchState(from: startSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func configReload(
|
private static func configReload(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
|
|
|
||||||
|
|
@ -197,10 +197,12 @@ extension Ghostty {
|
||||||
SecureInputOverlay()
|
SecureInputOverlay()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Search overlay
|
// Search overlay
|
||||||
SurfaceSearchOverlay()
|
if surfaceView.searchState != nil {
|
||||||
|
SurfaceSearchOverlay(searchState: $surfaceView.searchState)
|
||||||
|
}
|
||||||
|
|
||||||
// Show bell border if enabled
|
// Show bell border if enabled
|
||||||
if (ghostty.config.bellFeatures.contains(.border)) {
|
if (ghostty.config.bellFeatures.contains(.border)) {
|
||||||
BellBorderOverlay(bell: surfaceView.bell)
|
BellBorderOverlay(bell: surfaceView.bell)
|
||||||
|
|
@ -387,10 +389,12 @@ extension Ghostty {
|
||||||
|
|
||||||
/// Search overlay view that displays a search bar with input field and navigation buttons.
|
/// Search overlay view that displays a search bar with input field and navigation buttons.
|
||||||
struct SurfaceSearchOverlay: View {
|
struct SurfaceSearchOverlay: View {
|
||||||
|
@Binding var searchState: SurfaceView.SearchState?
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@State private var corner: Corner = .topRight
|
@State private var corner: Corner = .topRight
|
||||||
@State private var dragOffset: CGSize = .zero
|
@State private var dragOffset: CGSize = .zero
|
||||||
@State private var barSize: CGSize = .zero
|
@State private var barSize: CGSize = .zero
|
||||||
|
@FocusState private var isSearchFieldFocused: Bool
|
||||||
|
|
||||||
private let padding: CGFloat = 8
|
private let padding: CGFloat = 8
|
||||||
|
|
||||||
|
|
@ -404,6 +408,7 @@ extension Ghostty {
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
.background(Color.primary.opacity(0.1))
|
.background(Color.primary.opacity(0.1))
|
||||||
.cornerRadius(6)
|
.cornerRadius(6)
|
||||||
|
.focused($isSearchFieldFocused)
|
||||||
|
|
||||||
Button(action: {}) {
|
Button(action: {}) {
|
||||||
Image(systemName: "chevron.up")
|
Image(systemName: "chevron.up")
|
||||||
|
|
@ -415,7 +420,9 @@ extension Ghostty {
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
|
|
||||||
Button(action: {}) {
|
Button(action: {
|
||||||
|
searchState = nil
|
||||||
|
}) {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
|
|
@ -424,6 +431,12 @@ extension Ghostty {
|
||||||
.background(.background)
|
.background(.background)
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
.shadow(radius: 4)
|
.shadow(radius: 4)
|
||||||
|
.onAppear {
|
||||||
|
if let needle = searchState?.needle {
|
||||||
|
searchText = needle
|
||||||
|
}
|
||||||
|
isSearchFieldFocused = true
|
||||||
|
}
|
||||||
.background(
|
.background(
|
||||||
GeometryReader { barGeo in
|
GeometryReader { barGeo in
|
||||||
Color.clear.onAppear {
|
Color.clear.onAppear {
|
||||||
|
|
@ -770,3 +783,15 @@ extension FocusedValues {
|
||||||
typealias Value = OSSize
|
typealias Value = OSSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Search State
|
||||||
|
|
||||||
|
extension Ghostty.SurfaceView {
|
||||||
|
class SearchState: ObservableObject {
|
||||||
|
@Published var needle: String = ""
|
||||||
|
|
||||||
|
init(from startSearch: Ghostty.Action.StartSearch) {
|
||||||
|
self.needle = startSearch.needle ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,9 @@ extension Ghostty {
|
||||||
// The currently active key sequence. The sequence is not active if this is empty.
|
// The currently active key sequence. The sequence is not active if this is empty.
|
||||||
@Published var keySequence: [KeyboardShortcut] = []
|
@Published var keySequence: [KeyboardShortcut] = []
|
||||||
|
|
||||||
|
// The current search state. When non-nil, the search overlay should be shown.
|
||||||
|
@Published var searchState: SearchState? = nil
|
||||||
|
|
||||||
// The time this surface last became focused. This is a ContinuousClock.Instant
|
// The time this surface last became focused. This is a ContinuousClock.Instant
|
||||||
// on supported platforms.
|
// on supported platforms.
|
||||||
@Published var focusInstant: ContinuousClock.Instant? = nil
|
@Published var focusInstant: ContinuousClock.Instant? = nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue