mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: store default size as computed property
This commit is contained in:
@ -27,6 +27,9 @@ class TerminalController: BaseTerminalController {
|
|||||||
/// The notification cancellable for focused surface property changes.
|
/// The notification cancellable for focused surface property changes.
|
||||||
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
/// This will be set to the initial frame of the window from the xib on load.
|
||||||
|
private var initialFrame: NSRect? = nil
|
||||||
|
|
||||||
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
|
||||||
@ -308,6 +311,55 @@ class TerminalController: BaseTerminalController {
|
|||||||
y: frame.maxY - (CGFloat(y) + window.frame.height)))
|
y: frame.maxY - (CGFloat(y) + window.frame.height)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the default size of the window. This is contextual based on the focused surface because
|
||||||
|
/// the focused surface may specify a different default size than others.
|
||||||
|
private var defaultSize: NSRect? {
|
||||||
|
guard let screen = window?.screen ?? NSScreen.main else { return nil }
|
||||||
|
|
||||||
|
if derivedConfig.maximize {
|
||||||
|
return screen.visibleFrame
|
||||||
|
} else if let focusedSurface,
|
||||||
|
let initialSize = focusedSurface.initialSize {
|
||||||
|
// Get the current frame of the window
|
||||||
|
guard var frame = window?.frame else { return nil }
|
||||||
|
|
||||||
|
// Calculate the chrome size (window size minus view size)
|
||||||
|
let chromeWidth = frame.size.width - focusedSurface.frame.size.width
|
||||||
|
let chromeHeight = frame.size.height - focusedSurface.frame.size.height
|
||||||
|
|
||||||
|
// Calculate the new width and height, clamping to the screen's size
|
||||||
|
let newWidth = min(initialSize.width + chromeWidth, screen.visibleFrame.width)
|
||||||
|
let newHeight = min(initialSize.height + chromeHeight, screen.visibleFrame.height)
|
||||||
|
|
||||||
|
// Update the frame size while keeping the window's position intact
|
||||||
|
frame.size.width = newWidth
|
||||||
|
frame.size.height = newHeight
|
||||||
|
|
||||||
|
// Ensure the window doesn't go outside the screen boundaries
|
||||||
|
frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth))
|
||||||
|
frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight))
|
||||||
|
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let initialFrame else { return nil }
|
||||||
|
guard var frame = window?.frame else { return nil }
|
||||||
|
|
||||||
|
// Calculate the new width and height, clamping to the screen's size
|
||||||
|
let newWidth = min(initialFrame.size.width, screen.visibleFrame.width)
|
||||||
|
let newHeight = min(initialFrame.size.height, screen.visibleFrame.height)
|
||||||
|
|
||||||
|
// Update the frame size while keeping the window's position intact
|
||||||
|
frame.size.width = newWidth
|
||||||
|
frame.size.height = newHeight
|
||||||
|
|
||||||
|
// Ensure the window doesn't go outside the screen boundaries
|
||||||
|
frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth))
|
||||||
|
frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight))
|
||||||
|
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - NSWindowController
|
//MARK: - NSWindowController
|
||||||
|
|
||||||
override func windowWillLoad() {
|
override func windowWillLoad() {
|
||||||
@ -356,6 +408,9 @@ class TerminalController: BaseTerminalController {
|
|||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
guard let window = window as? TerminalWindow else { return }
|
guard let window = window as? TerminalWindow else { return }
|
||||||
|
|
||||||
|
// Store our initial frame so we can know our default later.
|
||||||
|
initialFrame = window.frame
|
||||||
|
|
||||||
// I copy this because we may change the source in the future but also because
|
// I copy this because we may change the source in the future but also because
|
||||||
// I regularly audit our codebase for "ghostty.config" access because generally
|
// I regularly audit our codebase for "ghostty.config" access because generally
|
||||||
// you shouldn't use it. Its safe in this case because for a new window we should
|
// you shouldn't use it. Its safe in this case because for a new window we should
|
||||||
@ -372,36 +427,15 @@ class TerminalController: BaseTerminalController {
|
|||||||
// If window decorations are disabled, remove our title
|
// If window decorations are disabled, remove our title
|
||||||
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||||
|
|
||||||
// If we have only a single surface (no splits) and that surface requested
|
// If we have only a single surface (no splits) and there is a default size then
|
||||||
// an initial size then we set it here now.
|
// we should resize to that default size.
|
||||||
if case let .leaf(leaf) = surfaceTree {
|
if case let .leaf(leaf) = surfaceTree {
|
||||||
if config.maximize {
|
// If this is our first surface then our focused surface will be nil
|
||||||
if let screen = window.screen ?? NSScreen.main {
|
// so we force the focused surface to the leaf.
|
||||||
window.setFrame(screen.visibleFrame, display: true)
|
focusedSurface = leaf.surface
|
||||||
}
|
|
||||||
} else if let initialSize = leaf.surface.initialSize,
|
|
||||||
let screen = window.screen ?? NSScreen.main {
|
|
||||||
// Get the current frame of the window
|
|
||||||
var frame = window.frame
|
|
||||||
|
|
||||||
// Calculate the chrome size (window size minus view size)
|
if let defaultSize {
|
||||||
let chromeWidth = frame.size.width - leaf.surface.frame.size.width
|
window.setFrame(defaultSize, display: true)
|
||||||
let chromeHeight = frame.size.height - leaf.surface.frame.size.height
|
|
||||||
|
|
||||||
// Calculate the new width and height, clamping to the screen's size
|
|
||||||
let newWidth = min(initialSize.width + chromeWidth, screen.visibleFrame.width)
|
|
||||||
let newHeight = min(initialSize.height + chromeHeight, screen.visibleFrame.height)
|
|
||||||
|
|
||||||
// Update the frame size while keeping the window's position intact
|
|
||||||
frame.size.width = newWidth
|
|
||||||
frame.size.height = newHeight
|
|
||||||
|
|
||||||
// Ensure the window doesn't go outside the screen boundaries
|
|
||||||
frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth))
|
|
||||||
frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight))
|
|
||||||
|
|
||||||
// Set the updated frame to the window
|
|
||||||
window.setFrame(frame, display: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,20 +613,8 @@ class TerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func returnToDefaultSize(_ sender: Any) {
|
@IBAction func returnToDefaultSize(_ sender: Any) {
|
||||||
guard
|
guard let defaultSize else { return }
|
||||||
let window = NSApp.keyWindow,
|
window?.setFrame(defaultSize, display: true)
|
||||||
let initialWidth = ghostty.config.windowWidth,
|
|
||||||
let initialHeight = ghostty.config.windowHeight
|
|
||||||
else { return }
|
|
||||||
let currentOrigin = window.frame.origin
|
|
||||||
let newFrame = NSRect(
|
|
||||||
origin: currentOrigin,
|
|
||||||
size: .init(
|
|
||||||
width: CGFloat(initialWidth),
|
|
||||||
height: CGFloat(initialHeight)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
window.setFrame(newFrame, display: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction override func closeWindow(_ sender: Any?) {
|
@IBAction override func closeWindow(_ sender: Any?) {
|
||||||
@ -828,15 +850,18 @@ class TerminalController: BaseTerminalController {
|
|||||||
struct DerivedConfig {
|
struct DerivedConfig {
|
||||||
let backgroundColor: Color
|
let backgroundColor: Color
|
||||||
let macosTitlebarStyle: String
|
let macosTitlebarStyle: String
|
||||||
|
let maximize: Bool
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.backgroundColor = Color(NSColor.windowBackgroundColor)
|
self.backgroundColor = Color(NSColor.windowBackgroundColor)
|
||||||
self.macosTitlebarStyle = "system"
|
self.macosTitlebarStyle = "system"
|
||||||
|
self.maximize = false
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ config: Ghostty.Config) {
|
init(_ config: Ghostty.Config) {
|
||||||
self.backgroundColor = config.backgroundColor
|
self.backgroundColor = config.backgroundColor
|
||||||
self.macosTitlebarStyle = config.macosTitlebarStyle
|
self.macosTitlebarStyle = config.macosTitlebarStyle
|
||||||
|
self.maximize = config.maximize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -846,23 +871,31 @@ extension TerminalController: NSMenuItemValidation {
|
|||||||
func validateMenuItem(_ item: NSMenuItem) -> Bool {
|
func validateMenuItem(_ item: NSMenuItem) -> Bool {
|
||||||
switch item.action {
|
switch item.action {
|
||||||
case #selector(returnToDefaultSize):
|
case #selector(returnToDefaultSize):
|
||||||
guard let focusedWindow = NSApp.keyWindow else {
|
guard let window else { return false }
|
||||||
|
|
||||||
|
// Native fullscreen windows can't revert to default size.
|
||||||
|
if window.styleMask.contains(.fullScreen) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if focusedWindow.styleMask.contains(.fullScreen) {
|
|
||||||
|
// If we're fullscreen at all then we can't change size
|
||||||
|
if fullscreenStyle?.isFullscreen ?? false {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
guard
|
|
||||||
let windowWidth = ghostty.config.windowWidth,
|
// If our window is already the default size or we don't have a
|
||||||
let windowHeight = ghostty.config.windowHeight,
|
// default size, then disable.
|
||||||
focusedWindow.frame.size != .init(
|
guard let defaultSize,
|
||||||
width: CGFloat(windowWidth),
|
window.frame.size != .init(
|
||||||
height: CGFloat(windowHeight)
|
width: defaultSize.size.width,
|
||||||
|
height: defaultSize.size.height
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -141,20 +141,6 @@ extension Ghostty {
|
|||||||
return String(cString: ptr)
|
return String(cString: ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowHeight: Int16? {
|
|
||||||
guard let config = self.config else { return nil }
|
|
||||||
var v: Int16 = 0
|
|
||||||
let key = "window-height"
|
|
||||||
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var windowWidth: Int16? {
|
|
||||||
guard let config = self.config else { return nil }
|
|
||||||
var v: Int16 = 0
|
|
||||||
let key = "window-width"
|
|
||||||
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var windowPositionX: Int16? {
|
var windowPositionX: Int16? {
|
||||||
guard let config = self.config else { return nil }
|
guard let config = self.config else { return nil }
|
||||||
var v: Int16 = 0
|
var v: Int16 = 0
|
||||||
|
Reference in New Issue
Block a user