mirror of
				https://github.com/ghostty-org/ghostty.git
				synced 2025-11-04 01:34:21 +00:00 
			
		
		
		
	macos: trigger fullscreenDidChange on any fullscreen event
Fixes #2840 Related to #2842 This builds on #2842 by missing a key situation: when native fullscreen is toggled using the menu bar items it doesn't go through our `FullscreenStyle` machinery so we don't trigger fullscreen change events. This commit makes it so that our FullscreenStyle always listens for native fullscreen change (even in non-native modes) to fire a fullscreen did change event. This way we can always rely on the event to be fired when fullscreen changes no matter what.
This commit is contained in:
		@@ -57,6 +57,7 @@ class QuickTerminalController: BaseTerminalController {
 | 
				
			|||||||
    // MARK: NSWindowController
 | 
					    // MARK: NSWindowController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override func windowDidLoad() {
 | 
					    override func windowDidLoad() {
 | 
				
			||||||
 | 
					        super.windowDidLoad()
 | 
				
			||||||
        guard let window = self.window else { return }
 | 
					        guard let window = self.window else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // The controller is the window delegate so we can detect events such as
 | 
					        // The controller is the window delegate so we can detect events such as
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -404,7 +404,21 @@ class BaseTerminalController: NSWindowController,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //MARK: - NSWindowDelegate
 | 
					    // MARK: NSWindowController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override func windowDidLoad() {
 | 
				
			||||||
 | 
					        guard let window else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // We always initialize our fullscreen style to native if we can because
 | 
				
			||||||
 | 
					        // initialization sets up some state (i.e. observers). If its set already
 | 
				
			||||||
 | 
					        // somehow we don't do this.
 | 
				
			||||||
 | 
					        if fullscreenStyle == nil {
 | 
				
			||||||
 | 
					            fullscreenStyle = NativeFullscreen(window)
 | 
				
			||||||
 | 
					            fullscreenStyle?.delegate = self
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // MARK: NSWindowDelegate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This is called when performClose is called on a window (NOT when close()
 | 
					    // This is called when performClose is called on a window (NOT when close()
 | 
				
			||||||
    // is called directly). performClose is called primarily when UI elements such
 | 
					    // is called directly). performClose is called primarily when UI elements such
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -275,6 +275,7 @@ class TerminalController: BaseTerminalController {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override func windowDidLoad() {
 | 
					    override func windowDidLoad() {
 | 
				
			||||||
 | 
					        super.windowDidLoad()
 | 
				
			||||||
        guard let window = window as? TerminalWindow else { return }
 | 
					        guard let window = window as? TerminalWindow else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // I copy this because we may change the source in the future but also because
 | 
					        // I copy this because we may change the source in the future but also because
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,20 +45,53 @@ extension FullscreenDelegate {
 | 
				
			|||||||
    func fullscreenDidChange() {}
 | 
					    func fullscreenDidChange() {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The base class for fullscreen implementations, cannot be used as a FullscreenStyle on its own.
 | 
				
			||||||
 | 
					class FullscreenBase {
 | 
				
			||||||
 | 
					    let window: NSWindow
 | 
				
			||||||
 | 
					    weak var delegate: FullscreenDelegate?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    required init?(_ window: NSWindow) {
 | 
				
			||||||
 | 
					        self.window = window
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // We want to trigger delegate methods on window native fullscreen
 | 
				
			||||||
 | 
					        // changes (didEnterFullScreenNotification, etc.) no matter what our
 | 
				
			||||||
 | 
					        // fullscreen style is.
 | 
				
			||||||
 | 
					        let center = NotificationCenter.default
 | 
				
			||||||
 | 
					        center.addObserver(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            selector: #selector(didEnterFullScreenNotification),
 | 
				
			||||||
 | 
					            name: NSWindow.didEnterFullScreenNotification,
 | 
				
			||||||
 | 
					            object: window)
 | 
				
			||||||
 | 
					        center.addObserver(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            selector: #selector(didExitFullScreenNotification),
 | 
				
			||||||
 | 
					            name: NSWindow.didExitFullScreenNotification,
 | 
				
			||||||
 | 
					            object: window)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    deinit {
 | 
				
			||||||
 | 
					        NotificationCenter.default.removeObserver(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @objc private func didEnterFullScreenNotification(_ notification: Notification) {
 | 
				
			||||||
 | 
					        delegate?.fullscreenDidChange()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @objc private func didExitFullScreenNotification(_ notification: Notification) {
 | 
				
			||||||
 | 
					        delegate?.fullscreenDidChange()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// macOS native fullscreen. This is the typical behavior you get by pressing the green fullscreen
 | 
					/// macOS native fullscreen. This is the typical behavior you get by pressing the green fullscreen
 | 
				
			||||||
/// button on regular titlebars.
 | 
					/// button on regular titlebars.
 | 
				
			||||||
class NativeFullscreen: FullscreenStyle {
 | 
					class NativeFullscreen: FullscreenBase, FullscreenStyle {
 | 
				
			||||||
    private let window: NSWindow
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    weak var delegate: FullscreenDelegate?
 | 
					 | 
				
			||||||
    var isFullscreen: Bool { window.styleMask.contains(.fullScreen) }
 | 
					    var isFullscreen: Bool { window.styleMask.contains(.fullScreen) }
 | 
				
			||||||
    var supportsTabs: Bool { true }
 | 
					    var supportsTabs: Bool { true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    required init?(_ window: NSWindow) {
 | 
					    required init?(_ window: NSWindow) {
 | 
				
			||||||
        // TODO: There are many requirements for native fullscreen we should
 | 
					        // TODO: There are many requirements for native fullscreen we should
 | 
				
			||||||
        // check here such as the stylemask.
 | 
					        // check here such as the stylemask.
 | 
				
			||||||
 | 
					        super.init(window)
 | 
				
			||||||
        self.window = window
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func enter() {
 | 
					    func enter() {
 | 
				
			||||||
@@ -72,8 +105,9 @@ class NativeFullscreen: FullscreenStyle {
 | 
				
			|||||||
        // Enter fullscreen
 | 
					        // Enter fullscreen
 | 
				
			||||||
        window.toggleFullScreen(self)
 | 
					        window.toggleFullScreen(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Notify the delegate
 | 
					        // Note: we don't call our delegate here because the base class
 | 
				
			||||||
        delegate?.fullscreenDidChange()
 | 
					        // will always trigger the delegate on native fullscreen notifications
 | 
				
			||||||
 | 
					        // and we don't want to double notify.
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func exit() {
 | 
					    func exit() {
 | 
				
			||||||
@@ -84,14 +118,13 @@ class NativeFullscreen: FullscreenStyle {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        window.toggleFullScreen(nil)
 | 
					        window.toggleFullScreen(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Notify the delegate
 | 
					        // Note: we don't call our delegate here because the base class
 | 
				
			||||||
        delegate?.fullscreenDidChange()
 | 
					        // will always trigger the delegate on native fullscreen notifications
 | 
				
			||||||
 | 
					        // and we don't want to double notify.
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NonNativeFullscreen: FullscreenStyle {
 | 
					class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
 | 
				
			||||||
    weak var delegate: FullscreenDelegate?
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Non-native fullscreen never supports tabs because tabs require
 | 
					    // Non-native fullscreen never supports tabs because tabs require
 | 
				
			||||||
    // the "titled" style and we don't have it for non-native fullscreen.
 | 
					    // the "titled" style and we don't have it for non-native fullscreen.
 | 
				
			||||||
    var supportsTabs: Bool { false }
 | 
					    var supportsTabs: Bool { false }
 | 
				
			||||||
@@ -110,13 +143,8 @@ class NonNativeFullscreen: FullscreenStyle {
 | 
				
			|||||||
        var hideMenu: Bool = true
 | 
					        var hideMenu: Bool = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private let window: NSWindow
 | 
					 | 
				
			||||||
    private var savedState: SavedState?
 | 
					    private var savedState: SavedState?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    required init?(_ window: NSWindow) {
 | 
					 | 
				
			||||||
        self.window = window
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    func enter() {
 | 
					    func enter() {
 | 
				
			||||||
        // If we are in fullscreen we don't do it again.
 | 
					        // If we are in fullscreen we don't do it again.
 | 
				
			||||||
        guard !isFullscreen else { return }
 | 
					        guard !isFullscreen else { return }
 | 
				
			||||||
@@ -187,8 +215,12 @@ class NonNativeFullscreen: FullscreenStyle {
 | 
				
			|||||||
        guard isFullscreen else { return }
 | 
					        guard isFullscreen else { return }
 | 
				
			||||||
        guard let savedState else { return }
 | 
					        guard let savedState else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Remove all our notifications
 | 
					        // Remove all our notifications. We remove them one by one because
 | 
				
			||||||
        NotificationCenter.default.removeObserver(self)
 | 
					        // we don't want to remove the observers that our superclass sets.
 | 
				
			||||||
 | 
					        let center = NotificationCenter.default
 | 
				
			||||||
 | 
					        center.removeObserver(self, name: NSWindow.didBecomeMainNotification, object: window)
 | 
				
			||||||
 | 
					        center.removeObserver(self, name: NSWindow.didResignMainNotification, object: window)
 | 
				
			||||||
 | 
					        center.removeObserver(self, name: NSWindow.didChangeScreenNotification, object: window)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Unhide our elements
 | 
					        // Unhide our elements
 | 
				
			||||||
        if savedState.dock {
 | 
					        if savedState.dock {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user