mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 17:26:09 +03:00
Don't use a toolbar for the traditional title/tab bar
I forgot I can use a `NSTitlebarAccessoryViewController` to house the button
This commit is contained in:
15
macos/Assets.xcassets/ResetZoom.imageset/Contents.json
vendored
Normal file
15
macos/Assets.xcassets/ResetZoom.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "ResetZoom.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/ResetZoom.imageset/ResetZoom.pdf
vendored
Normal file
BIN
macos/Assets.xcassets/ResetZoom.imageset/ResetZoom.pdf
vendored
Normal file
Binary file not shown.
@ -15,8 +15,6 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasTitle: Bool = false
|
|
||||||
|
|
||||||
override init(identifier: NSToolbar.Identifier) {
|
override init(identifier: NSToolbar.Identifier) {
|
||||||
super.init(identifier: identifier)
|
super.init(identifier: identifier)
|
||||||
|
|
||||||
@ -54,24 +52,6 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
item.isEnabled = true
|
item.isEnabled = true
|
||||||
case .resetZoom:
|
case .resetZoom:
|
||||||
item = NSToolbarItem(itemIdentifier: .resetZoom)
|
item = NSToolbarItem(itemIdentifier: .resetZoom)
|
||||||
|
|
||||||
let view = NSView(frame: NSRect(x: 0, y: 0, width: 20, height: 20))
|
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.widthAnchor.constraint(equalToConstant: 20).isActive = true
|
|
||||||
view.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
|
||||||
|
|
||||||
let button = NSButton(image: NSImage(systemSymbolName: "arrow.down.right.and.arrow.up.left.square.fill", accessibilityDescription: nil)!, target: nil, action: #selector(TerminalController.splitZoom(_:)))
|
|
||||||
|
|
||||||
button.frame = view.bounds
|
|
||||||
button.isBordered = false
|
|
||||||
button.contentTintColor = .controlAccentColor
|
|
||||||
button.state = .on
|
|
||||||
button.imageScaling = .scaleProportionallyUpOrDown
|
|
||||||
button.allowsExpansionToolTips = true
|
|
||||||
button.toolTip = "Reset Zoom"
|
|
||||||
view.addSubview(button)
|
|
||||||
|
|
||||||
item.view = view
|
|
||||||
default:
|
default:
|
||||||
item = NSToolbarItem(itemIdentifier: itemIdentifier)
|
item = NSToolbarItem(itemIdentifier: itemIdentifier)
|
||||||
}
|
}
|
||||||
@ -88,11 +68,7 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
// getting smaller than the max size so starts clipping. Lucky for us, two of the
|
// getting smaller than the max size so starts clipping. Lucky for us, two of the
|
||||||
// built-in spacers plus the un-zoom button item seems to exactly match the space
|
// built-in spacers plus the un-zoom button item seems to exactly match the space
|
||||||
// on the left that's reserved for the window buttons.
|
// on the left that's reserved for the window buttons.
|
||||||
if hasTitle {
|
return [.titleText, .flexibleSpace, .space, .space, .resetZoom]
|
||||||
return [.titleText, .flexibleSpace, .space, .space, .resetZoom]
|
|
||||||
} else {
|
|
||||||
return [.flexibleSpace, .resetZoom]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,30 +15,27 @@ class TerminalWindow: NSWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var resetZoomToolbarButton: NSButton? {
|
private lazy var resetZoomToolbarButton: NSButton = generateResetZoomButton()
|
||||||
guard let button = toolbar?.items.first(where: { $0.itemIdentifier == .resetZoom })?.view?.subviews.first as? NSButton
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
return button
|
private lazy var resetZoomTabButton: NSButton = generateResetZoomButton()
|
||||||
}
|
|
||||||
|
|
||||||
private let resetZoomTabButton: NSButton = {
|
private lazy var resetZoomTitlebarAccessoryViewController: NSTitlebarAccessoryViewController? = {
|
||||||
let button = NSButton()
|
guard let titlebarContainer = contentView?.superview?.subviews.first(where: { $0.className == "NSTitlebarContainerView" }) else { return nil }
|
||||||
button.target = nil
|
|
||||||
button.action = #selector(TerminalController.splitZoom(_:))
|
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
button.widthAnchor.constraint(equalToConstant: 20).isActive = true
|
|
||||||
button.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
|
||||||
button.isBordered = false
|
|
||||||
button.allowsExpansionToolTips = true
|
|
||||||
button.toolTip = "Reset Zoom"
|
|
||||||
button.contentTintColor = .controlAccentColor
|
|
||||||
button.state = .on
|
|
||||||
button.image = NSImage(systemSymbolName: "arrow.down.right.and.arrow.up.left.square.fill", accessibilityDescription: nil)!
|
|
||||||
.withSymbolConfiguration(NSImage.SymbolConfiguration(scale: .large))
|
|
||||||
|
|
||||||
return button
|
let size = NSSize(width: titlebarContainer.bounds.height, height: titlebarContainer.bounds.height)
|
||||||
}()
|
let view = NSView(frame: NSRect(origin: .zero, size: size))
|
||||||
|
|
||||||
|
let button = generateResetZoomButton()
|
||||||
|
button.frame.origin.x = size.width/2 - button.bounds.width/2
|
||||||
|
button.frame.origin.y = size.height/2 - button.bounds.height/2
|
||||||
|
view.addSubview(button)
|
||||||
|
|
||||||
|
let titlebarAccessoryViewController = NSTitlebarAccessoryViewController()
|
||||||
|
titlebarAccessoryViewController.view = view
|
||||||
|
titlebarAccessoryViewController.layoutAttribute = .right
|
||||||
|
|
||||||
|
return titlebarAccessoryViewController
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var keyEquivalentLabel: NSTextField = {
|
private lazy var keyEquivalentLabel: NSTextField = {
|
||||||
let label = NSTextField(labelWithAttributedString: NSAttributedString())
|
let label = NSTextField(labelWithAttributedString: NSAttributedString())
|
||||||
@ -50,10 +47,10 @@ class TerminalWindow: NSWindow {
|
|||||||
|
|
||||||
private lazy var bindings = [
|
private lazy var bindings = [
|
||||||
observe(\.surfaceIsZoomed, options: [.initial, .new]) { [weak self] window, _ in
|
observe(\.surfaceIsZoomed, options: [.initial, .new]) { [weak self] window, _ in
|
||||||
guard let resetZoomToolbarButton = self?.resetZoomToolbarButton, let tabGroup = self?.tabGroup else { return }
|
guard let tabGroup = self?.tabGroup else { return }
|
||||||
|
|
||||||
self?.resetZoomTabButton.isHidden = !window.surfaceIsZoomed
|
self?.resetZoomTabButton.isHidden = !window.surfaceIsZoomed
|
||||||
self?.updateResetZoomToolbarButtonVisibility()
|
self?.updateResetZoomTitlebarButtonVisibility()
|
||||||
},
|
},
|
||||||
|
|
||||||
observe(\.keyEquivalent, options: [.initial, .new]) { [weak self] window, _ in
|
observe(\.keyEquivalent, options: [.initial, .new]) { [weak self] window, _ in
|
||||||
@ -77,6 +74,8 @@ class TerminalWindow: NSWindow {
|
|||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
_ = bindings
|
||||||
|
|
||||||
// By hiding the visual effect view, we allow the window's (or titlebar's in this case)
|
// 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
|
// 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
|
// the selected tab would look fine, but the unselected ones and new tab button backgrounds
|
||||||
@ -95,9 +94,9 @@ class TerminalWindow: NSWindow {
|
|||||||
stackView.spacing = 3
|
stackView.spacing = 3
|
||||||
tab.accessoryView = stackView
|
tab.accessoryView = stackView
|
||||||
|
|
||||||
generateToolbar()
|
if titlebarTabs {
|
||||||
|
generateToolbar()
|
||||||
_ = bindings
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -118,7 +117,7 @@ class TerminalWindow: NSWindow {
|
|||||||
updateNewTabButtonOpacity()
|
updateNewTabButtonOpacity()
|
||||||
resetZoomTabButton.isEnabled = true
|
resetZoomTabButton.isEnabled = true
|
||||||
resetZoomTabButton.contentTintColor = .controlAccentColor
|
resetZoomTabButton.contentTintColor = .controlAccentColor
|
||||||
resetZoomToolbarButton?.contentTintColor = .controlAccentColor
|
resetZoomToolbarButton.contentTintColor = .controlAccentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
override func resignKey() {
|
override func resignKey() {
|
||||||
@ -127,13 +126,13 @@ class TerminalWindow: NSWindow {
|
|||||||
updateNewTabButtonOpacity()
|
updateNewTabButtonOpacity()
|
||||||
resetZoomTabButton.isEnabled = false
|
resetZoomTabButton.isEnabled = false
|
||||||
resetZoomTabButton.contentTintColor = .labelColor
|
resetZoomTabButton.contentTintColor = .labelColor
|
||||||
resetZoomToolbarButton?.contentTintColor = .tertiaryLabelColor
|
resetZoomToolbarButton.contentTintColor = .tertiaryLabelColor
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update() {
|
override func update() {
|
||||||
super.update()
|
super.update()
|
||||||
|
|
||||||
updateResetZoomToolbarButtonVisibility()
|
updateResetZoomTitlebarButtonVisibility()
|
||||||
|
|
||||||
titlebarSeparatorStyle = tabbedWindows != nil && !titlebarTabs ? .line : .none
|
titlebarSeparatorStyle = tabbedWindows != nil && !titlebarTabs ? .line : .none
|
||||||
|
|
||||||
@ -204,14 +203,24 @@ class TerminalWindow: NSWindow {
|
|||||||
newTabButtonImageView.alphaValue = isKeyWindow ? 1 : 0.5
|
newTabButtonImageView.alphaValue = isKeyWindow ? 1 : 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateResetZoomToolbarButtonVisibility() {
|
private func updateResetZoomTitlebarButtonVisibility() {
|
||||||
guard let resetZoomToolbarButton = resetZoomToolbarButton, let tabGroup else { return }
|
guard let tabGroup, let resetZoomTitlebarAccessoryViewController else { return }
|
||||||
|
|
||||||
if tabGroup.isTabBarVisible {
|
let isHidden = tabGroup.isTabBarVisible ? true : !surfaceIsZoomed
|
||||||
resetZoomToolbarButton.isHidden = true
|
|
||||||
} else {
|
if titlebarTabs {
|
||||||
resetZoomToolbarButton.isHidden = !surfaceIsZoomed
|
resetZoomToolbarButton.isHidden = isHidden
|
||||||
}
|
|
||||||
|
for (index, vc) in titlebarAccessoryViewControllers.enumerated() {
|
||||||
|
guard vc == resetZoomTitlebarAccessoryViewController else { return }
|
||||||
|
removeTitlebarAccessoryViewController(at: index)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !titlebarAccessoryViewControllers.contains(resetZoomTitlebarAccessoryViewController) {
|
||||||
|
addTitlebarAccessoryViewController(resetZoomTitlebarAccessoryViewController)
|
||||||
|
}
|
||||||
|
resetZoomTitlebarAccessoryViewController.view.isHidden = isHidden
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to regenerate a toolbar when the titlebar tabs setting changes since our
|
// We have to regenerate a toolbar when the titlebar tabs setting changes since our
|
||||||
@ -220,20 +229,45 @@ class TerminalWindow: NSWindow {
|
|||||||
// isn't possible.
|
// isn't possible.
|
||||||
private func generateToolbar() {
|
private func generateToolbar() {
|
||||||
let terminalToolbar = TerminalToolbar(identifier: "Toolbar")
|
let terminalToolbar = TerminalToolbar(identifier: "Toolbar")
|
||||||
terminalToolbar.hasTitle = titlebarTabs
|
|
||||||
|
|
||||||
toolbar = terminalToolbar
|
toolbar = terminalToolbar
|
||||||
toolbarStyle = .unifiedCompact
|
toolbarStyle = .unifiedCompact
|
||||||
updateResetZoomToolbarButtonVisibility()
|
if let resetZoomItem = terminalToolbar.items.first(where: { $0.itemIdentifier == .resetZoom }) {
|
||||||
|
resetZoomItem.view = resetZoomToolbarButton
|
||||||
|
resetZoomItem.view?.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
resetZoomItem.view?.widthAnchor.constraint(equalToConstant: 22).isActive = true
|
||||||
|
resetZoomItem.view?.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||||
|
}
|
||||||
|
updateResetZoomTitlebarButtonVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func generateResetZoomButton() -> NSButton {
|
||||||
|
let button = NSButton()
|
||||||
|
button.target = nil
|
||||||
|
button.action = #selector(TerminalController.splitZoom(_:))
|
||||||
|
button.isBordered = false
|
||||||
|
button.allowsExpansionToolTips = true
|
||||||
|
button.toolTip = "Reset Zoom"
|
||||||
|
button.contentTintColor = .controlAccentColor
|
||||||
|
button.state = .on
|
||||||
|
button.image = NSImage(named:"ResetZoom")
|
||||||
|
button.frame = NSRect(x: 0, y: 0, width: 20, height: 20)
|
||||||
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
button.widthAnchor.constraint(equalToConstant: 20).isActive = true
|
||||||
|
button.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||||
|
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Titlebar Tabs
|
// MARK: - Titlebar Tabs
|
||||||
|
|
||||||
// Used by the window controller to enable/disable titlebar tabs.
|
// Used by the window controller to enable/disable titlebar tabs.
|
||||||
var titlebarTabs = false {
|
var titlebarTabs = false {
|
||||||
didSet {
|
didSet {
|
||||||
self.titleVisibility = titlebarTabs ? .hidden : .visible
|
self.titleVisibility = titlebarTabs ? .hidden : .visible
|
||||||
generateToolbar()
|
if titlebarTabs {
|
||||||
|
generateToolbar()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user