mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
macos: add enum type for macos-titlebar-style
This commit is contained in:
@@ -19,10 +19,10 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
}
|
||||
|
||||
let nib = switch config.macosTitlebarStyle {
|
||||
case "native": "Terminal"
|
||||
case "hidden": "TerminalHiddenTitlebar"
|
||||
case "transparent": "TerminalTransparentTitlebar"
|
||||
case "tabs":
|
||||
case .native: "Terminal"
|
||||
case .hidden: "TerminalHiddenTitlebar"
|
||||
case .transparent: "TerminalTransparentTitlebar"
|
||||
case .tabs:
|
||||
#if compiler(>=6.2)
|
||||
if #available(macOS 26.0, *) {
|
||||
"TerminalTabsTitlebarTahoe"
|
||||
@@ -32,7 +32,6 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
#else
|
||||
"TerminalTabsTitlebarVentura"
|
||||
#endif
|
||||
default: defaultValue
|
||||
}
|
||||
|
||||
return nib
|
||||
@@ -1537,7 +1536,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
struct DerivedConfig {
|
||||
let backgroundColor: Color
|
||||
let macosWindowButtons: Ghostty.MacOSWindowButtons
|
||||
let macosTitlebarStyle: String
|
||||
let macosTitlebarStyle: Ghostty.Config.MacOSTitlebarStyle
|
||||
let maximize: Bool
|
||||
let windowPositionX: Int16?
|
||||
let windowPositionY: Int16?
|
||||
@@ -1545,7 +1544,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
init() {
|
||||
self.backgroundColor = Color(NSColor.windowBackgroundColor)
|
||||
self.macosWindowButtons = .visible
|
||||
self.macosTitlebarStyle = "system"
|
||||
self.macosTitlebarStyle = .default
|
||||
self.maximize = false
|
||||
self.windowPositionX = nil
|
||||
self.windowPositionY = nil
|
||||
|
||||
@@ -105,7 +105,7 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
idealHeight: lastFocusedSurface?.value?.initialSize?.height)
|
||||
}
|
||||
// Ignore safe area to extend up in to the titlebar region if we have the "hidden" titlebar style
|
||||
.ignoresSafeArea(.container, edges: ghostty.config.macosTitlebarStyle == "hidden" ? .top : [])
|
||||
.ignoresSafeArea(.container, edges: ghostty.config.macosTitlebarStyle == .hidden ? .top : [])
|
||||
|
||||
if let surfaceView = lastFocusedSurface?.value {
|
||||
TerminalCommandPaletteView(
|
||||
|
||||
@@ -588,7 +588,7 @@ class TerminalWindow: NSWindow {
|
||||
let backgroundColor: NSColor
|
||||
let backgroundOpacity: Double
|
||||
let macosWindowButtons: Ghostty.MacOSWindowButtons
|
||||
let macosTitlebarStyle: String
|
||||
let macosTitlebarStyle: Ghostty.Config.MacOSTitlebarStyle
|
||||
let windowCornerRadius: CGFloat
|
||||
|
||||
init() {
|
||||
@@ -597,7 +597,7 @@ class TerminalWindow: NSWindow {
|
||||
self.backgroundOpacity = 1
|
||||
self.macosWindowButtons = .visible
|
||||
self.backgroundBlur = .disabled
|
||||
self.macosTitlebarStyle = "transparent"
|
||||
self.macosTitlebarStyle = .default
|
||||
self.windowCornerRadius = 16
|
||||
}
|
||||
|
||||
@@ -613,7 +613,7 @@ class TerminalWindow: NSWindow {
|
||||
// Native, transparent, and hidden styles use 16pt radius
|
||||
// Tabs style uses 20pt radius
|
||||
switch config.macosTitlebarStyle {
|
||||
case "tabs":
|
||||
case .tabs:
|
||||
self.windowCornerRadius = 20
|
||||
default:
|
||||
self.windowCornerRadius = 16
|
||||
|
||||
@@ -92,8 +92,8 @@ class TransparentTitlebarTerminalWindow: TerminalWindow {
|
||||
// For glass background styles, use a transparent titlebar to let the glass effect show through
|
||||
// Only apply this for transparent and tabs titlebar styles
|
||||
let isGlassStyle = derivedConfig.backgroundBlur.isGlassStyle
|
||||
let isTransparentTitlebar = derivedConfig.macosTitlebarStyle == "transparent" ||
|
||||
derivedConfig.macosTitlebarStyle == "tabs"
|
||||
let isTransparentTitlebar = derivedConfig.macosTitlebarStyle == .transparent ||
|
||||
derivedConfig.macosTitlebarStyle == .tabs
|
||||
|
||||
titlebarView.layer?.backgroundColor = (isGlassStyle && isTransparentTitlebar)
|
||||
? NSColor.clear.cgColor
|
||||
|
||||
@@ -354,14 +354,14 @@ extension Ghostty {
|
||||
return MacOSWindowButtons(rawValue: str) ?? defaultValue
|
||||
}
|
||||
|
||||
var macosTitlebarStyle: String {
|
||||
let defaultValue = "transparent"
|
||||
var macosTitlebarStyle: MacOSTitlebarStyle {
|
||||
let defaultValue = MacOSTitlebarStyle.transparent
|
||||
guard let config = self.config else { return defaultValue }
|
||||
var v: UnsafePointer<Int8>?
|
||||
let key = "macos-titlebar-style"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.lengthOfBytes(using: .utf8))) else { return defaultValue }
|
||||
guard let ptr = v else { return defaultValue }
|
||||
return String(cString: ptr)
|
||||
return MacOSTitlebarStyle(rawValue: String(cString: ptr)) ?? defaultValue
|
||||
}
|
||||
|
||||
var macosTitlebarProxyIcon: MacOSTitlebarProxyIcon {
|
||||
@@ -906,4 +906,9 @@ extension Ghostty.Config {
|
||||
static let bell = NotifyOnCommandFinishAction(rawValue: 1 << 0)
|
||||
static let notify = NotifyOnCommandFinishAction(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
enum MacOSTitlebarStyle: String {
|
||||
static let `default` = MacOSTitlebarStyle.transparent
|
||||
case native, transparent, tabs, hidden
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import Testing
|
||||
private struct OptionalIdealSizeView: View {
|
||||
let idealWidth: CGFloat?
|
||||
let idealHeight: CGFloat?
|
||||
let titlebarStyle: String
|
||||
let titlebarStyle: Ghostty.Config.MacOSTitlebarStyle
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
@@ -20,7 +20,7 @@ private struct OptionalIdealSizeView: View {
|
||||
.frame(idealWidth: idealWidth, idealHeight: idealHeight)
|
||||
}
|
||||
// Matches TerminalView line 108: hidden style extends into titlebar
|
||||
.ignoresSafeArea(.container, edges: titlebarStyle == "hidden" ? .top : [])
|
||||
.ignoresSafeArea(.container, edges: titlebarStyle == .hidden ? .top : [])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,18 +28,18 @@ private let minReasonableWidth: CGFloat = 100
|
||||
private let minReasonableHeight: CGFloat = 50
|
||||
|
||||
/// All macos-titlebar-style values that map to different window nibs.
|
||||
private let allTitlebarStyles = ["native", "hidden", "transparent", "tabs"]
|
||||
private let allTitlebarStyles: [Ghostty.Config.MacOSTitlebarStyle] = [.native, .hidden, .transparent, .tabs]
|
||||
|
||||
/// Window style masks that roughly correspond to each titlebar style.
|
||||
/// In real Ghostty these come from different nib files; in tests we
|
||||
/// approximate with NSWindow style masks.
|
||||
private func styleMask(for titlebarStyle: String) -> NSWindow.StyleMask {
|
||||
private func styleMask(for titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) -> NSWindow.StyleMask {
|
||||
switch titlebarStyle {
|
||||
case "hidden":
|
||||
case .hidden:
|
||||
return [.titled, .resizable, .fullSizeContentView]
|
||||
case "transparent", "tabs":
|
||||
case .transparent, .tabs:
|
||||
return [.titled, .resizable, .fullSizeContentView]
|
||||
default:
|
||||
case .native:
|
||||
return [.titled, .resizable]
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// intrinsicContentSize returns a tiny value.
|
||||
@Test(.bug("https://github.com/ghostty-org/ghostty/issues/11256", "intrinsicContentSize too small before @FocusedValue propagates"),
|
||||
arguments: allTitlebarStyles)
|
||||
func intrinsicSizeTooSmallWithNilIdealSize(titlebarStyle: String) async throws {
|
||||
func intrinsicSizeTooSmallWithNilIdealSize(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let expectedSize = NSSize(width: 600, height: 400)
|
||||
|
||||
// nil ideal sizes = @FocusedValue hasn't propagated lastFocusedSurface
|
||||
@@ -106,7 +106,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// too-small window when intrinsicContentSize is based on nil ideal sizes.
|
||||
@Test(.bug("https://github.com/ghostty-org/ghostty/issues/11256", "apply() sets wrong window size due to racy intrinsicContentSize"),
|
||||
arguments: allTitlebarStyles)
|
||||
func applyProducesWrongSizeWithNilIdealSize(titlebarStyle: String) async throws {
|
||||
func applyProducesWrongSizeWithNilIdealSize(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let container = await TerminalViewContainer {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
@@ -150,7 +150,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// to propagate, so intrinsicContentSize is still tiny when apply() runs.
|
||||
@Test(.bug("https://github.com/ghostty-org/ghostty/issues/11256", "40ms async delay reads intrinsicContentSize before @FocusedValue propagates"),
|
||||
arguments: allTitlebarStyles)
|
||||
func asyncAfterDelayProducesWrongSizeWithNilIdealSize(titlebarStyle: String) async throws {
|
||||
func asyncAfterDelayProducesWrongSizeWithNilIdealSize(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let container = await TerminalViewContainer {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
@@ -195,7 +195,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// fallback value, not just adjust timing.
|
||||
@Test(.bug("https://github.com/ghostty-org/ghostty/issues/11256", "Synchronous apply also fails without fallback"),
|
||||
arguments: allTitlebarStyles)
|
||||
func synchronousApplyAlsoFailsWithNilIdealSize(titlebarStyle: String) async throws {
|
||||
func synchronousApplyAlsoFailsWithNilIdealSize(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let container = await TerminalViewContainer {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
@@ -233,7 +233,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// should be correct for every titlebar style. This is the "happy path" that
|
||||
/// works today when the 40ms delay is sufficient.
|
||||
@Test(arguments: allTitlebarStyles)
|
||||
func intrinsicSizeCorrectWhenIdealSizesAvailable(titlebarStyle: String) async throws {
|
||||
func intrinsicSizeCorrectWhenIdealSizesAvailable(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let expectedSize = NSSize(width: 600, height: 400)
|
||||
|
||||
let container = await TerminalViewContainer {
|
||||
@@ -274,7 +274,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// Verifies that apply() sets a correctly sized window when ideal sizes
|
||||
/// are available, for each titlebar style.
|
||||
@Test(arguments: allTitlebarStyles)
|
||||
func applyProducesCorrectSizeWhenIdealSizesAvailable(titlebarStyle: String) async throws {
|
||||
func applyProducesCorrectSizeWhenIdealSizesAvailable(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let expectedSize = NSSize(width: 600, height: 400)
|
||||
|
||||
let container = await TerminalViewContainer {
|
||||
@@ -319,7 +319,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// This should always pass — it validates the delay works when @FocusedValue
|
||||
/// has already propagated.
|
||||
@Test(arguments: allTitlebarStyles)
|
||||
func asyncAfterDelayProducesCorrectSizeWhenIdealSizesAvailable(titlebarStyle: String) async throws {
|
||||
func asyncAfterDelayProducesCorrectSizeWhenIdealSizesAvailable(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let expectedSize = NSSize(width: 600, height: 400)
|
||||
|
||||
let container = await TerminalViewContainer {
|
||||
@@ -364,7 +364,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// (never .contentIntrinsicSize). The window uses its initial frame.
|
||||
/// This should work for all titlebar styles regardless of the bug.
|
||||
@Test(arguments: allTitlebarStyles)
|
||||
func framePathWorksWithoutWindowSize(titlebarStyle: String) async throws {
|
||||
func framePathWorksWithoutWindowSize(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let expectedFrame = NSRect(x: 100, y: 100, width: 800, height: 600)
|
||||
|
||||
let container = await TerminalViewContainer {
|
||||
@@ -399,7 +399,7 @@ struct IntrinsicSizeTimingTests {
|
||||
/// Verifies isChanged correctly detects mismatch for contentIntrinsicSize
|
||||
/// across titlebar styles when ideal sizes are available.
|
||||
@Test(arguments: allTitlebarStyles)
|
||||
func isChangedDetectsMismatch(titlebarStyle: String) async throws {
|
||||
func isChangedDetectsMismatch(titlebarStyle: Ghostty.Config.MacOSTitlebarStyle) async throws {
|
||||
let expectedSize = NSSize(width: 600, height: 400)
|
||||
|
||||
let container = await TerminalViewContainer {
|
||||
|
||||
Reference in New Issue
Block a user