mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 03:25:50 +00:00
macos: pass last focused surface as env, use for focus detection (#11024)
Fixes #10935 This is a more robust way to detect "is my surface focused" because that question usually means "is my surface the last focused surface" if a _different_ surface is not focused. We already have used this pattern all over but we should extend it to SwiftUI too.
This commit is contained in:
@@ -47,9 +47,8 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
// An optional delegate to receive information about terminal changes.
|
||||
weak var delegate: (any TerminalViewDelegate)?
|
||||
|
||||
// The most recently focused surface, equal to focusedSurface when
|
||||
// it is non-nil.
|
||||
@State private var lastFocusedSurface: Weak<Ghostty.SurfaceView> = .init()
|
||||
/// The most recently focused surface, equal to `focusedSurface` when it is non-nil.
|
||||
@State private var lastFocusedSurface: Weak<Ghostty.SurfaceView>?
|
||||
|
||||
// This seems like a crutch after switching from SwiftUI to AppKit lifecycle.
|
||||
@FocusState private var focused: Bool
|
||||
@@ -84,6 +83,7 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
tree: viewModel.surfaceTree,
|
||||
action: { delegate?.performSplitAction($0) })
|
||||
.environmentObject(ghostty)
|
||||
.ghosttyLastFocusedSurface(lastFocusedSurface)
|
||||
.focused($focused)
|
||||
.onAppear { self.focused = true }
|
||||
.onChange(of: focusedSurface) { newValue in
|
||||
@@ -101,13 +101,13 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
guard let size = newValue else { return }
|
||||
self.delegate?.cellSizeDidChange(to: size)
|
||||
}
|
||||
.frame(idealWidth: lastFocusedSurface.value?.initialSize?.width,
|
||||
idealHeight: lastFocusedSurface.value?.initialSize?.height)
|
||||
.frame(idealWidth: lastFocusedSurface?.value?.initialSize?.width,
|
||||
idealHeight: lastFocusedSurface?.value?.initialSize?.height)
|
||||
}
|
||||
// Ignore safe area to extend up in to the titlebar region if we have the "hidden" titlebar style
|
||||
.ignoresSafeArea(.container, edges: ghostty.config.macosTitlebarStyle == "hidden" ? .top : [])
|
||||
|
||||
if let surfaceView = lastFocusedSurface.value {
|
||||
if let surfaceView = lastFocusedSurface?.value {
|
||||
TerminalCommandPaletteView(
|
||||
surfaceView: surfaceView,
|
||||
isPresented: $viewModel.commandPaletteIsShowing,
|
||||
|
||||
@@ -56,6 +56,11 @@ extension Ghostty {
|
||||
#endif
|
||||
|
||||
@EnvironmentObject private var ghostty: Ghostty.App
|
||||
@Environment(\.ghosttyLastFocusedSurface) private var lastFocusedSurface
|
||||
|
||||
private var isFocusedSurface: Bool {
|
||||
surfaceFocus || lastFocusedSurface?.value === surfaceView
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let center = NotificationCenter.default
|
||||
@@ -217,10 +222,9 @@ extension Ghostty {
|
||||
}
|
||||
|
||||
// If we're part of a split view and don't have focus, we put a semi-transparent
|
||||
// rectangle above our view to make it look unfocused. We use "surfaceFocus"
|
||||
// because we want to keep our focused surface dark even if we don't have window
|
||||
// focus.
|
||||
if isSplit && !surfaceFocus {
|
||||
// rectangle above our view to make it look unfocused. We include the last
|
||||
// focused surface so this still works while SwiftUI focus is temporarily nil.
|
||||
if isSplit && !isFocusedSurface {
|
||||
let overlayOpacity = ghostty.config.unfocusedSplitOpacity
|
||||
if overlayOpacity > 0 {
|
||||
Rectangle()
|
||||
@@ -1201,17 +1205,33 @@ private struct GhosttySurfaceViewKey: EnvironmentKey {
|
||||
static let defaultValue: Ghostty.SurfaceView? = nil
|
||||
}
|
||||
|
||||
private struct GhosttyLastFocusedSurfaceKey: EnvironmentKey {
|
||||
/// Optional read-only last-focused surface reference. If a surface view is currently focused this
|
||||
/// is equal to the currently focused surface.
|
||||
static let defaultValue: Weak<Ghostty.SurfaceView>? = nil
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var ghosttySurfaceView: Ghostty.SurfaceView? {
|
||||
get { self[GhosttySurfaceViewKey.self] }
|
||||
set { self[GhosttySurfaceViewKey.self] = newValue }
|
||||
}
|
||||
|
||||
var ghosttyLastFocusedSurface: Weak<Ghostty.SurfaceView>? {
|
||||
get { self[GhosttyLastFocusedSurfaceKey.self] }
|
||||
set { self[GhosttyLastFocusedSurfaceKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func ghosttySurfaceView(_ surfaceView: Ghostty.SurfaceView?) -> some View {
|
||||
environment(\.ghosttySurfaceView, surfaceView)
|
||||
}
|
||||
|
||||
/// The most recently focused surface (can be currently focused if the surface is currently focused).
|
||||
func ghosttyLastFocusedSurface(_ surfaceView: Weak<Ghostty.SurfaceView>?) -> some View {
|
||||
environment(\.ghosttyLastFocusedSurface, surfaceView)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Surface Focus Keys
|
||||
|
||||
Reference in New Issue
Block a user