ghostty/macos/Sources/Features/QuickTerminal/QuickTerminalPosition.swift
Friedrich Stoltzfus 1dab2d456e macOS: Add support for quick terminal sizing configuration
Added C bindings for the already existing quick-terminal-size
configuration. Created a new QuickTerminalSize struct to hold these
values in Swift. Updated the QuickTerminal implementation to use the
user's configuration if supplied. Retains defaults. Also adds support to
customize the width of the quick terminal (height if quick terminal is
set to right or left).
2025-06-30 10:13:53 -07:00

126 lines
5.0 KiB
Swift

import Cocoa
enum QuickTerminalPosition : String {
case top
case bottom
case left
case right
case center
/// Set the loaded state for a window.
func setLoaded(_ window: NSWindow, size: QuickTerminalSize) {
guard let screen = window.screen ?? NSScreen.main else { return }
let dimensions = size.calculate(position: self, screenDimensions: screen.frame.size)
window.setFrame(.init(
origin: window.frame.origin,
size: .init(
width: dimensions.width,
height: dimensions.height)
), display: false)
}
/// Set the initial state for a window for animating out of this position.
func setInitial(in window: NSWindow, on screen: NSScreen, terminalSize: QuickTerminalSize) {
// We always start invisible
window.alphaValue = 0
// Position depends
window.setFrame(.init(
origin: initialOrigin(for: window, on: screen),
size: restrictFrameSize(window.frame.size, on: screen, terminalSize: terminalSize)
), display: false)
}
/// Set the final state for a window in this position.
func setFinal(in window: NSWindow, on screen: NSScreen, terminalSize: QuickTerminalSize) {
// We always end visible
window.alphaValue = 1
// Position depends
window.setFrame(.init(
origin: finalOrigin(for: window, on: screen),
size: restrictFrameSize(window.frame.size, on: screen, terminalSize: terminalSize)
), display: true)
}
/// Restrict the frame size during resizing.
func restrictFrameSize(_ size: NSSize, on screen: NSScreen, terminalSize: QuickTerminalSize) -> NSSize {
var finalSize = size
let dimensions = terminalSize.calculate(position: self, screenDimensions: screen.frame.size)
switch (self) {
case .top, .bottom:
finalSize.width = dimensions.width
finalSize.height = dimensions.height
case .left, .right:
finalSize.width = dimensions.width
finalSize.height = dimensions.height
case .center:
finalSize.width = dimensions.width
finalSize.height = dimensions.height
}
return finalSize
}
/// The initial point origin for this position.
func initialOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
switch (self) {
case .top:
return .init(x: screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2, y: screen.frame.maxY)
case .bottom:
return .init(x: screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2, y: -window.frame.height)
case .left:
return .init(x: screen.frame.minX-window.frame.width, y: screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2)
case .right:
return .init(x: screen.frame.maxX, y: screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2)
case .center:
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.height - window.frame.width)
}
}
/// The final point origin for this position.
func finalOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
switch (self) {
case .top:
return .init(x: screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2, y: screen.visibleFrame.maxY - window.frame.height)
case .bottom:
return .init(x: screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2, y: screen.frame.minY)
case .left:
return .init(x: screen.frame.minX, y: screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2)
case .right:
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2)
case .center:
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
}
}
func conflictsWithDock(on screen: NSScreen) -> Bool {
// Screen must have a dock for it to conflict
guard screen.hasDock else { return false }
// Get the dock orientation for this screen
guard let orientation = Dock.orientation else { return false }
// Depending on the orientation of the dock, we conflict if our quick terminal
// would potentially "hit" the dock. In the future we should probably consider
// the frame of the quick terminal.
return switch (orientation) {
case .top: self == .top || self == .left || self == .right
case .bottom: self == .bottom || self == .left || self == .right
case .left: self == .top || self == .bottom
case .right: self == .top || self == .bottom
}
}
}