Move un-zoom button into the tab/toolbar

This commit is contained in:
Pete Schaffner
2024-02-16 16:35:42 +01:00
parent a0c43a8566
commit 472a5c93ad
3 changed files with 109 additions and 37 deletions

View File

@ -3,6 +3,31 @@ import Cocoa
import SwiftUI import SwiftUI
import GhosttyKit import GhosttyKit
fileprivate class ZoomButtonView: NSView {
let target: Any
let action: Selector
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(frame frameRect: NSRect, target: Any, selector: Selector) {
self.target = target
self.action = selector
super.init(frame: frameRect)
let zoomButton = NSButton(image: NSImage(systemSymbolName: "arrow.down.right.and.arrow.up.left.square.fill", accessibilityDescription: nil)!, target: target, action: selector)
zoomButton.frame = bounds
zoomButton.isBordered = false
zoomButton.contentTintColor = .systemBlue
zoomButton.state = .on
zoomButton.imageScaling = .scaleProportionallyUpOrDown
addSubview(zoomButton)
}
}
/// The terminal controller is an NSWindowController that maps 1:1 to a terminal window. /// The terminal controller is an NSWindowController that maps 1:1 to a terminal window.
class TerminalController: NSWindowController, NSWindowDelegate, class TerminalController: NSWindowController, NSWindowDelegate,
TerminalViewDelegate, TerminalViewModel, TerminalViewDelegate, TerminalViewModel,
@ -55,6 +80,8 @@ class TerminalController: NSWindowController, NSWindowDelegate,
/// For example, terminals executing custom scripts are not restorable. /// For example, terminals executing custom scripts are not restorable.
private var restorable: Bool = true private var restorable: Bool = true
private var surfaceIsZoomed: Bool = false
init(_ ghostty: Ghostty.App, init(_ ghostty: Ghostty.App,
withBaseConfig base: Ghostty.SurfaceConfiguration? = nil, withBaseConfig base: Ghostty.SurfaceConfiguration? = nil,
withSurfaceTree tree: Ghostty.SplitNode? = nil withSurfaceTree tree: Ghostty.SplitNode? = nil
@ -141,7 +168,22 @@ class TerminalController: NSWindowController, NSWindowDelegate,
let text = NSTextField(labelWithAttributedString: attributedString) let text = NSTextField(labelWithAttributedString: attributedString)
text.setContentCompressionResistancePriority(.windowSizeStayPut, for: .horizontal) text.setContentCompressionResistancePriority(.windowSizeStayPut, for: .horizontal)
text.postsFrameChangedNotifications = true text.postsFrameChangedNotifications = true
window.tab.accessoryView = text
let stackView = NSStackView(views: [text])
// stackView.setHuggingPriority(.defaultHigh, for: .horizontal)
window.tab.accessoryView = stackView
}
if surfaceIsZoomed {
guard let stackView = window?.tabGroup?.selectedWindow?.tab.accessoryView as? NSStackView else { return }
var zoomButton: ZoomButtonView = ZoomButtonView(frame: NSRect(x: 0, y: 0, width: 20, height: 20), target: self, selector: #selector(splitZoom(_:)))
zoomButton.translatesAutoresizingMaskIntoConstraints = false
zoomButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
zoomButton.heightAnchor.constraint(equalToConstant: 20).isActive = true
stackView.addArrangedSubview(zoomButton)
} }
} }
@ -201,6 +243,16 @@ class TerminalController: NSWindowController, NSWindowDelegate,
} }
} }
func windowDidUpdate(_ notification: Notification) {
updateToolbarZoomButton()
}
private func updateToolbarZoomButton() {
guard let itemView = window?.toolbar?.items.last?.view as? ZoomButtonView else { return }
itemView.alphaValue = surfaceIsZoomed ? 1 : 0
}
//MARK: - NSWindowController //MARK: - NSWindowController
override func windowWillLoad() { override func windowWillLoad() {
@ -305,6 +357,11 @@ class TerminalController: NSWindowController, NSWindowDelegate,
window.tabGroup?.removeWindow(window) window.tabGroup?.removeWindow(window)
} }
} }
guard let toolbarZoomItem = window.toolbar?.items.last else { return }
var zoomButton: ZoomButtonView = ZoomButtonView(frame: NSRect(x: 0, y: 0, width: 20, height: 20), target: self, selector: #selector(splitZoom(_:)))
toolbarZoomItem.view = zoomButton
} }
// Shows the "+" button in the tab bar, responds to that click. // Shows the "+" button in the tab bar, responds to that click.
@ -595,6 +652,12 @@ class TerminalController: NSWindowController, NSWindowDelegate,
invalidateRestorableState() invalidateRestorableState()
} }
func zoomStateDidChange(to: Bool) {
self.surfaceIsZoomed = to
updateToolbarZoomButton()
relabelTabs()
}
//MARK: - Clipboard Confirmation //MARK: - Clipboard Confirmation
func clipboardConfirmationComplete(_ action: ClipboardConfirmationView.Action, _ request: Ghostty.ClipboardRequest) { func clipboardConfirmationComplete(_ action: ClipboardConfirmationView.Action, _ request: Ghostty.ClipboardRequest) {

View File

@ -1,5 +1,9 @@
import Cocoa import Cocoa
fileprivate extension NSToolbarItem.Identifier {
static let zoom = NSToolbarItem.Identifier("zoom")
}
// Custom NSToolbar subclass that displays a centered window title, // Custom NSToolbar subclass that displays a centered window title,
// in order to accommodate the titlebar tabs feature. // in order to accommodate the titlebar tabs feature.
class TerminalToolbar: NSToolbar, NSToolbarDelegate { class TerminalToolbar: NSToolbar, NSToolbarDelegate {
@ -31,13 +35,13 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
func toolbar(_ toolbar: NSToolbar, func toolbar(_ toolbar: NSToolbar,
itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
guard itemIdentifier == Self.identifier else { var item: NSToolbarItem
return NSToolbarItem(itemIdentifier: itemIdentifier)
}
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier) switch itemIdentifier {
toolbarItem.view = self.titleTextField case Self.identifier:
toolbarItem.visibilityPriority = .user item = NSToolbarItem(itemIdentifier: itemIdentifier)
item.view = self.titleTextField
item.visibilityPriority = .user
// NSToolbarItem.minSize and NSToolbarItem.maxSize are deprecated, and make big ugly // NSToolbarItem.minSize and NSToolbarItem.maxSize are deprecated, and make big ugly
// warnings in Xcode when you use them, but I cannot for the life of me figure out // warnings in Xcode when you use them, but I cannot for the life of me figure out
@ -47,16 +51,21 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
// whether I have constraints set on its width, height, or both :/ // whether I have constraints set on its width, height, or both :/
// //
// If someone can fix this so we don't have to use deprecated properties: Please do. // If someone can fix this so we don't have to use deprecated properties: Please do.
toolbarItem.minSize = NSSize(width: 32, height: 1) item.minSize = NSSize(width: 32, height: 1)
toolbarItem.maxSize = NSSize(width: 1024, height: self.titleTextField.intrinsicContentSize.height) item.maxSize = NSSize(width: 1024, height: self.titleTextField.intrinsicContentSize.height)
toolbarItem.isEnabled = true item.isEnabled = true
case .zoom:
item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier("zoom"))
default:
item = NSToolbarItem(itemIdentifier: itemIdentifier)
}
return toolbarItem return item
} }
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [Self.identifier, .space] return [Self.identifier, .space, .zoom]
} }
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
@ -64,7 +73,7 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
// getting smaller than the max size so starts clipping. Lucky for us, three of the // getting smaller than the max size so starts clipping. Lucky for us, three of the
// built-in spacers seems to exactly match the space on the left that's reserved for // built-in spacers seems to exactly match the space on the left that's reserved for
// the window buttons. // the window buttons.
return [Self.identifier, .space, .space, .space] return [Self.identifier, .space, .space, .zoom]
} }
} }

