macos: titlebar fonts work with new terminal window

pull/7588/head
Mitchell Hashimoto 2025-06-11 15:08:12 -07:00
parent a804dab288
commit 63e56d0402
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
4 changed files with 72 additions and 69 deletions

View File

@ -471,6 +471,17 @@ class TerminalController: BaseTerminalController {
private func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) { private func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {
// Let our window handle its own appearance // Let our window handle its own appearance
if let window = window as? TerminalWindow { if let window = window as? TerminalWindow {
// Sync our zoom state for splits
window.surfaceIsZoomed2 = surfaceTree.zoomed != nil
// Set the font for the window and tab titles.
if let titleFontName = surfaceConfig.windowTitleFontFamily {
window.titlebarFont2 = NSFont(name: titleFontName, size: NSFont.systemFontSize)
} else {
window.titlebarFont2 = nil
}
// Call this last in case it uses any of the properties above.
window.syncAppearance(surfaceConfig) window.syncAppearance(surfaceConfig)
} }

View File

@ -77,48 +77,6 @@ class LegacyTerminalWindow: TerminalWindow {
bindings.forEach() { $0.invalidate() } bindings.forEach() { $0.invalidate() }
} }
// MARK: Titlebar Helpers
// These helpers are generic to what we're trying to achieve (i.e. titlebar
// style tabs, titlebar styling, etc.). They're just here to make it easier.
private var titlebarContainer: NSView? {
// If we aren't fullscreen then the titlebar container is part of our window.
if !styleMask.contains(.fullScreen) {
guard let view = contentView?.superview ?? contentView else { return nil }
return titlebarContainerView(in: view)
}
// If we are fullscreen, the titlebar container view is part of a separate
// "fullscreen window", we need to find the window and then get the view.
for window in NSApplication.shared.windows {
// This is the private window class that contains the toolbar
guard window.className == "NSToolbarFullScreenWindow" else { continue }
// The parent will match our window. This is used to filter the correct
// fullscreen window if we have multiple.
guard window.parent == self else { continue }
guard let view = window.contentView else { continue }
return titlebarContainerView(in: view)
}
return nil
}
private func titlebarContainerView(in view: NSView) -> NSView? {
if view.className == "NSTitlebarContainerView" {
return view
}
for subview in view.subviews {
if let found = titlebarContainerView(in: subview) {
return found
}
}
return nil
}
// MARK: - NSWindow // MARK: - NSWindow
override var title: String { override var title: String {

View File

@ -72,7 +72,7 @@ class TerminalWindow: NSWindow {
override func mergeAllWindows(_ sender: Any?) { override func mergeAllWindows(_ sender: Any?) {
super.mergeAllWindows(sender) super.mergeAllWindows(sender)
// It takes an event loop cycle to merge all the windows so we set a // It takes an event loop cycle to merge all the windows so we set a
// short timer to relabel the tabs (issue #1902) // short timer to relabel the tabs (issue #1902)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
@ -139,6 +139,66 @@ class TerminalWindow: NSWindow {
return button return button
} }
// MARK: Title Text
override var title: String {
didSet {
// Whenever we change the window title we must also update our
// tab title if we're using custom fonts.
tab.attributedTitle = attributedTitle
}
}
// Used to set the titlebar font.
var titlebarFont2: NSFont? {
didSet {
let font = titlebarFont2 ?? NSFont.titleBarFont(ofSize: NSFont.systemFontSize)
titlebarTextField?.font = font
tab.attributedTitle = attributedTitle
}
}
// Find the NSTextField responsible for displaying the titlebar's title.
private var titlebarTextField: NSTextField? {
titlebarContainer?
.firstDescendant(withClassName: "NSTitlebarView")?
.firstDescendant(withClassName: "NSTextField") as? NSTextField
}
// Return a styled representation of our title property.
private var attributedTitle: NSAttributedString? {
guard let titlebarFont = titlebarFont2 else { return nil }
let attributes: [NSAttributedString.Key: Any] = [
.font: titlebarFont,
.foregroundColor: isKeyWindow ? NSColor.labelColor : NSColor.secondaryLabelColor,
]
return NSAttributedString(string: title, attributes: attributes)
}
var titlebarContainer: NSView? {
// If we aren't fullscreen then the titlebar container is part of our window.
if !styleMask.contains(.fullScreen) {
return contentView?.firstViewFromRoot(withClassName: "NSTitlebarContainerView")
}
// If we are fullscreen, the titlebar container view is part of a separate
// "fullscreen window", we need to find the window and then get the view.
for window in NSApplication.shared.windows {
// This is the private window class that contains the toolbar
guard window.className == "NSToolbarFullScreenWindow" else { continue }
// The parent will match our window. This is used to filter the correct
// fullscreen window if we have multiple.
guard window.parent == self else { continue }
return window.contentView?.firstViewFromRoot(withClassName: "NSTitlebarContainerView")
}
return nil
}
// MARK: Positioning And Styling // MARK: Positioning And Styling
/// This is called by the controller when there is a need to reset the window apperance. /// This is called by the controller when there is a need to reset the window apperance.

View File

@ -94,32 +94,6 @@ class TransparentTitlebarTerminalWindow: TerminalWindow {
titlebarContainer?.firstDescendant(withClassName: "NSTitlebarBackgroundView") titlebarContainer?.firstDescendant(withClassName: "NSTitlebarBackgroundView")
} }
private var titlebarContainer: NSView? {
// If we aren't fullscreen then the titlebar container is part of our window.
if !styleMask.contains(.fullScreen) {
return titlebarContainerView
}
// If we are fullscreen, the titlebar container view is part of a separate
// "fullscreen window", we need to find the window and then get the view.
for window in NSApplication.shared.windows {
// This is the private window class that contains the toolbar
guard window.className == "NSToolbarFullScreenWindow" else { continue }
// The parent will match our window. This is used to filter the correct
// fullscreen window if we have multiple.
guard window.parent == self else { continue }
return titlebarContainerView
}
return nil
}
private var titlebarContainerView: NSView? {
contentView?.firstViewFromRoot(withClassName: "NSTitlebarContainerView")
}
// MARK: Tab Group Observation // MARK: Tab Group Observation
private func setupKVO() { private func setupKVO() {