mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
wip: redo terminal window styling
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
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 */; };
|
||||
A514C8D82B54DC6800493A16 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
||||
A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */; };
|
||||
A51B78472AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift */; };
|
||||
A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51BFC1D2B2FB5CE00E92F16 /* About.xib */; };
|
||||
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */; };
|
||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC212B2FB6B400E92F16 /* AboutView.swift */; };
|
||||
@@ -51,6 +51,12 @@
|
||||
A54B0CEF2D0D2E2800CBEFF8 /* ColorizedGhosttyIconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CEE2D0D2E2400CBEFF8 /* ColorizedGhosttyIconImage.swift */; };
|
||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */; };
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55685DF29A03A9F004303CE /* AppError.swift */; };
|
||||
A5593FDF2DF8D57C00B47B10 /* TerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5593FDE2DF8D57100B47B10 /* TerminalWindow.swift */; };
|
||||
A5593FE12DF8D74000B47B10 /* HiddenTitlebarTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5593FE02DF8D73400B47B10 /* HiddenTitlebarTerminalWindow.swift */; };
|
||||
A5593FE32DF8D78600B47B10 /* TerminalHiddenTitlebar.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5593FE22DF8D78600B47B10 /* TerminalHiddenTitlebar.xib */; };
|
||||
A5593FE52DF8DE3000B47B10 /* TerminalLegacy.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5593FE42DF8DE3000B47B10 /* TerminalLegacy.xib */; };
|
||||
A5593FE72DF927D200B47B10 /* TransparentTitlebarTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5593FE62DF927CC00B47B10 /* TransparentTitlebarTerminalWindow.swift */; };
|
||||
A5593FE92DF927DF00B47B10 /* TerminalTransparentTitlebar.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5593FE82DF927DF00B47B10 /* TerminalTransparentTitlebar.xib */; };
|
||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */; };
|
||||
A56B880B2A840447007A0E29 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A56B880A2A840447007A0E29 /* Carbon.framework */; };
|
||||
@@ -129,7 +135,7 @@
|
||||
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; 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 /* LegacyTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTerminalWindow.swift; sourceTree = "<group>"; };
|
||||
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
|
||||
A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; };
|
||||
A51BFC212B2FB6B400E92F16 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
@@ -159,6 +165,12 @@
|
||||
A54B0CEE2D0D2E2400CBEFF8 /* ColorizedGhosttyIconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorizedGhosttyIconImage.swift; sourceTree = "<group>"; };
|
||||
A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTerminalController.swift; sourceTree = "<group>"; };
|
||||
A55685DF29A03A9F004303CE /* AppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppError.swift; sourceTree = "<group>"; };
|
||||
A5593FDE2DF8D57100B47B10 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
|
||||
A5593FE02DF8D73400B47B10 /* HiddenTitlebarTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenTitlebarTerminalWindow.swift; sourceTree = "<group>"; };
|
||||
A5593FE22DF8D78600B47B10 /* TerminalHiddenTitlebar.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalHiddenTitlebar.xib; sourceTree = "<group>"; };
|
||||
A5593FE42DF8DE3000B47B10 /* TerminalLegacy.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalLegacy.xib; sourceTree = "<group>"; };
|
||||
A5593FE62DF927CC00B47B10 /* TransparentTitlebarTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparentTitlebarTerminalWindow.swift; sourceTree = "<group>"; };
|
||||
A5593FE82DF927DF00B47B10 /* TerminalTransparentTitlebar.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalTransparentTitlebar.xib; sourceTree = "<group>"; };
|
||||
A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView.swift; sourceTree = "<group>"; };
|
||||
A56B880A2A840447007A0E29 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||
@@ -384,6 +396,21 @@
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A5593FDD2DF8D56000B47B10 /* Window Styles */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A59630992AEE1C6400D64628 /* Terminal.xib */,
|
||||
A5593FDE2DF8D57100B47B10 /* TerminalWindow.swift */,
|
||||
A5593FE22DF8D78600B47B10 /* TerminalHiddenTitlebar.xib */,
|
||||
A5593FE02DF8D73400B47B10 /* HiddenTitlebarTerminalWindow.swift */,
|
||||
A5593FE42DF8DE3000B47B10 /* TerminalLegacy.xib */,
|
||||
A51B78462AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift */,
|
||||
A5593FE82DF927DF00B47B10 /* TerminalTransparentTitlebar.xib */,
|
||||
A5593FE62DF927CC00B47B10 /* TransparentTitlebarTerminalWindow.swift */,
|
||||
);
|
||||
path = "Window Styles";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A55B7BB429B6F4410055DE60 /* Ghostty */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -467,11 +494,10 @@
|
||||
A59630982AEE1C4400D64628 /* Terminal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A59630992AEE1C6400D64628 /* Terminal.xib */,
|
||||
A5593FDD2DF8D56000B47B10 /* Window Styles */,
|
||||
A596309B2AEE1C9E00D64628 /* TerminalController.swift */,
|
||||
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */,
|
||||
A596309D2AEE1D6C00D64628 /* TerminalView.swift */,
|
||||
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */,
|
||||
AEF9CE232B6AD07A0017E195 /* TerminalToolbar.swift */,
|
||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */,
|
||||
A54D786B2CA79788001B19B1 /* BaseTerminalController.swift */,
|
||||
@@ -647,9 +673,11 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */,
|
||||
A5593FE52DF8DE3000B47B10 /* TerminalLegacy.xib in Resources */,
|
||||
29C15B1D2CDC3B2900520DD4 /* bat in Resources */,
|
||||
A586167C2B7703CC009BDB1D /* fish in Resources */,
|
||||
55154BE02B33911F001622DC /* ghostty in Resources */,
|
||||
A5593FE32DF8D78600B47B10 /* TerminalHiddenTitlebar.xib in Resources */,
|
||||
A546F1142D7B68D7003B11A0 /* locale in Resources */,
|
||||
A5985CE62C33060F00C57AD3 /* man in Resources */,
|
||||
9351BE8E3D22937F003B3499 /* nvim in Resources */,
|
||||
@@ -658,6 +686,7 @@
|
||||
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */,
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */,
|
||||
A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */,
|
||||
A5593FE92DF927DF00B47B10 /* TerminalTransparentTitlebar.xib in Resources */,
|
||||
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */,
|
||||
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */,
|
||||
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */,
|
||||
@@ -702,6 +731,7 @@
|
||||
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
||||
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
||||
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
||||
A5593FE72DF927D200B47B10 /* TransparentTitlebarTerminalWindow.swift in Sources */,
|
||||
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */,
|
||||
A586365F2DEE6C2300E04A10 /* SplitTree.swift in Sources */,
|
||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
||||
@@ -709,6 +739,7 @@
|
||||
A53A29812DB44A6100B6E02C /* KeyboardShortcut+Extension.swift in Sources */,
|
||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
|
||||
C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */,
|
||||
A5593FDF2DF8D57C00B47B10 /* TerminalWindow.swift in Sources */,
|
||||
A58636712DF298FB00E04A10 /* ExpiringUndoManager.swift in Sources */,
|
||||
A59630972AEE163600D64628 /* HostingWindow.swift in Sources */,
|
||||
A51BFC2B2B30F6BE00E92F16 /* UpdateDelegate.swift in Sources */,
|
||||
@@ -734,9 +765,10 @@
|
||||
A53A297F2DB4480F00B6E02C /* EventModifiers+Extension.swift in Sources */,
|
||||
A53A297B2DB2E49700B6E02C /* CommandPalette.swift in Sources */,
|
||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||
A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */,
|
||||
A51B78472AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift in Sources */,
|
||||
A57D79272C9C879B001D522E /* SecureInput.swift in Sources */,
|
||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||
A5593FE12DF8D74000B47B10 /* HiddenTitlebarTerminalWindow.swift in Sources */,
|
||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
||||
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */,
|
||||
|
@@ -6,7 +6,22 @@ import GhosttyKit
|
||||
|
||||
/// A classic, tabbed terminal experience.
|
||||
class TerminalController: BaseTerminalController {
|
||||
override var windowNibName: NSNib.Name? { "Terminal" }
|
||||
override var windowNibName: NSNib.Name? {
|
||||
//NOTE(mitchellh): switch to this when we've transitioned all legacy logic out
|
||||
//let defaultValue = "Terminal"
|
||||
let defaultValue = "TerminalLegacy"
|
||||
|
||||
guard let appDelegate = NSApp.delegate as? AppDelegate else { return defaultValue }
|
||||
let config = appDelegate.ghostty.config
|
||||
let nib = switch config.macosTitlebarStyle {
|
||||
case "tabs": defaultValue
|
||||
case "hidden": "TerminalHiddenTitlebar"
|
||||
case "transparent": "TerminalTransparentTitlebar"
|
||||
default: defaultValue
|
||||
}
|
||||
|
||||
return nib
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -114,7 +129,7 @@ class TerminalController: BaseTerminalController {
|
||||
invalidateRestorableState()
|
||||
|
||||
// Update our zoom state
|
||||
if let window = window as? TerminalWindow {
|
||||
if let window = window as? LegacyTerminalWindow {
|
||||
window.surfaceIsZoomed = to.zoomed != nil
|
||||
}
|
||||
|
||||
@@ -129,11 +144,6 @@ class TerminalController: BaseTerminalController {
|
||||
// When our fullscreen state changes, we resync our appearance because some
|
||||
// properties change when fullscreen or not.
|
||||
guard let focusedSurface else { return }
|
||||
if (!(fullscreenStyle?.isFullscreen ?? false) &&
|
||||
ghostty.config.macosTitlebarStyle == "hidden")
|
||||
{
|
||||
applyHiddenTitlebarStyle()
|
||||
}
|
||||
|
||||
syncAppearance(focusedSurface.derivedConfig)
|
||||
}
|
||||
@@ -278,9 +288,8 @@ class TerminalController: BaseTerminalController {
|
||||
tg.removeWindow(window)
|
||||
}
|
||||
|
||||
// If we have the "hidden" titlebar style we want to create new
|
||||
// tabs as windows instead, so just skip adding it to the parent.
|
||||
if (ghostty.config.macosTitlebarStyle != "hidden") {
|
||||
// If we don't allow tabs then we create a new window instead.
|
||||
if (window.tabbingMode != .disallowed) {
|
||||
// Add the window to the tab group and show it.
|
||||
switch ghostty.config.windowNewTabPosition {
|
||||
case "end":
|
||||
@@ -389,7 +398,7 @@ class TerminalController: BaseTerminalController {
|
||||
// Reset this to false. It'll be set back to true later.
|
||||
tabListenForFrame = false
|
||||
|
||||
guard let windows = self.window?.tabbedWindows as? [TerminalWindow] else { return }
|
||||
guard let windows = self.window?.tabbedWindows as? [LegacyTerminalWindow] else { return }
|
||||
|
||||
// We only listen for frame changes if we have more than 1 window,
|
||||
// otherwise the accessory view doesn't matter.
|
||||
@@ -440,7 +449,11 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
private func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {
|
||||
guard let window = self.window as? TerminalWindow else { return }
|
||||
if let window = window as? TerminalWindow {
|
||||
window.syncAppearance(surfaceConfig)
|
||||
}
|
||||
|
||||
guard let window = self.window as? LegacyTerminalWindow else { return }
|
||||
|
||||
// Set our explicit appearance if we need to based on the configuration.
|
||||
window.appearance = surfaceConfig.windowAppearance
|
||||
@@ -523,31 +536,6 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
}
|
||||
|
||||
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
|
||||
guard let window else { return }
|
||||
|
||||
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||
guard let x, let y else {
|
||||
if (!LastWindowPosition.shared.restore(window)) {
|
||||
window.center()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Prefer the screen our window is being placed on otherwise our primary screen.
|
||||
guard let screen = window.screen ?? NSScreen.screens.first else {
|
||||
window.center()
|
||||
return
|
||||
}
|
||||
|
||||
// Orient based on the top left of the primary monitor
|
||||
let frame = screen.visibleFrame
|
||||
window.setFrameOrigin(.init(
|
||||
x: frame.minX + CGFloat(x),
|
||||
y: frame.maxY - (CGFloat(y) + window.frame.height)))
|
||||
}
|
||||
|
||||
/// Returns the default size of the window. This is contextual based on the focused surface because
|
||||
/// the focused surface may specify a different default size than others.
|
||||
private var defaultSize: NSRect? {
|
||||
@@ -889,52 +877,9 @@ class TerminalController: BaseTerminalController {
|
||||
shouldCascadeWindows = false
|
||||
}
|
||||
|
||||
fileprivate func hideWindowButtons() {
|
||||
guard let window else { return }
|
||||
|
||||
window.standardWindowButton(.closeButton)?.isHidden = true
|
||||
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||
window.standardWindowButton(.zoomButton)?.isHidden = true
|
||||
}
|
||||
|
||||
fileprivate func applyHiddenTitlebarStyle() {
|
||||
guard let window else { return }
|
||||
|
||||
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)
|
||||
hideWindowButtons()
|
||||
|
||||
// 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 }
|
||||
guard let window else { return }
|
||||
|
||||
// Store our initial frame so we can know our default later.
|
||||
initialFrame = window.frame
|
||||
@@ -952,9 +897,6 @@ class TerminalController: BaseTerminalController {
|
||||
window.identifier = .init(String(describing: TerminalWindowRestoration.self))
|
||||
}
|
||||
|
||||
// If window decorations are disabled, remove our title
|
||||
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||
|
||||
// If we have only a single surface (no splits) and there is a default size then
|
||||
// we should resize to that default size.
|
||||
if case let .leaf(view) = surfaceTree.root {
|
||||
@@ -967,42 +909,29 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
}
|
||||
|
||||
// Set our window positioning to coordinates if config value exists, otherwise
|
||||
// fallback to original centering behavior
|
||||
setInitialWindowPosition(
|
||||
x: config.windowPositionX,
|
||||
y: config.windowPositionY,
|
||||
windowDecorations: config.windowDecorations)
|
||||
|
||||
if config.macosWindowButtons == .hidden {
|
||||
hideWindowButtons()
|
||||
}
|
||||
|
||||
// Make sure our theme is set on the window so styling is correct.
|
||||
if let windowTheme = config.windowTheme {
|
||||
window.windowTheme = .init(rawValue: windowTheme)
|
||||
}
|
||||
|
||||
// Handle titlebar tabs config option. Something about what we do while setting up the
|
||||
// titlebar tabs interferes with the window restore process unless window.tabbingMode
|
||||
// is set to .preferred, so we set it, and switch back to automatic as soon as we can.
|
||||
if (config.macosTitlebarStyle == "tabs") {
|
||||
window.tabbingMode = .preferred
|
||||
window.titlebarTabs = true
|
||||
DispatchQueue.main.async {
|
||||
window.tabbingMode = .automatic
|
||||
// TODO: remove
|
||||
if let window = window as? LegacyTerminalWindow {
|
||||
// Handle titlebar tabs config option. Something about what we do while setting up the
|
||||
// titlebar tabs interferes with the window restore process unless window.tabbingMode
|
||||
// is set to .preferred, so we set it, and switch back to automatic as soon as we can.
|
||||
if (config.macosTitlebarStyle == "tabs") {
|
||||
window.tabbingMode = .preferred
|
||||
window.titlebarTabs = true
|
||||
DispatchQueue.main.async {
|
||||
window.tabbingMode = .automatic
|
||||
}
|
||||
} else if (config.macosTitlebarStyle == "transparent") {
|
||||
window.transparentTabs = true
|
||||
}
|
||||
} else if (config.macosTitlebarStyle == "transparent") {
|
||||
window.transparentTabs = true
|
||||
}
|
||||
|
||||
if window.hasStyledTabs {
|
||||
// Set the background color of the window
|
||||
let backgroundColor = NSColor(config.backgroundColor)
|
||||
window.backgroundColor = backgroundColor
|
||||
if window.hasStyledTabs {
|
||||
// Set the background color of the window
|
||||
let backgroundColor = NSColor(config.backgroundColor)
|
||||
window.backgroundColor = backgroundColor
|
||||
|
||||
// This makes sure our titlebar renders correctly when there is a transparent background
|
||||
window.titlebarColor = backgroundColor.withAlphaComponent(config.backgroundOpacity)
|
||||
// This makes sure our titlebar renders correctly when there is a transparent background
|
||||
window.titlebarColor = backgroundColor.withAlphaComponent(config.backgroundOpacity)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize our content view to the SwiftUI root
|
||||
@@ -1012,11 +941,6 @@ class TerminalController: BaseTerminalController {
|
||||
delegate: self
|
||||
))
|
||||
|
||||
// If our titlebar style is "hidden" we adjust the style appropriately
|
||||
if (config.macosTitlebarStyle == "hidden") {
|
||||
applyHiddenTitlebarStyle()
|
||||
}
|
||||
|
||||
// In various situations, macOS automatically tabs new windows. Ghostty handles
|
||||
// its own tabbing so we DONT want this behavior. This detects this scenario and undoes
|
||||
// it.
|
||||
@@ -1218,7 +1142,7 @@ class TerminalController: BaseTerminalController {
|
||||
override func titleDidChange(to: String) {
|
||||
super.titleDidChange(to: to)
|
||||
|
||||
guard let window = window as? TerminalWindow else { return }
|
||||
guard let window = window as? LegacyTerminalWindow else { return }
|
||||
|
||||
// Custom toolbar-based title used when titlebar tabs are enabled.
|
||||
if let toolbar = window.toolbar as? TerminalToolbar {
|
||||
|
@@ -0,0 +1,78 @@
|
||||
import AppKit
|
||||
|
||||
class HiddenTitlebarTerminalWindow: TerminalWindow {
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
// Setup our initial style
|
||||
reapplyHiddenStyle()
|
||||
|
||||
// Notifications
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(fullscreenDidExit(_:)),
|
||||
name: .fullscreenDidExit,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
// We override this so that with the hidden titlebar style the titlebar
|
||||
// area is not draggable.
|
||||
override var contentLayoutRect: CGRect {
|
||||
var rect = super.contentLayoutRect
|
||||
rect.origin.y = 0
|
||||
rect.size.height = self.frame.height
|
||||
return rect
|
||||
}
|
||||
|
||||
/// Apply the hidden titlebar style.
|
||||
private func reapplyHiddenStyle() {
|
||||
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
|
||||
titleVisibility = .hidden
|
||||
titlebarAppearsTransparent = true
|
||||
|
||||
// Hide the traffic lights (window control buttons)
|
||||
standardWindowButton(.closeButton)?.isHidden = true
|
||||
standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||
standardWindowButton(.zoomButton)?.isHidden = true
|
||||
|
||||
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
||||
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 = contentView?.superview,
|
||||
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
|
||||
titleBarContainer.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc private func fullscreenDidExit(_ notification: Notification) {
|
||||
// Make sure they're talking about our window
|
||||
guard let fullscreen = notification.object as? FullscreenBase else { return }
|
||||
guard fullscreen.window == self else { return }
|
||||
|
||||
// On exit we need to reapply the style because macOS breaks it usually.
|
||||
// This is safe to call repeatedly so if its not broken its still safe.
|
||||
reapplyHiddenStyle()
|
||||
}
|
||||
}
|
@@ -1,9 +1,8 @@
|
||||
import Cocoa
|
||||
|
||||
class TerminalWindow: NSWindow {
|
||||
/// This is the key in UserDefaults to use for the default `level` value.
|
||||
static let defaultLevelKey: String = "TerminalDefaultLevel"
|
||||
|
||||
/// The terminal window that we originally had in Ghostty for a long time. Kind of a soupy mess
|
||||
/// of styling.
|
||||
class LegacyTerminalWindow: TerminalWindow {
|
||||
@objc dynamic var keyEquivalent: String = ""
|
||||
|
||||
/// This is used to determine if certain elements should be drawn light or dark and should
|
||||
@@ -56,11 +55,6 @@ class TerminalWindow: NSWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// Both of these must be true for windows without decorations to be able to
|
||||
// still become key/main and receive events.
|
||||
override var canBecomeKey: Bool { return true }
|
||||
override var canBecomeMain: Bool { return true }
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
@@ -77,8 +71,6 @@ class TerminalWindow: NSWindow {
|
||||
if titlebarTabs {
|
||||
generateToolbar()
|
||||
}
|
||||
|
||||
level = UserDefaults.standard.value(forKey: Self.defaultLevelKey) as? NSWindow.Level ?? .normal
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -135,25 +127,6 @@ class TerminalWindow: NSWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// We override this so that with the hidden titlebar style the titlebar
|
||||
// area is not draggable.
|
||||
override var contentLayoutRect: CGRect {
|
||||
var rect = super.contentLayoutRect
|
||||
|
||||
// If we are using a hidden titlebar style, the content layout is the
|
||||
// full frame making it so that it is not draggable.
|
||||
if let controller = windowController as? TerminalController,
|
||||
controller.derivedConfig.macosTitlebarStyle == "hidden" {
|
||||
rect.origin.y = 0
|
||||
rect.size.height = self.frame.height
|
||||
}
|
||||
return rect
|
||||
}
|
||||
|
||||
// The window theme configuration from Ghostty. This is used to control some
|
||||
// behaviors that don't look quite right in certain situations.
|
||||
var windowTheme: TerminalWindowTheme?
|
||||
|
||||
// We only need to set this once, but need to do it after the window has been created in order
|
||||
// to determine if the theme is using a very dark background, in which case we don't want to
|
||||
// remove the effect view if the default tab bar is being used since the effect created in
|
||||
@@ -703,7 +676,7 @@ fileprivate class WindowDragView: NSView {
|
||||
fileprivate class WindowButtonsBackdropView: NSView {
|
||||
// This must be weak because the window has this view. Otherwise
|
||||
// a retain cycle occurs.
|
||||
private weak var terminalWindow: TerminalWindow?
|
||||
private weak var terminalWindow: LegacyTerminalWindow?
|
||||
private let isLightTheme: Bool
|
||||
private let overlayLayer = VibrantLayer()
|
||||
|
||||
@@ -731,7 +704,7 @@ fileprivate class WindowButtonsBackdropView: NSView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
init(window: TerminalWindow) {
|
||||
init(window: LegacyTerminalWindow) {
|
||||
self.terminalWindow = window
|
||||
self.isLightTheme = window.isLightTheme
|
||||
|
||||
@@ -746,10 +719,3 @@ fileprivate class WindowButtonsBackdropView: NSView {
|
||||
layer?.addSublayer(overlayLayer)
|
||||
}
|
||||
}
|
||||
|
||||
enum TerminalWindowTheme: String {
|
||||
case auto
|
||||
case system
|
||||
case light
|
||||
case dark
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -17,10 +17,10 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1667"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1661"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="tG2-b7-nb8"/>
|
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="TerminalController" customModule="Ghostty" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="cg9-Ep-qHg"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="👻 Ghostty" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="HiddenTitlebarTerminalWindow" customModule="Ghostty" customModuleProvider="target">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1661"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="tG2-b7-nb8"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="132" y="-82"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="TerminalController" customModule="Ghostty" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="cg9-Ep-qHg"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="👻 Ghostty" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="LegacyTerminalWindow" customModule="Ghostty" customModuleProvider="target">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1661"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="tG2-b7-nb8"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="132" y="-82"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24093.7"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="TerminalController" customModule="Ghostty" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="cg9-Ep-qHg"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="👻 Ghostty" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="TransparentTitlebarTerminalWindow" customModule="Ghostty" customModuleProvider="target">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1512" height="948"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="tG2-b7-nb8"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="132" y="-82"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
@@ -0,0 +1,75 @@
|
||||
import AppKit
|
||||
|
||||
/// The base class for all standalone, "normal" terminal windows. This sets the basic
|
||||
/// style and configuration of the window based on the app configuration.
|
||||
class TerminalWindow: NSWindow {
|
||||
/// This is the key in UserDefaults to use for the default `level` value. This is
|
||||
/// used by the manual float on top menu item feature.
|
||||
static let defaultLevelKey: String = "TerminalDefaultLevel"
|
||||
|
||||
// MARK: NSWindow Overrides
|
||||
|
||||
override func awakeFromNib() {
|
||||
guard let appDelegate = NSApp.delegate as? AppDelegate else { return }
|
||||
|
||||
// All new windows are based on the app config at the time of creation.
|
||||
let config = appDelegate.ghostty.config
|
||||
|
||||
// If window decorations are disabled, remove our title
|
||||
if (!config.windowDecorations) { styleMask.remove(.titled) }
|
||||
|
||||
// Set our window positioning to coordinates if config value exists, otherwise
|
||||
// fallback to original centering behavior
|
||||
setInitialWindowPosition(
|
||||
x: config.windowPositionX,
|
||||
y: config.windowPositionY,
|
||||
windowDecorations: config.windowDecorations)
|
||||
|
||||
// If our traffic buttons should be hidden, then hide them
|
||||
if config.macosWindowButtons == .hidden {
|
||||
hideWindowButtons()
|
||||
}
|
||||
|
||||
// Get our saved level
|
||||
level = UserDefaults.standard.value(forKey: Self.defaultLevelKey) as? NSWindow.Level ?? .normal
|
||||
}
|
||||
|
||||
// Both of these must be true for windows without decorations to be able to
|
||||
// still become key/main and receive events.
|
||||
override var canBecomeKey: Bool { return true }
|
||||
override var canBecomeMain: Bool { return true }
|
||||
|
||||
// MARK: Positioning And Styling
|
||||
|
||||
/// This is called by the controller when there is a need to reset the window apperance.
|
||||
func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {}
|
||||
|
||||
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
|
||||
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||
guard let x, let y else {
|
||||
if (!LastWindowPosition.shared.restore(self)) {
|
||||
center()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Prefer the screen our window is being placed on otherwise our primary screen.
|
||||
guard let screen = screen ?? NSScreen.screens.first else {
|
||||
center()
|
||||
return
|
||||
}
|
||||
|
||||
// Orient based on the top left of the primary monitor
|
||||
let frame = screen.visibleFrame
|
||||
setFrameOrigin(.init(
|
||||
x: frame.minX + CGFloat(x),
|
||||
y: frame.maxY - (CGFloat(y) + frame.height)))
|
||||
}
|
||||
|
||||
private func hideWindowButtons() {
|
||||
standardWindowButton(.closeButton)?.isHidden = true
|
||||
standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||
standardWindowButton(.zoomButton)?.isHidden = true
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
import AppKit
|
||||
|
||||
class TransparentTitlebarTerminalWindow: TerminalWindow {
|
||||
private var reapplyTimer: Timer?
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
deinit {
|
||||
reapplyTimer?.invalidate()
|
||||
}
|
||||
|
||||
override func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {
|
||||
if #available(macOS 26.0, *) {
|
||||
syncAppearanceTahoe(surfaceConfig)
|
||||
} else {
|
||||
syncAppearanceVentura(surfaceConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 26.0, *)
|
||||
private func syncAppearanceTahoe(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {
|
||||
guard let titlebarBackgroundView else { return }
|
||||
titlebarBackgroundView.isHidden = true
|
||||
backgroundColor = NSColor(surfaceConfig.backgroundColor)
|
||||
}
|
||||
|
||||
@available(macOS 13.0, *)
|
||||
private func syncAppearanceVentura(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {
|
||||
guard let titlebarContainer else { return }
|
||||
|
||||
let configBgColor = NSColor(surfaceConfig.backgroundColor)
|
||||
|
||||
// Set our window background color so it shows up
|
||||
backgroundColor = configBgColor
|
||||
|
||||
// Set the background color of our titlebar to match
|
||||
titlebarContainer.wantsLayer = true
|
||||
titlebarContainer.layer?.backgroundColor = configBgColor.withAlphaComponent(surfaceConfig.backgroundOpacity).cgColor
|
||||
}
|
||||
|
||||
private var titlebarBackgroundView: NSView? {
|
||||
titlebarContainer?.firstDescendant(withClassName: "NSTitlebarBackgroundView")
|
||||
}
|
||||
|
||||
private var titlebarContainer: NSView? {
|
||||
// If we aren't fullscreen then the titlebar container is part of our window.
|
||||
if !styleMask.contains(.fullScreen) {
|
||||
return titlebarContainerView
|
||||
}
|
||||
|
||||
// If we are fullscreen, the titlebar container view is part of a separate
|
||||
// "fullscreen window", we need to find the window and then get the view.
|
||||
for window in NSApplication.shared.windows {
|
||||
// This is the private window class that contains the toolbar
|
||||
guard window.className == "NSToolbarFullScreenWindow" else { continue }
|
||||
|
||||
// The parent will match our window. This is used to filter the correct
|
||||
// fullscreen window if we have multiple.
|
||||
guard window.parent == self else { continue }
|
||||
|
||||
return titlebarContainerView
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private var titlebarContainerView: NSView? {
|
||||
contentView?.firstViewFromRoot(withClassName: "NSTitlebarContainerView")
|
||||
}
|
||||
}
|
@@ -13,6 +13,19 @@ extension NSView {
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: View Traversal and Search
|
||||
|
||||
extension NSView {
|
||||
/// Returns the absolute root view by walking up the superview chain.
|
||||
var rootView: NSView {
|
||||
var root: NSView = self
|
||||
while let superview = root.superview {
|
||||
root = superview
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
/// Recursively finds and returns the first descendant view that has the given class name.
|
||||
func firstDescendant(withClassName name: String) -> NSView? {
|
||||
@@ -54,4 +67,18 @@ extension NSView {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Finds and returns the first view with the given class name starting from the absolute root of the view hierarchy.
|
||||
/// This includes private views like title bar views.
|
||||
func firstViewFromRoot(withClassName name: String) -> NSView? {
|
||||
let root = rootView
|
||||
|
||||
// Check if the root view itself matches
|
||||
if String(describing: type(of: root)) == name {
|
||||
return root
|
||||
}
|
||||
|
||||
// Otherwise search descendants
|
||||
return root.firstDescendant(withClassName: name)
|
||||
}
|
||||
}
|
||||
|
@@ -78,10 +78,12 @@ class FullscreenBase {
|
||||
}
|
||||
|
||||
@objc private func didEnterFullScreenNotification(_ notification: Notification) {
|
||||
NotificationCenter.default.post(name: .fullscreenDidEnter, object: self)
|
||||
delegate?.fullscreenDidChange()
|
||||
}
|
||||
|
||||
@objc private func didExitFullScreenNotification(_ notification: Notification) {
|
||||
NotificationCenter.default.post(name: .fullscreenDidExit, object: self)
|
||||
delegate?.fullscreenDidChange()
|
||||
}
|
||||
}
|
||||
@@ -238,6 +240,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
self.window.makeFirstResponder(firstResponder)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .fullscreenDidEnter, object: self)
|
||||
self.delegate?.fullscreenDidChange()
|
||||
}
|
||||
}
|
||||
@@ -268,7 +271,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
|
||||
// This is a hack that I want to remove from this but for now, we need to
|
||||
// fix up the titlebar tabs here before we do everything below.
|
||||
if let window = window as? TerminalWindow,
|
||||
if let window = window as? LegacyTerminalWindow,
|
||||
window.titlebarTabs {
|
||||
window.titlebarTabs = true
|
||||
}
|
||||
@@ -303,6 +306,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
|
||||
// Notify the delegate
|
||||
NotificationCenter.default.post(name: .fullscreenDidExit, object: self)
|
||||
self.delegate?.fullscreenDidChange()
|
||||
}
|
||||
|
||||
@@ -422,3 +426,8 @@ class NonNativeFullscreenVisibleMenu: NonNativeFullscreen {
|
||||
class NonNativeFullscreenPaddedNotch: NonNativeFullscreen {
|
||||
override var properties: Properties { Properties(paddedNotch: true) }
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
static let fullscreenDidEnter = Notification.Name("com.mitchellh.fullscreenDidEnter")
|
||||
static let fullscreenDidExit = Notification.Name("com.mitchellh.fullscreenDidExit")
|
||||
}
|
||||
|
Reference in New Issue
Block a user