diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index 6903c8439..9de22b3d9 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -292,6 +292,7 @@ class BaseTerminalController: NSWindowController, if to.isEmpty { focusedSurface = nil } + syncSurfaceTreeOcclusionState() } /// Update all surfaces with the focus state. This ensures that libghostty has an accurate view about @@ -470,8 +471,12 @@ class BaseTerminalController: NSWindowController, replaceSurfaceTree( surfaceTree.removing(node), - moveFocusTo: nextFocus, - moveFocusFrom: focusedSurface, + // When a non-focused surface is removed and this window stays as the key window, + // we should refocus the `focusedSurface` to make sure the window's firstResponder remains as it is. + // + // This is a weird workaround, since `resignFirstResponder` wasn't called on `focusedSurface` after drag, + // but the first responder became the window itself. + moveFocusTo: nextFocus ?? focusedSurface, undoAction: "Close Terminal" ) } @@ -1277,10 +1282,15 @@ class BaseTerminalController: NSWindowController, } func windowDidChangeOcclusionState(_ notification: Notification) { + syncSurfaceTreeOcclusionState() + } + + private func syncSurfaceTreeOcclusionState() { let visible = self.window?.occlusionState.contains(.visible) ?? false for view in surfaceTree { - if let surface = view.surface { + if let surface = view.surface, view.isWindowVisible != visible { ghostty_surface_set_occlusion(surface, visible) + view.isWindowVisible = visible } } } diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift index b1920f170..887482b30 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift @@ -89,6 +89,12 @@ extension Ghostty { // Whether the cursor is currently visible (not hidden by typing, etc.) @Published private(set) var cursorVisible: Bool = true + /// Whether the belonging window is visible + /// + /// We track this to restore surface occlusion state + /// after this surface is dragged to another window + var isWindowVisible = false + /// The configuration derived from the Ghostty config so we don't need to rely on references. @Published private(set) var derivedConfig: DerivedConfig