macos: new FullscreenMode/Style architecture for fullscreen impls

This commit is contained in:
Mitchell Hashimoto
2024-09-29 21:31:41 -07:00
parent ea46cb19e6
commit 332e1a1494
7 changed files with 141 additions and 18 deletions

View File

@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
55154BE02B33911F001622DC /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 55154BDF2B33911F001622DC /* ghostty */; }; 55154BE02B33911F001622DC /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 55154BDF2B33911F001622DC /* ghostty */; };
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; }; 552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */; }; 8503D7C72A549C66006CFF3D /* FullscreenHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8503D7C62A549C66006CFF3D /* FullscreenHandler.swift */; };
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; }; 857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; }; A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; }; A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
@ -22,6 +22,8 @@
A51BFC2B2B30F6BE00E92F16 /* UpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */; }; A51BFC2B2B30F6BE00E92F16 /* UpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */; };
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; }; A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; };
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */; }; A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */; };
A52FFF592CAA4FF3000C6A5B /* FullscreenHandler2.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52FFF582CAA4FF1000C6A5B /* FullscreenHandler2.swift */; };
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */; };
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */; }; A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */; };
A5333E1D2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */; }; A5333E1D2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */; };
A5333E202B5A2111008AEFF7 /* SurfaceView_UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E152B59DE8E008AEFF7 /* SurfaceView_UIKit.swift */; }; A5333E202B5A2111008AEFF7 /* SurfaceView_UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E152B59DE8E008AEFF7 /* SurfaceView_UIKit.swift */; };
@ -94,7 +96,7 @@
3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyReleaseLocal.entitlements; sourceTree = "<group>"; }; 3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyReleaseLocal.entitlements; sourceTree = "<group>"; };
55154BDF2B33911F001622DC /* ghostty */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ghostty; path = "../zig-out/share/ghostty"; sourceTree = "<group>"; }; 55154BDF2B33911F001622DC /* ghostty */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ghostty; path = "../zig-out/share/ghostty"; sourceTree = "<group>"; };
552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; }; 552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; };
8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenHandler.swift; sourceTree = "<group>"; }; 8503D7C62A549C66006CFF3D /* FullscreenHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenHandler.swift; sourceTree = "<group>"; };
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; }; 857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = "<group>"; }; A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = "<group>"; };
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; }; A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
@ -105,6 +107,8 @@
A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateDelegate.swift; sourceTree = "<group>"; }; A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateDelegate.swift; sourceTree = "<group>"; };
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; }; A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; };
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalScreen.swift; sourceTree = "<group>"; }; A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalScreen.swift; sourceTree = "<group>"; };
A52FFF582CAA4FF1000C6A5B /* FullscreenHandler2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenHandler2.swift; sourceTree = "<group>"; };
A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FullscreenMode+Extension.swift"; sourceTree = "<group>"; };
A5333E152B59DE8E008AEFF7 /* SurfaceView_UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_UIKit.swift; sourceTree = "<group>"; }; A5333E152B59DE8E008AEFF7 /* SurfaceView_UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_UIKit.swift; sourceTree = "<group>"; };
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossKit.swift; sourceTree = "<group>"; }; A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossKit.swift; sourceTree = "<group>"; };
A5333E212B5A2128008AEFF7 /* SurfaceView_AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_AppKit.swift; sourceTree = "<group>"; }; A5333E212B5A2128008AEFF7 /* SurfaceView_AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_AppKit.swift; sourceTree = "<group>"; };
@ -229,11 +233,12 @@
A534263D2A7DCBB000EBB7A2 /* Helpers */ = { A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A52FFF582CAA4FF1000C6A5B /* FullscreenHandler2.swift */,
A5CEAFFE29C2410700646FDA /* Backport.swift */, A5CEAFFE29C2410700646FDA /* Backport.swift */,
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */, A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
A5CBD0572C9F30860017A1AE /* Cursor.swift */, A5CBD0572C9F30860017A1AE /* Cursor.swift */,
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */, A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */, 8503D7C62A549C66006CFF3D /* FullscreenHandler.swift */,
A59630962AEE163600D64628 /* HostingWindow.swift */, A59630962AEE163600D64628 /* HostingWindow.swift */,
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */, A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */, A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
@ -314,6 +319,7 @@
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */, A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
A59630A12AF0415000D64628 /* Ghostty.TerminalSplit.swift */, A59630A12AF0415000D64628 /* Ghostty.TerminalSplit.swift */,
A55685DF29A03A9F004303CE /* AppError.swift */, A55685DF29A03A9F004303CE /* AppError.swift */,
A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */,
); );
path = Ghostty; path = Ghostty;
sourceTree = "<group>"; sourceTree = "<group>";
@ -575,6 +581,7 @@
AEE8B3452B9AA39600260C5E /* NSPasteboard+Extension.swift in Sources */, AEE8B3452B9AA39600260C5E /* NSPasteboard+Extension.swift in Sources */,
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */, A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */, A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */,
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */, A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */, A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */, A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
@ -597,8 +604,9 @@
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */, A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */,
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */, A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */,
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */, A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */,
8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */, 8503D7C72A549C66006CFF3D /* FullscreenHandler.swift in Sources */,
A596309E2AEE1D6C00D64628 /* TerminalView.swift in Sources */, A596309E2AEE1D6C00D64628 /* TerminalView.swift in Sources */,
A52FFF592CAA4FF3000C6A5B /* FullscreenHandler2.swift in Sources */,
AEF9CE242B6AD07A0017E195 /* TerminalToolbar.swift in Sources */, AEF9CE242B6AD07A0017E195 /* TerminalToolbar.swift in Sources */,
C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */, C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */,
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */, A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */,

