mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
Remember last focused window position for next startup (#5529)
Implements window position persistence on macOS, similar to Terminal.app and iTerm2. The window position is saved when it becomes main or moves, and restored on next startup. Window position is kept within visible screen bounds. cc @jsumners - Would you mind giving it a try? Any feedback would be appreciated! Resolves #4233
This commit is contained in:
@ -72,6 +72,7 @@
|
||||
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3C92D4445E20033CF96 /* Dock.swift */; };
|
||||
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */; };
|
||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
||||
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; };
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; };
|
||||
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378D2D31D6C100931030 /* Weak.swift */; };
|
||||
@ -168,6 +169,7 @@
|
||||
A5A2A3C92D4445E20033CF96 /* Dock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dock.swift; sourceTree = "<group>"; };
|
||||
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Extension.swift"; sourceTree = "<group>"; };
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
||||
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWindowPosition.swift; sourceTree = "<group>"; };
|
||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||
@ -270,6 +272,7 @@
|
||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||
@ -623,6 +626,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */,
|
||||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
||||
|
@ -283,9 +283,12 @@ 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.
|
||||
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||
guard let x, let y else {
|
||||
window.center()
|
||||
if (!LastWindowPosition.shared.restore(window)) {
|
||||
window.center()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -490,6 +493,20 @@ class TerminalController: BaseTerminalController {
|
||||
override func windowDidMove(_ notification: Notification) {
|
||||
super.windowDidMove(notification)
|
||||
self.fixTabBar()
|
||||
|
||||
// Whenever we move save our last position for the next start.
|
||||
if let window {
|
||||
LastWindowPosition.shared.save(window)
|
||||
}
|
||||
}
|
||||
|
||||
func windowDidBecomeMain(_ notification: Notification) {
|
||||
// Whenever we get focused, use that as our last window position for
|
||||
// restart. This differs from Terminal.app but matches iTerm2 behavior
|
||||
// and I think its sensible.
|
||||
if let window {
|
||||
LastWindowPosition.shared.save(window)
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the window will be encoded. We handle the data encoding here in the
|
||||
|
34
macos/Sources/Helpers/LastWindowPosition.swift
Normal file
34
macos/Sources/Helpers/LastWindowPosition.swift
Normal file
@ -0,0 +1,34 @@
|
||||
import Cocoa
|
||||
|
||||
/// Manages the persistence and restoration of window positions across app launches.
|
||||
class LastWindowPosition {
|
||||
static let shared = LastWindowPosition()
|
||||
|
||||
private let positionKey = "NSWindowLastPosition"
|
||||
|
||||
func save(_ window: NSWindow) {
|
||||
let origin = window.frame.origin
|
||||
let point = [origin.x, origin.y]
|
||||
UserDefaults.standard.set(point, forKey: positionKey)
|
||||
}
|
||||
|
||||
func restore(_ window: NSWindow) -> Bool {
|
||||
guard let points = UserDefaults.standard.array(forKey: positionKey) as? [Double],
|
||||
points.count == 2 else { return false }
|
||||
|
||||
let lastPosition = CGPoint(x: points[0], y: points[1])
|
||||
|
||||
guard let screen = window.screen ?? NSScreen.main else { return false }
|
||||
let visibleFrame = screen.visibleFrame
|
||||
|
||||
var newFrame = window.frame
|
||||
newFrame.origin = lastPosition
|
||||
if !visibleFrame.contains(newFrame.origin) {
|
||||
newFrame.origin.x = max(visibleFrame.minX, min(visibleFrame.maxX - newFrame.width, newFrame.origin.x))
|
||||
newFrame.origin.y = max(visibleFrame.minY, min(visibleFrame.maxY - newFrame.height, newFrame.origin.y))
|
||||
}
|
||||
|
||||
window.setFrame(newFrame, display: true)
|
||||
return true
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user