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:
Pete Schaffner
2024-02-08 10:14:15 +01:00
parent 40cfa1abd4
commit 33a8368a68

View File

@ -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
@ -180,7 +192,20 @@ class TerminalWindow: NSWindow {
self.markHierarchyForLayout(accessoryView)
}
}
// 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,32 +213,22 @@ 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
}
}