tweaks to window position

This commit is contained in:
Mitchell Hashimoto
2025-01-02 13:04:30 -08:00
parent f49a029c49
commit 29b96be84f
4 changed files with 61 additions and 46 deletions

View File

@ -273,6 +273,28 @@ class TerminalController: BaseTerminalController {
} }
} }
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
guard let window else { return }
// If we don't have both an X and Y we center.
guard let x, let y else {
window.center()
return
}
// Prefer the screen our window is being placed on otherwise our primary screen.
guard let screen = window.screen ?? NSScreen.screens.first else {
window.center()
return
}
// Orient based on the top left of the primary monitor
let frame = screen.visibleFrame
window.setFrameOrigin(.init(
x: frame.minX + CGFloat(x),
y: frame.maxY - (CGFloat(y) + window.frame.height)))
}
//MARK: - NSWindowController //MARK: - NSWindowController
override func windowWillLoad() { override func windowWillLoad() {
@ -370,7 +392,10 @@ class TerminalController: BaseTerminalController {
// Set our window positioning to coordinates if config value exists, otherwise // Set our window positioning to coordinates if config value exists, otherwise
// fallback to original centering behavior // fallback to original centering behavior
setInitialWindowPosition(window, x: config.windowInitialPositionX, y: config.windowInitialPositionY, windowDecorations: config.windowDecorations) setInitialWindowPosition(
x: config.windowPositionX,
y: config.windowPositionY,
windowDecorations: config.windowDecorations)
// Make sure our theme is set on the window so styling is correct. // Make sure our theme is set on the window so styling is correct.
if let windowTheme = config.windowTheme { if let windowTheme = config.windowTheme {
@ -469,31 +494,6 @@ class TerminalController: BaseTerminalController {
data.encode(with: state) data.encode(with: state)
} }
func setInitialWindowPosition(_ window: NSWindow, x: Int16?, y: Int16?, windowDecorations: Bool) {
if let primaryScreen = NSScreen.screens.first {
let frame = primaryScreen.visibleFrame
if let windowPositionX = x, let windowPositionY = y {
// Offset titlebar if needed, otherwise use default padding of 12
// NOTE: Not 100% certain where this extra padding comes from but I'd love
// to calculate it dynamically if possible
let titlebarHeight = windowDecorations ? window.frame.height - (window.contentView?.frame.height ?? 0) : 12
// Orient based on the top left of the primary monitor
let startPositionX = frame.origin.x + CGFloat(windowPositionX)
let startPositionY = (frame.origin.y + frame.height) - (CGFloat(windowPositionY) + window.frame.height) + titlebarHeight
window.setFrameOrigin(NSPoint(x: startPositionX, y: startPositionY))
} else {
// Fallback to original centering behavior
window.center()
}
} else {
// Fallback to original centering behavior
window.center()
}
}
// MARK: First Responder // MARK: First Responder
@IBAction func newWindow(_ sender: Any?) { @IBAction func newWindow(_ sender: Any?) {

View File

@ -150,17 +150,17 @@ extension Ghostty {
return String(cString: ptr) return String(cString: ptr)
} }
var windowInitialPositionX: 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
let key = "window-initial-position-x" let key = "window-position-x"
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
} }
var windowInitialPositionY: Int16? { var windowPositionY: 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
let key = "window-initial-position-y" let key = "window-position-y"
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
} }

View File

@ -149,14 +149,10 @@ pub const App = struct {
value: apprt.Action.Value(action), value: apprt.Action.Value(action),
) !void { ) !void {
switch (action) { switch (action) {
.new_window => { .new_window => _ = try self.newSurface(switch (target) {
var surface = try self.newSurface(switch (target) {
.app => null, .app => null,
.surface => |v| v, .surface => |v| v,
}); }),
try surface.setInitialWindowPosition(self.config.@"window-initial-position-x", self.config.@"window-initial-position-y");
},
.new_tab => try self.newTab(switch (target) { .new_tab => try self.newTab(switch (target) {
.app => null, .app => null,
@ -514,6 +510,13 @@ pub const Surface = struct {
) orelse return glfw.mustGetErrorCode(); ) orelse return glfw.mustGetErrorCode();
errdefer win.destroy(); errdefer win.destroy();
// Setup our
setInitialWindowPosition(
win,
app.config.@"window-position-x",
app.config.@"window-position-y",
);
// Get our physical DPI - debug only because we don't have a use for // Get our physical DPI - debug only because we don't have a use for
// this but the logging of it may be useful // this but the logging of it may be useful
if (builtin.mode == .Debug) { if (builtin.mode == .Debug) {
@ -670,12 +673,12 @@ pub const Surface = struct {
/// Set the initial window position. This is called exactly once at /// Set the initial window position. This is called exactly once at
/// surface initialization time. This may be called before "self" /// surface initialization time. This may be called before "self"
/// is fully initialized. /// is fully initialized.
fn setInitialWindowPosition(self: *const Surface, x: ?i16, y: ?i16) !void { fn setInitialWindowPosition(win: glfw.Window, x: ?i16, y: ?i16) void {
const start_position_x = x orelse return; const start_position_x = x orelse return;
const start_position_y = y orelse return; const start_position_y = y orelse return;
log.debug("setting initial window position ({},{})", .{ start_position_x, start_position_y }); log.debug("setting initial window position ({},{})", .{ start_position_x, start_position_y });
self.window.setPos(.{ .x = start_position_x, .y = start_position_y }); win.setPos(.{ .x = start_position_x, .y = start_position_y });
} }
/// Set the size limits of the window. /// Set the size limits of the window.

View File

@ -1108,7 +1108,7 @@ keybind: Keybinds = .{},
@"window-height": u32 = 0, @"window-height": u32 = 0,
@"window-width": u32 = 0, @"window-width": u32 = 0,
/// The initial window position. This position is in pixels and is relative /// The starting window position. This position is in pixels and is relative
/// to the top-left corner of the primary monitor. Both values must be set to take /// to the top-left corner of the primary monitor. Both values must be set to take
/// effect. If only one value is set, it is ignored. /// effect. If only one value is set, it is ignored.
/// ///
@ -1117,10 +1117,22 @@ keybind: Keybinds = .{},
/// to be a certain position to fit within the grid. There is nothing Ghostty /// to be a certain position to fit within the grid. There is nothing Ghostty
/// will do about this, but it will make an effort. /// will do about this, but it will make an effort.
/// ///
/// Important note: Setting this value will only work on macOS and glfw builds /// Also note that negative values are also up to the operating system and
/// on Linux. GTK 4.0 does not support setting the window position. /// window manager. Some window managers may not allow windows to be placed
@"window-initial-position-x": ?i16 = null, /// off-screen.
@"window-initial-position-y": ?i16 = null, ///
/// Invalid positions are runtime-specific, but generally the positions are
/// clamped to the nearest valid position.
///
/// On macOS, the window position is relative to the top-left corner of
/// the visible screen area. This means that if the menu bar is visible, the
/// window will be placed below the menu bar.
///
/// Note: this is only supported on macOS and Linux GLFW builds. The GTK
/// runtime does not support setting the window position (this is a limitation
/// of GTK 4.0).
@"window-position-x": ?i16 = null,
@"window-position-y": ?i16 = null,
/// Whether to enable saving and restoring window state. Window state includes /// Whether to enable saving and restoring window state. Window state includes
/// their position, size, tabs, splits, etc. Some window state requires shell /// their position, size, tabs, splits, etc. Some window state requires shell