View File

@ -9,7 +9,7 @@ class TerminalController: BaseTerminalController
override var windowNibName: NSNib.Name? { "Terminal" } override var windowNibName: NSNib.Name? { "Terminal" }
/// Fullscreen state management. /// Fullscreen state management.
let fullscreenHandler = FullScreenHandler() private(set) var fullscreenStyle: FullscreenStyle?
/// This is set to true when we care about frame changes. This is a small optimization since /// This is set to true when we care about frame changes. This is a small optimization since
/// this controller registers a listener for ALL frame change notifications and this lets us bail /// this controller registers a listener for ALL frame change notifications and this lets us bail
@ -534,12 +534,34 @@ class TerminalController: BaseTerminalController
// We need a window to fullscreen // We need a window to fullscreen
guard let window = self.window else { return } guard let window = self.window else { return }
// Check whether we use non-native fullscreen // Get the fullscreen mode we want to toggle
guard let fullscreenModeAny = notification.userInfo?[Ghostty.Notification.FullscreenModeKey] else { return } let fullscreenMode: FullscreenMode
guard let fullscreenMode = fullscreenModeAny as? ghostty_action_fullscreen_e else { return } if let any = notification.userInfo?[Ghostty.Notification.FullscreenModeKey],
self.fullscreenHandler.toggleFullscreen(window: window, mode: fullscreenMode) let mode = any as? FullscreenMode {
fullscreenMode = mode
} else {
Ghostty.logger.warning("no fullscreen mode specified or invalid mode, doing nothing")
return
}
// For some reason focus always gets lost when we toggle fullscreen, so we set it back. // TODO: handle changing fullscreen modes at runtime
// This is where we'd handle this.
// Initialize our style for the window. This may fail for various reasons so
// we also guard below.
if self.fullscreenStyle == nil {
self.fullscreenStyle = fullscreenMode.style(for: window)
}
guard let fullscreenStyle else { return }
if fullscreenStyle.isFullscreen {
fullscreenStyle.exit()
} else {
fullscreenStyle.enter()
}
// For some reason focus can get lost when we change fullscreen. Regardless of
// mode above we just move it back.
if let focusedSurface { if let focusedSurface {
Ghostty.moveFocus(to: focusedSurface) Ghostty.moveFocus(to: focusedSurface)
} }

View File

