diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index 690c7f132..fac3a2fbb 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -33,9 +33,6 @@ class QuickTerminalController: BaseTerminalController { /// The configuration derived from the Ghostty config so we don't need to rely on references. private var derivedConfig: DerivedConfig - /// Storage for the window frame before entering fullscreen mode - private var savedWindowFrame: NSRect? = nil - init(_ ghostty: Ghostty.App, position: QuickTerminalPosition = .top, baseConfig base: Ghostty.SurfaceConfiguration? = nil, @@ -488,16 +485,9 @@ class QuickTerminalController: BaseTerminalController { @objc private func onToggleFullscreen(notification: SwiftUI.Notification) { guard let target = notification.object as? Ghostty.SurfaceView else { return } guard target == self.focusedSurface else { return } - guard let window = self.window else { return } - guard let screen = window.screen ?? NSScreen.main else { return } - if let originalFrame = savedWindowFrame { - window.setFrame(originalFrame, display: true) - savedWindowFrame = nil - } else { - savedWindowFrame = window.frame - window.setFrame(screen.frame, display: true) - } + // We ignore the requested mode and always use non-native for the quick terminal + toggleFullscreen(mode: .nonNative) } @objc private func ghosttyConfigDidChange(_ notification: Notification) { diff --git a/macos/Sources/Helpers/Fullscreen.swift b/macos/Sources/Helpers/Fullscreen.swift index 59865fc9e..850485830 100644 --- a/macos/Sources/Helpers/Fullscreen.swift +++ b/macos/Sources/Helpers/Fullscreen.swift @@ -167,6 +167,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // screen the window is currently on. guard let screen = window.screen else { return } + // Check if we're on a space that has a fullscreen window already + let alreadyHasFullscreenWindow = hasScreenWithFullscreenWindow(screen) + // Save the state that we need to exit again guard let savedState = SavedState(window) else { return } self.savedState = savedState @@ -179,8 +182,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { hideDock() } - // Hide the menu if requested - if (properties.hideMenu) { + // Hide the menu if requested and if we don't already have a fullscreen window + // on this space (which would cause conflicts with presentation options) + if (properties.hideMenu && !alreadyHasFullscreenWindow) { hideMenu() } @@ -325,11 +329,32 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle { // MARK: Menu func hideMenu() { - NSApp.acquirePresentationOption(.autoHideMenuBar) + // Check if we're on a space that already has a fullscreen window + // which would cause macOS to already have the menu bar hidden + let currentOptions = NSApp.presentationOptions + if !currentOptions.contains(.autoHideMenuBar) && !currentOptions.contains(.hideMenuBar) { + // Only set if not already managed by the system + NSApp.acquirePresentationOption(.autoHideMenuBar) + } } func unhideMenu() { - NSApp.releasePresentationOption(.autoHideMenuBar) + let currentOptions = NSApp.presentationOptions + if currentOptions.contains(.autoHideMenuBar) { + // Only release what we've acquired + NSApp.releasePresentationOption(.autoHideMenuBar) + } + } + + /// Determines if a screen already has a fullscreen window, which would + /// affect how we handle presentation options + private func hasScreenWithFullscreenWindow(_ screen: NSScreen) -> Bool { + // Check if we're on a space with a fullscreen window already + let currentOptions = NSApp.presentationOptions + + // If any of these are set, macOS is probably already managing the menu bar + return currentOptions.contains(.hideMenuBar) || + currentOptions.contains(.autoHideMenuBar) } /// The state that must be saved for non-native fullscreen to exit fullscreen.