Skip to content

Commit

Permalink
Restore hidden titlebar after fullscreen
Browse files Browse the repository at this point in the history
This fixes #3535 .

There exists an issue in ghostty on mac where if you have hidden your
titlebar, then enter fullscreen, the titlebar will reappear after
exiting fullscreen.

The reason for this is that after exiting fullscreen macos reapplies
some styling on the new window created after exiting fullscreen. To
combat this we will reapply the styling to hide the titlebar after
exiting fullscreen.

Steps to reproduce:

- Open Ghostty
- Enter fullscreen (non-native)
- Exit fullscreen

On main you will see the titlebar reappearing after exiting fullscreen,
while that does not happen with this patch.
  • Loading branch information
cristeahub committed Dec 27, 2024
1 parent a8e5eef commit 12c29de
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ class BaseTerminalController: NSWindowController,
}
}

func fullscreenDidChange() {
func fullscreenDidChange(_ action: FullscreenModeAction) {
// For some reason focus can get lost when we change fullscreen. Regardless of
// mode above we just move it back.
if let focusedSurface {
Expand Down
80 changes: 46 additions & 34 deletions macos/Sources/Features/Terminal/TerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,20 @@ class TerminalController: BaseTerminalController {
}


override func fullscreenDidChange() {
super.fullscreenDidChange()
override func fullscreenDidChange(_ action: FullscreenModeAction) {
super.fullscreenDidChange(action)

// When our fullscreen state changes, we resync our appearance because some
// properties change when fullscreen or not.
guard let focusedSurface else { return }
if window != nil,
let window = window as? TerminalWindow,
action == .exit,
ghostty.config.macosTitlebarStyle == "hidden"
{
applyHiddenTitlebarStyle(window)
}

syncAppearance(focusedSurface.derivedConfig)
}

Expand Down Expand Up @@ -277,6 +285,41 @@ class TerminalController: BaseTerminalController {
shouldCascadeWindows = false
}

fileprivate func applyHiddenTitlebarStyle(_ window: TerminalWindow) {
window.styleMask = [
// We need `titled` in the mask to get the normal window frame
.titled,

// Full size content view so we can extend
// content in to the hidden titlebar's area
.fullSizeContentView,

.resizable,
.closable,
.miniaturizable,
]

// Hide the title
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true

// Hide the traffic lights (window control buttons)
window.standardWindowButton(.closeButton)?.isHidden = true
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
window.standardWindowButton(.zoomButton)?.isHidden = true

// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
window.tabbingMode = .disallowed

// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
// some operations that appear to bring back the titlebar visibility so this ensures
// it is gone forever.
if let themeFrame = window.contentView?.superview,
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
titleBarContainer.isHidden = true
}
}

override func windowDidLoad() {
super.windowDidLoad()
guard let window = window as? TerminalWindow else { return }
Expand Down Expand Up @@ -368,38 +411,7 @@ class TerminalController: BaseTerminalController {

// If our titlebar style is "hidden" we adjust the style appropriately
if (config.macosTitlebarStyle == "hidden") {
window.styleMask = [
// We need `titled` in the mask to get the normal window frame
.titled,

// Full size content view so we can extend
// content in to the hidden titlebar's area
.fullSizeContentView,

.resizable,
.closable,
.miniaturizable,
]

// Hide the title
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true

// Hide the traffic lights (window control buttons)
window.standardWindowButton(.closeButton)?.isHidden = true
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
window.standardWindowButton(.zoomButton)?.isHidden = true

// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
window.tabbingMode = .disallowed

// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
// some operations that appear to bring back the titlebar visibility so this ensures
// it is gone forever.
if let themeFrame = window.contentView?.superview,
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
titleBarContainer.isHidden = true
}
applyHiddenTitlebarStyle(window)
}

// In various situations, macOS automatically tabs new windows. Ghostty handles
Expand Down
17 changes: 11 additions & 6 deletions macos/Sources/Helpers/Fullscreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,20 @@ protocol FullscreenStyle {
func exit()
}

enum FullscreenModeAction {
case enter
case exit
}

/// Delegate that can be implemented for fullscreen implementations.
protocol FullscreenDelegate: AnyObject {
/// Called whenever the fullscreen state changed. You can call isFullscreen to see
/// the current state.
func fullscreenDidChange()
func fullscreenDidChange(_ action: FullscreenModeAction)
}

extension FullscreenDelegate {
func fullscreenDidChange() {}
func fullscreenDidChange(_ action: FullscreenModeAction) {}
}

/// The base class for fullscreen implementations, cannot be used as a FullscreenStyle on its own.
Expand Down Expand Up @@ -74,11 +79,11 @@ class FullscreenBase {
}

@objc private func didEnterFullScreenNotification(_ notification: Notification) {
delegate?.fullscreenDidChange()
delegate?.fullscreenDidChange(.enter)
}

@objc private func didExitFullScreenNotification(_ notification: Notification) {
delegate?.fullscreenDidChange()
delegate?.fullscreenDidChange(.exit)
}
}

Expand Down Expand Up @@ -202,7 +207,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
// https://github.com/ghostty-org/ghostty/issues/1996
DispatchQueue.main.async {
self.window.setFrame(self.fullscreenFrame(screen), display: true)
self.delegate?.fullscreenDidChange()
self.delegate?.fullscreenDidChange(.enter)
}
}

Expand Down Expand Up @@ -258,7 +263,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
window.makeKeyAndOrderFront(nil)

// Notify the delegate
self.delegate?.fullscreenDidChange()
self.delegate?.fullscreenDidChange(.exit)
}

private func fullscreenFrame(_ screen: NSScreen) -> NSRect {
Expand Down

0 comments on commit 12c29de

Please sign in to comment.