@ -114,7 +114,8 @@ class TerminalManager {
// If our parent is in non-native fullscreen, then new tabs do not work. // If our parent is in non-native fullscreen, then new tabs do not work.
// See: https://github.com/mitchellh/ghostty/issues/392 // See: https://github.com/mitchellh/ghostty/issues/392
if let controller = parent.windowController as? TerminalController, if let controller = parent.windowController as? TerminalController,
controller.fullscreenHandler.isInNonNativeFullscreen { let fullscreenStyle = controller.fullscreenStyle,
!fullscreenStyle.supportsTabs {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Cannot Create New Tab" alert.messageText = "Cannot Create New Tab"
alert.informativeText = "New tabs are unsupported while in non-native fullscreen. Exit fullscreen and try again." alert.informativeText = "New tabs are unsupported while in non-native fullscreen. Exit fullscreen and try again."

View File

@ -0,0 +1,20 @@
import GhosttyKit
extension FullscreenMode {
/// Initialize from a Ghostty fullscreen action.
static func from(ghostty: ghostty_action_fullscreen_e) -> Self? {
return switch ghostty {
case GHOSTTY_FULLSCREEN_NATIVE:
.native
case GHOSTTY_FULLSCREEN_NON_NATIVE:
.nonNative
case GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU:
.nonNativeVisibleMenu
default:
nil
}
}
}

View File

@ -598,7 +598,7 @@ extension Ghostty {
private static func toggleFullscreen( private static func toggleFullscreen(
_ app: ghostty_app_t, _ app: ghostty_app_t,
target: ghostty_target_s, target: ghostty_target_s,
mode: ghostty_action_fullscreen_e) { mode raw: ghostty_action_fullscreen_e) {
switch (target.tag) { switch (target.tag) {
case GHOSTTY_TARGET_APP: case GHOSTTY_TARGET_APP:
Ghostty.logger.warning("toggle fullscreen does nothing with an app target") Ghostty.logger.warning("toggle fullscreen does nothing with an app target")
@ -607,6 +607,10 @@ extension Ghostty {
case GHOSTTY_TARGET_SURFACE: case GHOSTTY_TARGET_SURFACE:
guard let surface = target.target.surface else { return } guard let surface = target.target.surface else { return }
guard let surfaceView = self.surfaceView(from: surface) else { return } guard let surfaceView = self.surfaceView(from: surface) else { return }
guard let mode = FullscreenMode.from(ghostty: raw) else {
Ghostty.logger.warning("unknow fullscreen mode raw=\(raw.rawValue)")
return
}
NotificationCenter.default.post( NotificationCenter.default.post(
name: Notification.ghosttyToggleFullscreen, name: Notification.ghosttyToggleFullscreen,
object: surfaceView, object: surfaceView,

View File

@ -1,7 +1,7 @@
import SwiftUI import SwiftUI
import GhosttyKit import GhosttyKit
class FullScreenHandler { class FullscreenHandler {
var previousTabGroup: NSWindowTabGroup? var previousTabGroup: NSWindowTabGroup?
var previousTabGroupIndex: Int? var previousTabGroupIndex: Int?
var previousContentFrame: NSRect? var previousContentFrame: NSRect?
@ -24,7 +24,7 @@ class FullScreenHandler {
default: default:
false false
} }
if isInFullscreen { if isInFullscreen {
if useNonNativeFullscreen || isInNonNativeFullscreen { if useNonNativeFullscreen || isInNonNativeFullscreen {
leaveFullscreen(window: window) leaveFullscreen(window: window)
@ -75,12 +75,12 @@ class FullScreenHandler {
// Ensure that we always hide the dock bar for this window, but not for non fullscreen ones // Ensure that we always hide the dock bar for this window, but not for non fullscreen ones
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(FullScreenHandler.hideDock), selector: #selector(hideDock),
name: NSWindow.didBecomeMainNotification, name: NSWindow.didBecomeMainNotification,
object: window) object: window)
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(FullScreenHandler.unHideDock), selector: #selector(unHideDock),
name: NSWindow.didResignMainNotification, name: NSWindow.didResignMainNotification,
object: window) object: window)
} }
@ -94,12 +94,12 @@ class FullScreenHandler {
// handling this there. // handling this there.
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(FullScreenHandler.hideMenu), selector: #selector(Self.hideMenu),
name: NSWindow.didBecomeMainNotification, name: NSWindow.didBecomeMainNotification,
object: window) object: window)
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(FullScreenHandler.onDidResignMain), selector: #selector(onDidResignMain),
name: NSWindow.didResignMainNotification, name: NSWindow.didResignMainNotification,
object: window) object: window)
} }

View File

@ -0,0 +1,68 @@
import Cocoa
import GhosttyKit
/// The fullscreen modes we support define how the fullscreen behaves.
enum FullscreenMode {
case native
case nonNative
case nonNativeVisibleMenu
/// Initializes the fullscreen style implementation for the mode. This will not toggle any
/// fullscreen properties. This may fail if the window isn't configured properly for a given
/// mode.
func style(for window: NSWindow) -> FullscreenStyle? {
switch self {
case .native:
return NativeFullscreen(window)
case .nonNative, .nonNativeVisibleMenu:
return nil
}
}
}
/// Protocol that must be implemented by all fullscreen styles.
protocol FullscreenStyle {
var isFullscreen: Bool { get }
var supportsTabs: Bool { get }
init?(_ window: NSWindow)
func enter()
func exit()
}
/// macOS native fullscreen. This is the typical behavior you get by pressing the green fullscreen
/// button on regular titlebars.
class NativeFullscreen: FullscreenStyle {
private let window: NSWindow
var isFullscreen: Bool { window.styleMask.contains(.fullScreen) }
var supportsTabs: Bool { true }
required init?(_ window: NSWindow) {
// TODO: There are many requirements for native fullscreen we should
// check here such as the stylemask.
self.window = window
}
func enter() {
guard !isFullscreen else { return }
// The titlebar separator shows up erroneously in fullscreen if the tab bar
// is made to appear and then disappear by opening and then closing a tab.
// We get rid of the separator while in fullscreen to prevent this.
window.titlebarSeparatorStyle = .none
// Enter fullscreen
window.toggleFullScreen(self)
}
func exit() {
guard isFullscreen else { return }
// Restore titlebar separator style. See enter for explanation.
window.titlebarSeparatorStyle = .automatic
window.toggleFullScreen(nil)
}
}