diff --git a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift index fcc8c6505..37c9985c9 100644 --- a/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift +++ b/macos/Sources/Features/QuickTerminal/QuickTerminalController.swift @@ -37,7 +37,7 @@ class QuickTerminalController: BaseTerminalController { /// Tracks if we're currently handling a manual resize to prevent recursion private var isHandlingResize: Bool = false - + init(_ ghostty: Ghostty.App, position: QuickTerminalPosition = .top, baseConfig base: Ghostty.SurfaceConfiguration? = nil, diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index f660ea3ad..b9f9c5a05 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -48,6 +48,9 @@ class BaseTerminalController: NSWindowController, /// This can be set to show/hide the command palette. @Published var commandPaletteIsShowing: Bool = false + + /// Set if the terminal view should show the update overlay. + @Published var updateOverlayIsVisible: Bool = false /// Whether the terminal surface should focus when the mouse is over it. var focusFollowsMouse: Bool { @@ -818,7 +821,18 @@ class BaseTerminalController: NSWindowController, } } - func fullscreenDidChange() {} + func fullscreenDidChange() { + guard let fullscreenStyle else { return } + + // When we enter fullscreen, we want to show the update overlay so that it + // is easily visible. For native fullscreen this is visible by showing the + // menubar but we don't want to rely on that. + if fullscreenStyle.isFullscreen { + updateOverlayIsVisible = true + } else { + updateOverlayIsVisible = defaultUpdateOverlayVisibility() + } + } // MARK: Clipboard Confirmation @@ -900,6 +914,28 @@ class BaseTerminalController: NSWindowController, fullscreenStyle = NativeFullscreen(window) fullscreenStyle?.delegate = self } + + // Set our update overlay state + updateOverlayIsVisible = defaultUpdateOverlayVisibility() + } + + func defaultUpdateOverlayVisibility() -> Bool { + guard let window else { return true } + + // No titlebar we always show the update overlay because it can't support + // updates in the titlebar + guard window.styleMask.contains(.titled) else { + return true + } + + // If it's a non terminal window we can't trust it has an update accessory, + // so we always want to show the overlay. + guard let window = window as? TerminalWindow else { + return true + } + + // Show the overlay if the window isn't. + return !window.supportsUpdateAccessory } // MARK: NSWindowDelegate diff --git a/macos/Sources/Features/Terminal/TerminalView.swift b/macos/Sources/Features/Terminal/TerminalView.swift index 54d2011c7..832cd7966 100644 --- a/macos/Sources/Features/Terminal/TerminalView.swift +++ b/macos/Sources/Features/Terminal/TerminalView.swift @@ -31,6 +31,9 @@ protocol TerminalViewModel: ObservableObject { /// The command palette state. var commandPaletteIsShowing: Bool { get set } + + /// The update overlay should be visible. + var updateOverlayIsVisible: Bool { get } } /// The main terminal view. This terminal view supports splits. @@ -111,7 +114,9 @@ struct TerminalView: View { } // Show update information above all else. - UpdateOverlay() + if viewModel.updateOverlayIsVisible { + UpdateOverlay() + } } } } diff --git a/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift index dc7dd7633..dd8b258f3 100644 --- a/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift @@ -1,6 +1,9 @@ import AppKit class HiddenTitlebarTerminalWindow: TerminalWindow { + // No titlebar, we don't support accessories. + override var supportsUpdateAccessory: Bool { false } + override func awakeFromNib() { super.awakeFromNib() diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift index 62439f676..6e657f33e 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift @@ -20,12 +20,19 @@ class TerminalWindow: NSWindow { /// The configuration derived from the Ghostty config so we don't need to rely on references. private(set) var derivedConfig: DerivedConfig = .init() + + /// Whether this window supports the update accessory. If this is false, then views within this + /// window should determine how to show update notifications. + var supportsUpdateAccessory: Bool { + // Native window supports it. + true + } /// Gets the terminal controller from the window controller. var terminalController: TerminalController? { windowController as? TerminalController } - + // MARK: NSWindow Overrides override var toolbar: NSToolbar? { @@ -90,14 +97,16 @@ class TerminalWindow: NSWindow { resetZoomAccessory.view.translatesAutoresizingMaskIntoConstraints = false // Create update notification accessory - updateAccessory.layoutAttribute = .right - updateAccessory.view = NSHostingView(rootView: UpdateAccessoryView( - viewModel: viewModel, - model: appDelegate.updateUIModel, - actions: appDelegate.updateActions - )) - addTitlebarAccessoryViewController(updateAccessory) - updateAccessory.view.translatesAutoresizingMaskIntoConstraints = false + if supportsUpdateAccessory { + updateAccessory.layoutAttribute = .right + updateAccessory.view = NSHostingView(rootView: UpdateAccessoryView( + viewModel: viewModel, + model: appDelegate.updateUIModel, + actions: appDelegate.updateActions + )) + addTitlebarAccessoryViewController(updateAccessory) + updateAccessory.view.translatesAutoresizingMaskIntoConstraints = false + } } // Setup the accessory view for tabs that shows our keyboard shortcuts, diff --git a/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift index 260fac4cc..855d29f52 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift @@ -8,6 +8,10 @@ import SwiftUI class TitlebarTabsTahoeTerminalWindow: TransparentTitlebarTerminalWindow, NSToolbarDelegate { /// The view model for SwiftUI views private var viewModel = ViewModel() + + /// Titlebar tabs can't support the update accessory because of the way we layout + /// the native tabs back into the menu bar. + override var supportsUpdateAccessory: Bool { false } deinit { tabBarObserver = nil diff --git a/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift index 8589877d8..0c087faeb 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift @@ -2,6 +2,10 @@ import Cocoa /// Titlebar tabs for macOS 13 to 15. class TitlebarTabsVenturaTerminalWindow: TerminalWindow { + /// Titlebar tabs can't support the update accessory because of the way we layout + /// the native tabs back into the menu bar. + override var supportsUpdateAccessory: Bool { false } + /// This is used to determine if certain elements should be drawn light or dark and should /// be updated whenever the window background color or surrounding elements changes. fileprivate var isLightTheme: Bool = false