mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Make unselected tabs blend better with background color
This enables the standard effect created by a sytem tab bar, which ensures unselected tabs blend with the window's/titlebar's background color. This also ensures the `windowButtonsBackdrop` view matches the color of the adjacent tab (be it selected or not).
This commit is contained in:
@ -27,7 +27,7 @@ class TerminalWindow: NSWindow {
|
||||
}
|
||||
}
|
||||
|
||||
private var windowButtonsBackdrop: NSView? = nil
|
||||
private var windowButtonsBackdrop: WindowButtonsBackdropView? = nil
|
||||
private var windowDragHandle: WindowDragView? = nil
|
||||
private var storedTitlebarBackgroundColor: CGColor? = nil
|
||||
|
||||
@ -51,9 +51,21 @@ class TerminalWindow: NSWindow {
|
||||
|
||||
/// This is called by titlebarTabs changing so that we can setup the rest of our window
|
||||
private func changedTitlebarTabs(to newValue: Bool) {
|
||||
self.titlebarAppearsTransparent = newValue
|
||||
|
||||
if (newValue) {
|
||||
// By hiding the visual effect view, we allow the window's (or titlebar's in this case)
|
||||
// background color to show through. If we were to set `titlebarAppearsTransparent` to true
|
||||
// the selected tab would look fine, but the unselected ones and new tab button backgrounds
|
||||
// would be an opaque color. When the titlebar isn't transparent, however, the system applies
|
||||
// a compositing effect to the unselected tab backgrounds, which makes them blend with the
|
||||
// titlebar's/window's background.
|
||||
if let titlebarContainer = contentView?.superview?.subviews.first(where: {
|
||||
$0.className == "NSTitlebarContainerView"
|
||||
}), let effectView = titlebarContainer.descendants(withClassName: "NSVisualEffectView").first {
|
||||
effectView.isHidden = true
|
||||
}
|
||||
|
||||
self.titlebarSeparatorStyle = .none
|
||||
|
||||
// We use the toolbar to anchor our tab bar positions in the titlebar,
|
||||
// so we make sure it's the right size/position, and exists.
|
||||
self.toolbarStyle = .unifiedCompact
|
||||
@ -181,6 +193,19 @@ class TerminalWindow: NSWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// This is called when we open, close, switch, and reorder tabs, at which point we determine if the
|
||||
// first tab in the tab bar is selected. If it is, we make the `windowButtonsBackdrop` color the same
|
||||
// as that of the active tab (i.e. the titlebar's background color), otherwise we make it the same
|
||||
// color as the background of unselected tabs.
|
||||
override func update() {
|
||||
super.update()
|
||||
if let index = windowController?.window?.tabbedWindows?.firstIndex(of: self) {
|
||||
let firstTabIsSelected = index == 0
|
||||
|
||||
windowButtonsBackdrop?.isHighlighted = firstTabIsSelected
|
||||
}
|
||||
}
|
||||
|
||||
private func addWindowButtonsBackdrop(titlebarView: NSView, toolbarView: NSView) {
|
||||
// If we already made the view, just make sure it's unhidden and correctly placed as a subview.
|
||||
if let view = windowButtonsBackdrop {
|
||||
@ -188,31 +213,21 @@ class TerminalWindow: NSWindow {
|
||||
view.isHidden = false
|
||||
titlebarView.addSubview(view)
|
||||
view.leftAnchor.constraint(equalTo: toolbarView.leftAnchor).isActive = true
|
||||
view.rightAnchor.constraint(equalTo: toolbarView.leftAnchor, constant: 80).isActive = true
|
||||
view.rightAnchor.constraint(equalTo: toolbarView.leftAnchor, constant: 78).isActive = true
|
||||
view.topAnchor.constraint(equalTo: toolbarView.topAnchor).isActive = true
|
||||
view.heightAnchor.constraint(equalTo: toolbarView.heightAnchor).isActive = true
|
||||
return
|
||||
}
|
||||
|
||||
let view = NSView()
|
||||
let view = WindowButtonsBackdropView(backgroundColor: storedTitlebarBackgroundColor ?? NSColor.windowBackgroundColor.cgColor)
|
||||
view.identifier = NSUserInterfaceItemIdentifier("_windowButtonsBackdrop")
|
||||
titlebarView.addSubview(view)
|
||||
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.leftAnchor.constraint(equalTo: toolbarView.leftAnchor).isActive = true
|
||||
view.rightAnchor.constraint(equalTo: toolbarView.leftAnchor, constant: 80).isActive = true
|
||||
view.rightAnchor.constraint(equalTo: toolbarView.leftAnchor, constant: 78).isActive = true
|
||||
view.topAnchor.constraint(equalTo: toolbarView.topAnchor).isActive = true
|
||||
view.heightAnchor.constraint(equalTo: toolbarView.heightAnchor).isActive = true
|
||||
view.wantsLayer = true
|
||||
|
||||
// This is jank but this makes the background color for light themes on the button
|
||||
// backdrop look MUCH better. I couldn't figure out a perfect color to use that works
|
||||
// for both so we just check the appearance.
|
||||
if effectiveAppearance.name == .aqua {
|
||||
view.layer?.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.95, alpha: 1)
|
||||
} else {
|
||||
view.layer?.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.0, alpha: 0.45)
|
||||
}
|
||||
|
||||
windowButtonsBackdrop = view
|
||||
}
|
||||
@ -283,3 +298,45 @@ fileprivate class WindowDragView: NSView {
|
||||
addCursorRect(bounds, cursor: .openHand)
|
||||
}
|
||||
}
|
||||
|
||||
// A view that matches the color of selected and unselected tabs in the adjacent tab bar.
|
||||
fileprivate class WindowButtonsBackdropView: NSView {
|
||||
private let overlayLayer = PlusDarkerBlendingModeLayer()
|
||||
private let backgroundColor: CGColor
|
||||
private let isLightTheme: Bool
|
||||
|
||||
var isHighlighted: Bool = true {
|
||||
didSet {
|
||||
if isLightTheme {
|
||||
overlayLayer.isHidden = isHighlighted
|
||||
layer?.backgroundColor = backgroundColor
|
||||
} else {
|
||||
overlayLayer.isHidden = true
|
||||
layer?.backgroundColor = isHighlighted ? backgroundColor : CGColor(genericGrayGamma2_2Gray: 0.0, alpha: 0.45)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
init(backgroundColor: CGColor) {
|
||||
self.backgroundColor = backgroundColor
|
||||
self.isLightTheme = NSColor(cgColor: backgroundColor)!.isLightColor
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
|
||||
wantsLayer = true
|
||||
|
||||
overlayLayer.frame = layer!.bounds
|
||||
overlayLayer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
|
||||
overlayLayer.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.95, alpha: 1)
|
||||
|
||||
layer?.addSublayer(overlayLayer)
|
||||
|
||||
isHighlighted = true
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user