diff --git a/macos/Sources/Features/Terminal/TerminalView.swift b/macos/Sources/Features/Terminal/TerminalView.swift index 1aab8f497..697009579 100644 --- a/macos/Sources/Features/Terminal/TerminalView.swift +++ b/macos/Sources/Features/Terminal/TerminalView.swift @@ -47,9 +47,8 @@ struct TerminalView: 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 = .init() + /// The most recently focused surface, equal to `focusedSurface` when it is non-nil. + @State private var lastFocusedSurface: Weak? // This seems like a crutch after switching from SwiftUI to AppKit lifecycle. @FocusState private var focused: Bool @@ -84,6 +83,7 @@ struct TerminalView: 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: 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, diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView.swift b/macos/Sources/Ghostty/Surface View/SurfaceView.swift index fb5a1a864..47503dc0e 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView.swift @@ -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? = nil +} + extension EnvironmentValues { var ghosttySurfaceView: Ghostty.SurfaceView? { get { self[GhosttySurfaceViewKey.self] } set { self[GhosttySurfaceViewKey.self] = newValue } } + + var ghosttyLastFocusedSurface: Weak? { + 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?) -> some View { + environment(\.ghosttyLastFocusedSurface, surfaceView) + } } // MARK: Surface Focus Keys