macos: only color the titlebar of surfaces that border the top

This commit is contained in:
Mitchell Hashimoto
2024-11-21 14:07:21 -08:00
parent 7fb86a3c9c
commit 36a57826a6
3 changed files with 53 additions and 8 deletions

View File

@ -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
}
}

View File

@ -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]> {

View File

@ -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,