macOS: Fix more `macos-titlebar-style` related issues (#9163)

### This PR depends on #9162 

#1691 doesn't seem to be an issue anymore, but changing appearance while
Ghosty is active will result in similar nastiness.



https://github.com/user-attachments/assets/fcd7761e-a521-4382-8d7a-9d93dc0806bc



### Changes

- [Sequoia/Ventura] Fix flickering new tab icon, and it also didn't
respond to window's key status change correctly
- [Sequoia/Ventura] Fix after changing appearance, tab bar may disappear
or have inconsistent background colour
- Fix initial tint of reset zoom button on Sequoia/Ventura with
`macos-titlebar-style=tabs` and all `native/transparent` titlebars
- Fix title alignment with custom font with `native/transparent`
titlebar
pull/9168/head
Xiangbao Meng 2025-10-12 16:31:42 +02:00 committed by GitHub
parent cbc06a0abc
commit 47a8f8083d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 39 deletions

View File

@ -159,6 +159,12 @@ class TerminalWindow: NSWindow {
} else {
tabBarDidDisappear()
}
viewModel.isMainWindow = true
}
override func resignMain() {
super.resignMain()
viewModel.isMainWindow = false
}
override func mergeAllWindows(_ sender: Any?) {
@ -298,7 +304,7 @@ class TerminalWindow: NSWindow {
button.isBordered = false
button.allowsExpansionToolTips = true
button.toolTip = "Reset Zoom"
button.contentTintColor = .controlAccentColor
button.contentTintColor = isMainWindow ? .controlAccentColor : .secondaryLabelColor
button.state = .on
button.image = NSImage(named:"ResetZoom")
button.frame = NSRect(x: 0, y: 0, width: 20, height: 20)
@ -324,6 +330,7 @@ class TerminalWindow: NSWindow {
let font = titlebarFont ?? NSFont.titleBarFont(ofSize: NSFont.systemFontSize)
titlebarTextField?.font = font
titlebarTextField?.usesSingleLineMode = true
tab.attributedTitle = attributedTitle
}
}
@ -505,6 +512,7 @@ extension TerminalWindow {
class ViewModel: ObservableObject {
@Published var isSurfaceZoomed: Bool = false
@Published var hasToolbar: Bool = false
@Published var isMainWindow: Bool = true
/// Calculates the top padding based on toolbar visibility and macOS version
fileprivate var accessoryTopPadding: CGFloat {
@ -525,7 +533,7 @@ extension TerminalWindow {
VStack {
Button(action: action) {
Image("ResetZoom")
.foregroundColor(.accentColor)
.foregroundColor(viewModel.isMainWindow ? .accentColor : .secondary)
}
.buttonStyle(.plain)
.help("Reset Split Zoom")

View File

@ -145,6 +145,7 @@ class TitlebarTabsVenturaTerminalWindow: TerminalWindow {
super.syncAppearance(surfaceConfig)
// Update our window light/darkness based on our updated background color
let themeChanged = isLightTheme != OSColor(surfaceConfig.backgroundColor).isLightColor
isLightTheme = OSColor(surfaceConfig.backgroundColor).isLightColor
// Update our titlebar color
@ -154,7 +155,7 @@ class TitlebarTabsVenturaTerminalWindow: TerminalWindow {
titlebarColor = derivedConfig.backgroundColor.withAlphaComponent(derivedConfig.backgroundOpacity)
}
if (isOpaque) {
if (isOpaque || themeChanged) {
// If there is transparency, calling this will make the titlebar opaque
// so we only call this if we are opaque.
updateTabBar()
@ -187,41 +188,33 @@ class TitlebarTabsVenturaTerminalWindow: TerminalWindow {
// so we need to do it manually.
private func updateNewTabButtonOpacity() {
guard let newTabButton: NSButton = titlebarContainer?.firstDescendant(withClassName: "NSTabBarNewTabButton") as? NSButton else { return }
guard let newTabButtonImageView: NSImageView = newTabButton.subviews.first(where: {
$0 as? NSImageView != nil
}) as? NSImageView else { return }
guard let newTabButtonImageView = newTabButton.firstDescendant(withClassName: "NSImageView") as? NSImageView else { return }
newTabButtonImageView.alphaValue = isKeyWindow ? 1 : 0.5
}
// Color the new tab button's image to match the color of the tab title/keyboard shortcut labels,
// just as it does in the stock tab bar.
/// Update: This method only add a vibrant overlay now,
/// since the image itself supports light/dark tint,
/// and system could restore it any time,
/// altering it will only cause maintenance burden for us.
///
/// And if we hide original image,
/// ``updateNewTabButtonOpacity`` will not work
///
/// ~~Color the new tab button's image to match the color of the tab title/keyboard shortcut labels,~~
/// ~~just as it does in the stock tab bar.~~
private func updateNewTabButtonImage() {
guard let newTabButton: NSButton = titlebarContainer?.firstDescendant(withClassName: "NSTabBarNewTabButton") as? NSButton else { return }
guard let newTabButtonImageView: NSImageView = newTabButton.subviews.first(where: {
$0 as? NSImageView != nil
}) as? NSImageView else { return }
guard let newTabButtonImageView = newTabButton.firstDescendant(withClassName: "NSImageView") as? NSImageView else { return }
guard let newTabButtonImage = newTabButtonImageView.image else { return }
let imageLayer = newTabButtonImageLayer ?? VibrantLayer(forAppearance: isLightTheme ? .light : .dark)!
imageLayer.frame = NSRect(origin: NSPoint(x: newTabButton.bounds.midX - newTabButtonImage.size.width/2, y: newTabButton.bounds.midY - newTabButtonImage.size.height/2), size: newTabButtonImage.size)
imageLayer.contentsGravity = .resizeAspect
imageLayer.opacity = 0.5
if newTabButtonImageLayer == nil {
let fillColor: NSColor = isLightTheme ? .black.withAlphaComponent(0.85) : .white.withAlphaComponent(0.85)
let newImage = NSImage(size: newTabButtonImage.size, flipped: false) { rect in
newTabButtonImage.draw(in: rect)
fillColor.setFill()
rect.fill(using: .sourceAtop)
return true
}
let imageLayer = VibrantLayer(forAppearance: isLightTheme ? .light : .dark)!
imageLayer.frame = NSRect(origin: NSPoint(x: newTabButton.bounds.midX - newTabButtonImage.size.width/2, y: newTabButton.bounds.midY - newTabButtonImage.size.height/2), size: newTabButtonImage.size)
imageLayer.contentsGravity = .resizeAspect
imageLayer.contents = newImage
imageLayer.opacity = 0.5
newTabButtonImageLayer = imageLayer
newTabButtonImageLayer = imageLayer
}
newTabButtonImageView.isHidden = true
newTabButton.layer?.sublayers?.first(where: { $0.className == "VibrantLayer" })?.removeFromSuperlayer()
newTabButton.layer?.addSublayer(newTabButtonImageLayer!)
}
@ -452,6 +445,13 @@ class TitlebarTabsVenturaTerminalWindow: TerminalWindow {
}
private func addWindowButtonsBackdrop(titlebarView: NSView, toolbarView: NSView) {
guard windowButtonsBackdrop?.superview != titlebarView else {
/// replacing existing backdrop aggressively
/// may cause incorrect hierarchy
///
/// because multiple windows are adding this around the 'same time'
return
}
windowButtonsBackdrop?.removeFromSuperview()
windowButtonsBackdrop = nil
@ -470,16 +470,11 @@ class TitlebarTabsVenturaTerminalWindow: TerminalWindow {
private func addWindowDragHandle(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 = windowDragHandle {
view.removeFromSuperview()
view.isHidden = false
titlebarView.superview?.addSubview(view)
view.leftAnchor.constraint(equalTo: toolbarView.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: toolbarView.rightAnchor).isActive = true
view.topAnchor.constraint(equalTo: toolbarView.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: toolbarView.topAnchor, constant: 12).isActive = true
guard windowDragHandle?.superview != titlebarView.superview else {
// similar to `addWindowButtonsBackdrop`
return
}
windowDragHandle?.removeFromSuperview()
let view = WindowDragView()
view.identifier = NSUserInterfaceItemIdentifier("_windowDragHandle")
@ -540,7 +535,10 @@ fileprivate class WindowButtonsBackdropView: NSView {
// This must be weak because the window has this view. Otherwise
// a retain cycle occurs.
private weak var terminalWindow: TitlebarTabsVenturaTerminalWindow?
private let isLightTheme: Bool
private var isLightTheme: Bool {
// using up-to-date value from hosting window directly
terminalWindow?.isLightTheme ?? false
}
private let overlayLayer = VibrantLayer()
var isHighlighted: Bool = true {
@ -569,7 +567,6 @@ fileprivate class WindowButtonsBackdropView: NSView {
init(window: TitlebarTabsVenturaTerminalWindow) {
self.terminalWindow = window
self.isLightTheme = window.isLightTheme
super.init(frame: .zero)