From 5877913ab8104d9887efb6dbacee8c2bc7b39200 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 12 Jun 2025 12:02:31 -0700 Subject: [PATCH] macoS: Split out terminal tabs for ventura vs tahoe --- macos/Ghostty.xcodeproj/project.pbxproj | 32 ++--- .../Terminal/TerminalController.swift | 114 +++--------------- .../HiddenTitlebarTerminalWindow.swift | 29 +++-- .../Window Styles/TerminalHiddenTitlebar.xib | 2 +- ...gacy.xib => TerminalTabsTitlebarTahoe.xib} | 2 +- ...ar.xib => TerminalTabsTitlebarVentura.xib} | 4 +- .../Window Styles/TerminalWindow.swift | 2 +- ... => TitlebarTabsTahoeTerminalWindow.swift} | 5 +- ...> TitlebarTabsVenturaTerminalWindow.swift} | 72 +++++++++-- macos/Sources/Helpers/Fullscreen.swift | 3 +- 10 files changed, 120 insertions(+), 145 deletions(-) rename macos/Sources/Features/Terminal/Window Styles/{TerminalLegacy.xib => TerminalTabsTitlebarTahoe.xib} (94%) rename macos/Sources/Features/Terminal/Window Styles/{TerminalTabsTitlebar.xib => TerminalTabsTitlebarVentura.xib} (91%) rename macos/Sources/Features/Terminal/Window Styles/{TabsTitlebarTerminalWindow.swift => TitlebarTabsTahoeTerminalWindow.swift} (90%) rename macos/Sources/Features/Terminal/Window Styles/{LegacyTerminalWindow.swift => TitlebarTabsVenturaTerminalWindow.swift} (91%) diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index 3f1cddf44..cd0c17f9b 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -16,9 +16,9 @@ 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 */; }; - A51544FE2DFB111C009E85D8 /* TabsTitlebarTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51544FD2DFB1110009E85D8 /* TabsTitlebarTerminalWindow.swift */; }; - A51545002DFB112E009E85D8 /* TerminalTabsTitlebar.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51544FF2DFB112E009E85D8 /* TerminalTabsTitlebar.xib */; }; - A51B78472AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift */; }; + A51544FE2DFB111C009E85D8 /* TitlebarTabsTahoeTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51544FD2DFB1110009E85D8 /* TitlebarTabsTahoeTerminalWindow.swift */; }; + A51545002DFB112E009E85D8 /* TerminalTabsTitlebarTahoe.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51544FF2DFB112E009E85D8 /* TerminalTabsTitlebarTahoe.xib */; }; + A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.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 */; }; @@ -57,7 +57,7 @@ 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 */; }; + A5593FE52DF8DE3000B47B10 /* TerminalTabsTitlebarVentura.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5593FE42DF8DE3000B47B10 /* TerminalTabsTitlebarVentura.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 */; }; @@ -139,9 +139,9 @@ 9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = ""; }; A50297342DFA0F3300B4E924 /* Double+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = ""; }; A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = ""; }; - A51544FD2DFB1110009E85D8 /* TabsTitlebarTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTitlebarTerminalWindow.swift; sourceTree = ""; }; - A51544FF2DFB112E009E85D8 /* TerminalTabsTitlebar.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalTabsTitlebar.xib; sourceTree = ""; }; - A51B78462AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTerminalWindow.swift; sourceTree = ""; }; + A51544FD2DFB1110009E85D8 /* TitlebarTabsTahoeTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitlebarTabsTahoeTerminalWindow.swift; sourceTree = ""; }; + A51544FF2DFB112E009E85D8 /* TerminalTabsTitlebarTahoe.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalTabsTitlebarTahoe.xib; sourceTree = ""; }; + A51B78462AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitlebarTabsVenturaTerminalWindow.swift; sourceTree = ""; }; A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = ""; }; A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = ""; }; A51BFC212B2FB6B400E92F16 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; @@ -174,7 +174,7 @@ A5593FDE2DF8D57100B47B10 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = ""; }; A5593FE02DF8D73400B47B10 /* HiddenTitlebarTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HiddenTitlebarTerminalWindow.swift; sourceTree = ""; }; A5593FE22DF8D78600B47B10 /* TerminalHiddenTitlebar.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalHiddenTitlebar.xib; sourceTree = ""; }; - A5593FE42DF8DE3000B47B10 /* TerminalLegacy.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalLegacy.xib; sourceTree = ""; }; + A5593FE42DF8DE3000B47B10 /* TerminalTabsTitlebarVentura.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalTabsTitlebarVentura.xib; sourceTree = ""; }; A5593FE62DF927CC00B47B10 /* TransparentTitlebarTerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparentTitlebarTerminalWindow.swift; sourceTree = ""; }; A5593FE82DF927DF00B47B10 /* TerminalTransparentTitlebar.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TerminalTransparentTitlebar.xib; sourceTree = ""; }; A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; @@ -407,13 +407,13 @@ children = ( A59630992AEE1C6400D64628 /* Terminal.xib */, A5593FE22DF8D78600B47B10 /* TerminalHiddenTitlebar.xib */, - A5593FE42DF8DE3000B47B10 /* TerminalLegacy.xib */, - A51544FF2DFB112E009E85D8 /* TerminalTabsTitlebar.xib */, + A51544FF2DFB112E009E85D8 /* TerminalTabsTitlebarTahoe.xib */, + A5593FE42DF8DE3000B47B10 /* TerminalTabsTitlebarVentura.xib */, A5593FE82DF927DF00B47B10 /* TerminalTransparentTitlebar.xib */, A5593FDE2DF8D57100B47B10 /* TerminalWindow.swift */, A5593FE02DF8D73400B47B10 /* HiddenTitlebarTerminalWindow.swift */, - A51B78462AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift */, - A51544FD2DFB1110009E85D8 /* TabsTitlebarTerminalWindow.swift */, + A51B78462AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift */, + A51544FD2DFB1110009E85D8 /* TitlebarTabsTahoeTerminalWindow.swift */, A5593FE62DF927CC00B47B10 /* TransparentTitlebarTerminalWindow.swift */, ); path = "Window Styles"; @@ -682,7 +682,7 @@ buildActionMask = 2147483647; files = ( FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */, - A5593FE52DF8DE3000B47B10 /* TerminalLegacy.xib in Resources */, + A5593FE52DF8DE3000B47B10 /* TerminalTabsTitlebarVentura.xib in Resources */, 29C15B1D2CDC3B2900520DD4 /* bat in Resources */, A586167C2B7703CC009BDB1D /* fish in Resources */, 55154BE02B33911F001622DC /* ghostty in Resources */, @@ -700,7 +700,7 @@ A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */, 857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */, A596309A2AEE1C6400D64628 /* Terminal.xib in Resources */, - A51545002DFB112E009E85D8 /* TerminalTabsTitlebar.xib in Resources */, + A51545002DFB112E009E85D8 /* TerminalTabsTitlebarTahoe.xib in Resources */, A5CBD05C2CA0C5C70017A1AE /* QuickTerminal.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -768,7 +768,7 @@ A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */, A5874D992DAD751B00E83852 /* CGS.swift in Sources */, A586366B2DF0A98C00E04A10 /* Array+Extension.swift in Sources */, - A51544FE2DFB111C009E85D8 /* TabsTitlebarTerminalWindow.swift in Sources */, + A51544FE2DFB111C009E85D8 /* TitlebarTabsTahoeTerminalWindow.swift in Sources */, A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */, A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */, A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */, @@ -777,7 +777,7 @@ A53A297F2DB4480F00B6E02C /* EventModifiers+Extension.swift in Sources */, A53A297B2DB2E49700B6E02C /* CommandPalette.swift in Sources */, A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */, - A51B78472AF4B58B00F3EDB9 /* LegacyTerminalWindow.swift in Sources */, + A51B78472AF4B58B00F3EDB9 /* TitlebarTabsVenturaTerminalWindow.swift in Sources */, A57D79272C9C879B001D522E /* SecureInput.swift in Sources */, A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */, A5593FE12DF8D74000B47B10 /* HiddenTitlebarTerminalWindow.swift in Sources */, diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index d59f71619..977a064e0 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -13,10 +13,15 @@ class TerminalController: BaseTerminalController { let config = appDelegate.ghostty.config let nib = switch config.macosTitlebarStyle { case "native": "Terminal" - //case "tabs": "TerminalTabsTitlebar" - case "tabs": "TerminalLegacy" case "hidden": "TerminalHiddenTitlebar" case "transparent": "TerminalTransparentTitlebar" + case "tabs": + if #available(macOS 26.0, *) { + // TODO: Switch to Tahoe when ready + "TerminalTabsTitlebarVentura" + } else { + "TerminalTabsTitlebarVentura" + } default: defaultValue } @@ -447,68 +452,20 @@ class TerminalController: BaseTerminalController { private func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) { // Let our window handle its own appearance - if let window = window as? TerminalWindow { - // Sync our zoom state for splits - window.surfaceIsZoomed = surfaceTree.zoomed != nil + guard let window = window as? TerminalWindow else { return } - // Set the font for the window and tab titles. - if let titleFontName = surfaceConfig.windowTitleFontFamily { - window.titlebarFont = NSFont(name: titleFontName, size: NSFont.systemFontSize) - } else { - window.titlebarFont = nil - } + // Sync our zoom state for splits + window.surfaceIsZoomed = surfaceTree.zoomed != nil - // Call this last in case it uses any of the properties above. - window.syncAppearance(surfaceConfig) - } - - guard let window else { return } - - if let window = window as? LegacyTerminalWindow { - // Update our window light/darkness based on our updated background color - window.isLightTheme = OSColor(surfaceConfig.backgroundColor).isLightColor - } - - // If our window is not visible, then we do nothing. Some things such as blurring - // have no effect if the window is not visible. Ultimately, we'll have this called - // at some point when a surface becomes focused. - guard window.isVisible else { return } - - guard let window = window as? LegacyTerminalWindow, window.hasStyledTabs else { return } - - // Our background color depends on if our focused surface borders the top or not. - // If it does, we match the focused surface. If it doesn't, we use the app - // configuration. - let backgroundColor: OSColor - if !surfaceTree.isEmpty { - if let focusedSurface = focusedSurface, - let treeRoot = surfaceTree.root, - let focusedNode = treeRoot.node(view: focusedSurface), - treeRoot.spatial().doesBorder(side: .up, from: focusedNode) { - // Similar to above, an alpha component of "0" causes compositor issues, so - // we use 0.001. See: https://github.com/ghostty-org/ghostty/pull/4308 - backgroundColor = OSColor(focusedSurface.backgroundColor ?? surfaceConfig.backgroundColor).withAlphaComponent(0.001) - } else { - // We don't have a focused surface or our surface doesn't border the - // top. We choose to match the color of the top-left most surface. - let topLeftSurface = surfaceTree.root?.leftmostLeaf() - backgroundColor = OSColor(topLeftSurface?.backgroundColor ?? derivedConfig.backgroundColor) - } + // Set the font for the window and tab titles. + if let titleFontName = surfaceConfig.windowTitleFontFamily { + window.titlebarFont = NSFont(name: titleFontName, size: NSFont.systemFontSize) } else { - backgroundColor = OSColor(self.derivedConfig.backgroundColor) + window.titlebarFont = nil } - window.titlebarColor = backgroundColor.withAlphaComponent(surfaceConfig.backgroundOpacity) - if (window.isOpaque) { - // Bg color is only synced if we have no transparency. This is because - // the transparency is handled at the surface level (window.backgroundColor - // ignores alpha components) - window.backgroundColor = backgroundColor - - // If there is transparency, calling this will make the titlebar opaque - // so we only call this if we are opaque. - window.updateTabBar() - } + // Call this last in case it uses any of the properties above. + window.syncAppearance(surfaceConfig) } /// Returns the default size of the window. This is contextual based on the focused surface because @@ -884,28 +841,6 @@ class TerminalController: BaseTerminalController { } } - // TODO: remove - if let window = window as? LegacyTerminalWindow, - config.macosTitlebarStyle == "tabs" { - // 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. - window.tabbingMode = .preferred - window.titlebarTabs = true - DispatchQueue.main.async { - window.tabbingMode = .automatic - } - - 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) - } - } - // Initialize our content view to the SwiftUI root window.contentView = NSHostingView(rootView: TerminalView( ghostty: self.ghostty, @@ -1110,23 +1045,6 @@ class TerminalController: BaseTerminalController { } //MARK: - TerminalViewDelegate - - override func titleDidChange(to: String) { - super.titleDidChange(to: to) - - 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 { - if (window.titlebarTabs || derivedConfig.macosTitlebarStyle == "hidden") { - // Updating the title text as above automatically reveals the - // native title view in macOS 15.0 and above. Since we're using - // a custom view instead, we need to re-hide it. - window.titleVisibility = .hidden - } - toolbar.titleText = to - } - } override func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) { super.focusedSurfaceDidChange(to: to) diff --git a/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift index f2d3b9b85..5f4d6b177 100644 --- a/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/HiddenTitlebarTerminalWindow.swift @@ -19,15 +19,6 @@ class HiddenTitlebarTerminalWindow: TerminalWindow { 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 = [ @@ -64,6 +55,26 @@ class HiddenTitlebarTerminalWindow: TerminalWindow { } } + // MARK: NSWindow + + override var title: String { + didSet { + // Updating the title text as above automatically reveals the + // native title view in macOS 15.0 and above. Since we're using + // a custom view instead, we need to re-hide it. + reapplyHiddenStyle() + } + } + + // 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 + } + // MARK: Notifications @objc private func fullscreenDidExit(_ notification: Notification) { diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalHiddenTitlebar.xib b/macos/Sources/Features/Terminal/Window Styles/TerminalHiddenTitlebar.xib index 1a2a6c192..eb4675657 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalHiddenTitlebar.xib +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalHiddenTitlebar.xib @@ -17,7 +17,7 @@ - + diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalLegacy.xib b/macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebarTahoe.xib similarity index 94% rename from macos/Sources/Features/Terminal/Window Styles/TerminalLegacy.xib rename to macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebarTahoe.xib index 61ed6f782..deaeded9f 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalLegacy.xib +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebarTahoe.xib @@ -13,7 +13,7 @@ - + diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebar.xib b/macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebarVentura.xib similarity index 91% rename from macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebar.xib rename to macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebarVentura.xib index 779b6e094..bf53a4510 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebar.xib +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalTabsTitlebarVentura.xib @@ -13,11 +13,11 @@ - + - + diff --git a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift index a0e18d283..f6b53c289 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift @@ -10,7 +10,7 @@ class TerminalWindow: NSWindow { static let defaultLevelKey: String = "TerminalDefaultLevel" /// The configuration derived from the Ghostty config so we don't need to rely on references. - private var derivedConfig: DerivedConfig? + private(set) var derivedConfig: DerivedConfig? /// Gets the terminal controller from the window controller. var terminalController: TerminalController? { diff --git a/macos/Sources/Features/Terminal/Window Styles/TabsTitlebarTerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift similarity index 90% rename from macos/Sources/Features/Terminal/Window Styles/TabsTitlebarTerminalWindow.swift rename to macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift index 858b54829..c45e93d79 100644 --- a/macos/Sources/Features/Terminal/Window Styles/TabsTitlebarTerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift @@ -1,7 +1,8 @@ import AppKit import SwiftUI -class TabsTitlebarTerminalWindow: TerminalWindow, NSToolbarDelegate { +/// `macos-titlebar-style = tabs` for macOS 26 (Tahoe) and later. +class TitlebarTabsTahoeTerminalWindow: TerminalWindow, NSToolbarDelegate { override func awakeFromNib() { super.awakeFromNib() @@ -49,7 +50,7 @@ extension NSToolbarItem.Identifier { static let title = NSToolbarItem.Identifier("Title") } -extension TabsTitlebarTerminalWindow { +extension TitlebarTabsTahoeTerminalWindow { struct TitleItem: View { var body: some View { Text("HELLO THIS IS A PRETTY LONG TITLE") diff --git a/macos/Sources/Features/Terminal/Window Styles/LegacyTerminalWindow.swift b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift similarity index 91% rename from macos/Sources/Features/Terminal/Window Styles/LegacyTerminalWindow.swift rename to macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift index 89afbf72f..2f8eb5840 100644 --- a/macos/Sources/Features/Terminal/Window Styles/LegacyTerminalWindow.swift +++ b/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsVenturaTerminalWindow.swift @@ -1,11 +1,10 @@ import Cocoa -/// The terminal window that we originally had in Ghostty for a long time. Kind of a soupy mess -/// of styling. -class LegacyTerminalWindow: TerminalWindow { +/// Titlebar tabs for macOS 13 to 15. +class TitlebarTabsVenturaTerminalWindow: TerminalWindow { /// 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. - var isLightTheme: Bool = false + fileprivate var isLightTheme: Bool = false override var surfaceIsZoomed: Bool { didSet { @@ -32,17 +31,30 @@ class LegacyTerminalWindow: TerminalWindow { } } - // MARK: - Lifecycle + // MARK: NSWindow override func awakeFromNib() { super.awakeFromNib() - if titlebarTabs { - generateToolbar() - } - } + // 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. + tabbingMode = .preferred + DispatchQueue.main.async { + self.tabbingMode = .automatic + } - // MARK: - NSWindow + titlebarTabs = true + + // This should always be true since our super sets this up. + if let derivedConfig { + // Set the background color of the window + backgroundColor = derivedConfig.backgroundColor + + // This makes sure our titlebar renders correctly when there is a transparent background + titlebarColor = derivedConfig.backgroundColor.withAlphaComponent(derivedConfig.backgroundOpacity) + } + } // 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 @@ -145,7 +157,29 @@ class LegacyTerminalWindow: TerminalWindow { } } - // MARK: - Tab Bar Styling + // MARK: Appearance + + override func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) { + super.syncAppearance(surfaceConfig) + + // Update our window light/darkness based on our updated background color + isLightTheme = OSColor(surfaceConfig.backgroundColor).isLightColor + + // Update our titlebar color + if let preferredBackgroundColor { + titlebarColor = preferredBackgroundColor + } else if let derivedConfig { + titlebarColor = derivedConfig.backgroundColor.withAlphaComponent(derivedConfig.backgroundOpacity) + } + + if (isOpaque) { + // If there is transparency, calling this will make the titlebar opaque + // so we only call this if we are opaque. + updateTabBar() + } + } + + // MARK: Tab Bar Styling // This is true if we should apply styles to the titlebar or tab bar. var hasStyledTabs: Bool { @@ -333,6 +367,18 @@ class LegacyTerminalWindow: TerminalWindow { } } + override var title: String { + didSet { + // Updating the title text as above automatically reveals the + // native title view in macOS 15.0 and above. Since we're using + // a custom view instead, we need to re-hide it. + titleVisibility = .hidden + if let toolbar = toolbar as? TerminalToolbar { + toolbar.titleText = title + } + } + } + // We have to regenerate a toolbar when the titlebar tabs setting changes since our // custom toolbar conditionally generates the items based on this setting. I tried to // invalidate the toolbar items and force a refresh, but as far as I can tell that @@ -559,7 +605,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: LegacyTerminalWindow? + private weak var terminalWindow: TitlebarTabsVenturaTerminalWindow? private let isLightTheme: Bool private let overlayLayer = VibrantLayer() @@ -587,7 +633,7 @@ fileprivate class WindowButtonsBackdropView: NSView { fatalError("init(coder:) has not been implemented") } - init(window: LegacyTerminalWindow) { + init(window: TitlebarTabsVenturaTerminalWindow) { self.terminalWindow = window self.isLightTheme = window.isLightTheme diff --git a/macos/Sources/Helpers/Fullscreen.swift b/macos/Sources/Helpers/Fullscreen.swift index 3200608d0..d1dac49a3 100644 --- a/macos/Sources/Helpers/Fullscreen.swift +++ b/macos/Sources/Helpers/Fullscreen.swift @@ -271,8 +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? LegacyTerminalWindow, - window.titlebarTabs { + if let window = window as? TitlebarTabsVenturaTerminalWindow, window.titlebarTabs { window.titlebarTabs = true }