macOS: center window title when titlebar tabs enabled

Uses a custom toolbar that populates itself with a centered text field and provides a method to set the text.
This commit is contained in:
Qwerasd
2024-01-31 16:16:41 -05:00
parent 1a3d2d151e
commit 308f8cce36
4 changed files with 79 additions and 8 deletions

View File

@ -65,6 +65,7 @@
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */; };
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */; };
A5FEB3002ABB69450068369E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FEB2FF2ABB69450068369E /* main.swift */; };
AEF9CE242B6AD07A0017E195 /* TerminalToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEF9CE232B6AD07A0017E195 /* TerminalToolbar.swift */; };
C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
/* End PBXBuildFile section */
@ -125,6 +126,7 @@
A5E112942AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationController.swift; sourceTree = "<group>"; };
A5E112962AF7401B00C6E0C2 /* ClipboardConfirmationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipboardConfirmationView.swift; sourceTree = "<group>"; };
A5FEB2FF2ABB69450068369E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
AEF9CE232B6AD07A0017E195 /* TerminalToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalToolbar.swift; sourceTree = "<group>"; };
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OSColor+Extension.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -281,6 +283,7 @@
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */,
A596309D2AEE1D6C00D64628 /* TerminalView.swift */,
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */,
AEF9CE232B6AD07A0017E195 /* TerminalToolbar.swift */,
A535B9D9299C569B0017E2E4 /* ErrorView.swift */,
);
path = Terminal;
@ -493,6 +496,7 @@
A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */,
8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */,
A596309E2AEE1D6C00D64628 /* TerminalView.swift in Sources */,
AEF9CE242B6AD07A0017E195 /* TerminalToolbar.swift in Sources */,
C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */,
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */,
A5E112972AF7401B00C6E0C2 /* ClipboardConfirmationView.swift in Sources */,

View File

@ -482,7 +482,13 @@ class TerminalController: NSWindowController, NSWindowDelegate,
}
func titleDidChange(to: String) {
self.window?.title = to
guard let window = window as? TerminalWindow else { return }
window.title = to
// Custom toolbar-based title used when titlebar tabs are enabled.
guard let toolbar = window.toolbar as? TerminalToolbar else { return }
toolbar.setTitleText(to)
}
func cellSizeDidChange(to: NSSize) {

View File

@ -0,0 +1,44 @@
// Custom NSToolbar subclass that displays a centered window title,
// in order to accommodate the titlebar tabs feature.
import Foundation
import Cocoa
import SwiftUI
class TerminalToolbar: NSToolbar, NSToolbarDelegate {
static private let TitleIdentifier = NSToolbarItem.Identifier("TitleText")
private let TitleTextField = NSTextField(
labelWithString: "👻 Ghostty"
)
func setTitleText(_ text: String) {
self.TitleTextField.stringValue = text
}
override init(identifier: NSToolbar.Identifier) {
super.init(identifier: identifier)
delegate = self
if #available(macOS 13.0, *) {
centeredItemIdentifiers.insert(Self.TitleIdentifier)
} else {
centeredItemIdentifier = Self.TitleIdentifier
}
}
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
guard itemIdentifier == Self.TitleIdentifier else { return nil }
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
toolbarItem.isEnabled = true
toolbarItem.view = self.TitleTextField
return toolbarItem
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [Self.TitleIdentifier]
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [Self.TitleIdentifier]
}
}

View File

@ -42,8 +42,12 @@ class TerminalWindow: NSWindow {
// so we make sure it's the right size/position, and exists.
self.toolbarStyle = .unifiedCompact
if (self.toolbar == nil) {
self.toolbar = NSToolbar(identifier: "Toolbar")
self.toolbar = TerminalToolbar(identifier: "Toolbar")
}
// We directly hide the view containing the title text because if we use the
// `titleVisibility` property for this it prevents the window from hiding the
// tab bar when we get down to a single tab.
self.hideTitleText()
} else {
// "expanded" places the toolbar below the titlebar, so setting this style and
// removing the toolbar ensures that the titlebar will be the default height.
@ -62,6 +66,21 @@ class TerminalWindow: NSWindow {
titlebarContainer.layer?.backgroundColor = color
}
// Directly hide the view containing the title text
func hideTitleText() {
guard let toolbarTitleView = contentView?.superview?.subviews.first(where: {
$0.className == "NSTitlebarContainerView"
})?.subviews.first(where: {
$0.className == "NSTitlebarView"
})?.subviews.first(where: {
$0.className == "NSToolbarView"
})?.subviews.first(where: {
$0.className == "NSToolbarTitleView"
}) else { return }
toolbarTitleView.isHidden = true
}
// This is called by macOS for native tabbing in order to add the tab bar. We hook into
// this, detect the tab bar being added, and override its behavior.
override func addTitlebarAccessoryViewController(_ childViewController: NSTitlebarAccessoryViewController) {
@ -74,7 +93,8 @@ class TerminalWindow: NSWindow {
// Ensure it has the right layoutAttribute to force it next to our titlebar
childViewController.layoutAttribute = .right
// Hide the title text if the tab bar is showing since we show it in the tab
// If we don't set titleVisibility to hidden here, the toolbar will display a
// "collapsed items" indicator which interferes with the tab bar.
titleVisibility = .hidden
// Mark the controller for future reference so we can easily find it. Otherwise
@ -90,9 +110,9 @@ class TerminalWindow: NSWindow {
}
override func removeTitlebarAccessoryViewController(at index: Int) {
let childViewController = titlebarAccessoryViewControllers[index]
let isTabBar = titlebarAccessoryViewControllers[index].identifier == Self.TabBarController
super.removeTitlebarAccessoryViewController(at: index)
if (childViewController.identifier == Self.TabBarController) {
if (isTabBar) {
hideCustomTabBarViews()
}
}
@ -104,9 +124,6 @@ class TerminalWindow: NSWindow {
// Hide the window drag handle.
windowDragHandle?.isHidden = true
// Enable the window title text.
titleVisibility = .visible
}
private func pushTabsToTitlebar(_ tabBarController: NSTitlebarAccessoryViewController) {