diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index e5f3bc298..75bb0e3bd 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -227,9 +227,21 @@ class TerminalController: BaseTerminalController { guard window.hasStyledTabs else { return } - // The titlebar is always updated. We don't need to worry about opacity - // because we handle it here. - let backgroundColor = OSColor(focusedSurface?.backgroundColor ?? surfaceConfig.backgroundColor) + // 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 let surfaceTree { + if let focusedSurface, surfaceTree.doesBorderTop(view: focusedSurface) { + backgroundColor = OSColor(focusedSurface.backgroundColor ?? derivedConfig.backgroundColor) + } 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. + backgroundColor = OSColor(surfaceTree.topLeft().backgroundColor ?? derivedConfig.backgroundColor) + } + } else { + backgroundColor = OSColor(self.derivedConfig.backgroundColor) + } window.titlebarColor = backgroundColor.withAlphaComponent(surfaceConfig.backgroundOpacity) if (window.isOpaque) { @@ -677,13 +689,16 @@ class TerminalController: BaseTerminalController { } private struct DerivedConfig { + let backgroundColor: Color let macosTitlebarStyle: String init() { + self.backgroundColor = Color(NSColor.windowBackgroundColor) self.macosTitlebarStyle = "system" } init(_ config: Ghostty.Config) { + self.backgroundColor = config.backgroundColor self.macosTitlebarStyle = config.macosTitlebarStyle } } diff --git a/macos/Sources/Ghostty/Ghostty.SplitNode.swift b/macos/Sources/Ghostty/Ghostty.SplitNode.swift index 361af1e1b..f863eeada 100644 --- a/macos/Sources/Ghostty/Ghostty.SplitNode.swift +++ b/macos/Sources/Ghostty/Ghostty.SplitNode.swift @@ -38,6 +38,16 @@ extension Ghostty { } } + func topLeft() -> SurfaceView { + switch (self) { + case .leaf(let leaf): + return leaf.surface + + case .split(let container): + return container.topLeft.topLeft() + } + } + /// Returns the view that would prefer receiving focus in this tree. This is always the /// top-left-most view. This is used when creating a split or closing a split to find the /// next view to send focus to. @@ -136,6 +146,24 @@ extension Ghostty { } } + /// Returns true if the surface borders the top. Assumes the view is in the tree. + func doesBorderTop(view: SurfaceView) -> Bool { + switch (self) { + case .leaf(let leaf): + return leaf.surface == view + + case .split(let container): + switch (container.direction) { + case .vertical: + return container.topLeft.doesBorderTop(view: view) + + case .horizontal: + return container.topLeft.doesBorderTop(view: view) || + container.bottomRight.doesBorderTop(view: view) + } + } + } + // MARK: - Sequence func makeIterator() -> IndexingIterator<[Leaf]> { diff --git a/src/config/Config.zig b/src/config/Config.zig index a22779ac1..1e67a7e12 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1518,6 +1518,13 @@ keybind: Keybinds = .{}, /// This makes a more seamless window appearance but looks a little less /// typical for a macOS application and may not work well with all themes. /// +/// The "transparent" style will also update in real-time to dynamic +/// changes to the window background color, i.e. via OSC 11. To make this +/// more aesthetically pleasing, this only happens if the terminal is +/// a window, tab, or split that borders the top of the window. This +/// avoids a disjointed appearance where the titlebar color changes +/// but all the topmost terminals don't match. +/// /// The "tabs" style is a completely custom titlebar that integrates the /// tab bar into the titlebar. This titlebar always matches the background /// color of the terminal. There are some limitations to this style: @@ -1536,11 +1543,6 @@ keybind: Keybinds = .{}, /// but its one I think is the most aesthetically pleasing and works in /// most cases. /// -/// BUG: If a separate light/dark mode theme is configured with "theme", -/// then `macos-titlebar-style = transparent` will not work correctly. To -/// avoid ugly titlebars, `macos-titlebar-style` will become `native` if -/// a separate light/dark theme is configured. -/// /// Changing this option at runtime only applies to new windows. @"macos-titlebar-style": MacTitlebarStyle = .transparent,