mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Move un-zoom button into the tab/toolbar
This commit is contained in:
@ -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) {
|
||||||
|
@ -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,32 +35,37 @@ 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)
|
|
||||||
|
switch itemIdentifier {
|
||||||
|
case Self.identifier:
|
||||||
|
item = NSToolbarItem(itemIdentifier: itemIdentifier)
|
||||||
|
item.view = self.titleTextField
|
||||||
|
item.visibilityPriority = .user
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// how to get this to work with constraints. The behavior isn't the same, instead of
|
||||||
|
// shrinking the item and clipping the subview, it hides the item as soon as the
|
||||||
|
// intrinsic size of the subview gets too big for the toolbar width, regardless of
|
||||||
|
// 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.
|
||||||
|
item.minSize = NSSize(width: 32, height: 1)
|
||||||
|
item.maxSize = NSSize(width: 1024, height: self.titleTextField.intrinsicContentSize.height)
|
||||||
|
|
||||||
|
item.isEnabled = true
|
||||||
|
case .zoom:
|
||||||
|
item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier("zoom"))
|
||||||
|
default:
|
||||||
|
item = NSToolbarItem(itemIdentifier: itemIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
|
return item
|
||||||
toolbarItem.view = self.titleTextField
|
|
||||||
toolbarItem.visibilityPriority = .user
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// how to get this to work with constraints. The behavior isn't the same, instead of
|
|
||||||
// shrinking the item and clipping the subview, it hides the item as soon as the
|
|
||||||
// intrinsic size of the subview gets too big for the toolbar width, regardless of
|
|
||||||
// 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.
|
|
||||||
toolbarItem.minSize = NSSize(width: 32, height: 1)
|
|
||||||
toolbarItem.maxSize = NSSize(width: 1024, height: self.titleTextField.intrinsicContentSize.height)
|
|
||||||
|
|
||||||
toolbarItem.isEnabled = true
|
|
||||||
|
|
||||||
return toolbarItem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user