ghostty/macos/Sources/Features/Terminal/Window Styles/TitlebarTabsTahoeTerminalWindow.swift
2025-06-12 13:39:19 -07:00

96 lines
2.8 KiB
Swift

import AppKit
import SwiftUI
/// `macos-titlebar-style = tabs` for macOS 26 (Tahoe) and later.
class TitlebarTabsTahoeTerminalWindow: TerminalWindow, NSToolbarDelegate {
/// The view model for SwiftUI views
private var viewModel = ViewModel()
override func awakeFromNib() {
super.awakeFromNib()
// We must hide the title since we're going to be moving tabs into
// the titlebar which have their own title.
titleVisibility = .hidden
// Create a toolbar
let toolbar = NSToolbar(identifier: "TerminalToolbar")
toolbar.delegate = self
toolbar.centeredItemIdentifiers.insert(.title)
self.toolbar = toolbar
toolbarStyle = .unifiedCompact
}
// MARK: NSWindow
override var title: String {
didSet {
viewModel.title = title
}
}
override func update() {
super.update()
if let glass = titlebarContainer?.firstDescendant(withClassName: "NSGlassContainerView") {
glass.isHidden = true
}
}
// MARK: NSToolbarDelegate
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.title, .flexibleSpace, .space]
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [.flexibleSpace, .title, .flexibleSpace]
}
func toolbar(_ toolbar: NSToolbar,
itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
switch itemIdentifier {
case .title:
let item = NSToolbarItem(itemIdentifier: .title)
item.view = NSHostingView(rootView: TitleItem(viewModel: viewModel))
item.visibilityPriority = .user
item.isEnabled = true
return item
default:
return NSToolbarItem(itemIdentifier: itemIdentifier)
}
}
// MARK: SwiftUI
class ViewModel: ObservableObject {
@Published var title: String = "👻 Ghostty"
}
}
extension NSToolbarItem.Identifier {
/// Displays the title of the window
static let title = NSToolbarItem.Identifier("Title")
}
extension TitlebarTabsTahoeTerminalWindow {
/// Displays the window title
struct TitleItem: View {
@ObservedObject var viewModel: ViewModel
var title: String {
// An empty title makes this view zero-sized and NSToolbar on macOS
// tahoe just deletes the item when that happens. So we use a space
// instead to ensure there's always some size.
viewModel.title.isEmpty ? " " : viewModel.title
}
var body: some View {
Text(title)
.lineLimit(1)
.truncationMode(.tail)
}
}
}