View File

@ -17,6 +17,8 @@ protocol TerminalViewDelegate: AnyObject {
/// The surface tree did change in some way, i.e. a split was added, removed, etc. This is /// The surface tree did change in some way, i.e. a split was added, removed, etc. This is
/// not called initially. /// not called initially.
func surfaceTreeDidChange() func surfaceTreeDidChange()
func zoomStateDidChange(to: Bool)
} }
// Default all the functions so they're optional // Default all the functions so they're optional
@ -24,6 +26,7 @@ extension TerminalViewDelegate {
func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) {} func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) {}
func titleDidChange(to: String) {} func titleDidChange(to: String) {}
func cellSizeDidChange(to: NSSize) {} func cellSizeDidChange(to: NSSize) {}
func zoomStateDidChange(to: Bool) {}
} }
/// The view model is a required implementation for TerminalView callers. This contains /// The view model is a required implementation for TerminalView callers. This contains
@ -64,12 +67,6 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
} }
} }
if let zoomedSplit = zoomedSplit {
if zoomedSplit {
title = "🔍 " + title
}
}
return title return title
} }
@ -107,6 +104,9 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
// in the hash value. // in the hash value.
self.delegate?.surfaceTreeDidChange() self.delegate?.surfaceTreeDidChange()
} }
.onChange(of: zoomedSplit) { newValue in
self.delegate?.zoomStateDidChange(to: newValue ?? false)
}
} }
} }
} }