macos: window-position-x/y works with window-width/height (#9313)

Fixes #9132

We were processing our window size defaults separate from our window
position and the result was that you'd get some incorrect behavior.
Unify the logic more to fix the positioning.

Note there is room to improve this further, I think that all initial
positioning could go into the controller completely. But I wanted to
minimize the diff for a backport.
pull/9317/head
Mitchell Hashimoto 2025-10-22 16:14:28 -07:00 committed by GitHub
parent bdbda2fd83
commit b764055c33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 17 deletions

View File

@ -531,7 +531,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
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
return adjustForWindowPosition(frame: frame, on: screen)
}
guard let initialFrame else { return nil }
@ -549,7 +549,30 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
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
return adjustForWindowPosition(frame: frame, on: screen)
}
/// Adjusts the given frame for the configured window position.
func adjustForWindowPosition(frame: NSRect, on screen: NSScreen) -> NSRect {
guard let x = derivedConfig.windowPositionX else { return frame }
guard let y = derivedConfig.windowPositionY else { return frame }
// Convert top-left coordinates to bottom-left origin using our utility extension
let origin = screen.origin(
fromTopLeftOffsetX: CGFloat(x),
offsetY: CGFloat(y),
windowSize: frame.size)
// Clamp the origin to ensure the window stays fully visible on screen
var safeOrigin = origin
let vf = screen.visibleFrame
safeOrigin.x = min(max(safeOrigin.x, vf.minX), vf.maxX - frame.width)
safeOrigin.y = min(max(safeOrigin.y, vf.minY), vf.maxY - frame.height)
// Return our new origin
var result = frame
result.origin = safeOrigin
return result
}
/// This is called anytime a node in the surface tree is being removed.
@ -1362,12 +1385,16 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
let macosWindowButtons: Ghostty.MacOSWindowButtons
let macosTitlebarStyle: String
let maximize: Bool
let windowPositionX: Int16?
let windowPositionY: Int16?
init() {
self.backgroundColor = Color(NSColor.windowBackgroundColor)
self.macosWindowButtons = .visible
self.macosTitlebarStyle = "system"
self.maximize = false
self.windowPositionX = nil
self.windowPositionY = nil
}
init(_ config: Ghostty.Config) {
@ -1375,6 +1402,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
self.macosWindowButtons = config.macosWindowButtons
self.macosTitlebarStyle = config.macosTitlebarStyle
self.maximize = config.maximize
self.windowPositionX = config.windowPositionX
self.windowPositionY = config.windowPositionY
}
}
}

View File

@ -84,8 +84,7 @@ class TerminalWindow: NSWindow {
// fallback to original centering behavior
setInitialWindowPosition(
x: config.windowPositionX,
y: config.windowPositionY,
windowDecorations: config.windowDecorations)
y: config.windowPositionY)
// If our traffic buttons should be hidden, then hide them
if config.macosWindowButtons == .hidden {
@ -463,7 +462,7 @@ class TerminalWindow: NSWindow {
return derivedConfig.backgroundColor.withAlphaComponent(alpha)
}
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
private func setInitialWindowPosition(x: Int16?, y: Int16?) {
// If we don't have an X/Y then we try to use the previously saved window pos.
guard let x, let y else {
if (!LastWindowPosition.shared.restore(self)) {
@ -479,19 +478,14 @@ class TerminalWindow: NSWindow {
return
}
// Convert top-left coordinates to bottom-left origin using our utility extension
let origin = screen.origin(
fromTopLeftOffsetX: CGFloat(x),
offsetY: CGFloat(y),
windowSize: frame.size)
// We have an X/Y, use our controller function to set it up.
guard let terminalController else {
center()
return
}
// Clamp the origin to ensure the window stays fully visible on screen
var safeOrigin = origin
let vf = screen.visibleFrame
safeOrigin.x = min(max(safeOrigin.x, vf.minX), vf.maxX - frame.width)
safeOrigin.y = min(max(safeOrigin.y, vf.minY), vf.maxY - frame.height)
setFrameOrigin(safeOrigin)
let frame = terminalController.adjustForWindowPosition(frame: frame, on: screen)
setFrameOrigin(frame.origin)
}
private func hideWindowButtons() {