Skip to content

Commit 0d8597c

Browse files
New window inherits size from current window (#2124)
* New window inherits size from current window When creating a new window via Cmd+Shift+N, use the key window's frame dimensions instead of the hardcoded 460x360 default. The new window cascades from the existing window's position so it doesn't stack directly on top. * Use Ghostty's cascade algorithm for new window positioning Match upstream Ghostty's window cascade logic: maintain a lastCascadePoint that tracks where the next window should appear. First window seeds the point from its own top-left corner, subsequent windows advance the cascade point via NSWindow.cascadeTopLeft(from:). On window close, reset the cascade point to the closing window's position so the next window appears nearby. New windows still inherit the key window's size so Cmd+Shift+N creates a window matching the previous one's dimensions. * Fix frame-to-contentRect conversion and use preferred window resolver Convert existingFrame to a content rect via NSWindow.contentRect(forFrameRect:styleMask:) so the new window matches the source window's actual size instead of growing by titlebar insets on each Cmd+Shift+N. Use preferredMainWindowContextForWorkspaceCreation to resolve the source window, consistent with showOpenFolderPanel and other callers. --------- Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
1 parent ffb660d commit 0d8597c

1 file changed

Lines changed: 34 additions & 2 deletions

File tree

Sources/AppDelegate.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
21512151

21522152
private var mainWindowContexts: [ObjectIdentifier: MainWindowContext] = [:]
21532153
private var mainWindowControllers: [MainWindowController] = []
2154+
2155+
/// Tracks the cascade point for new windows, matching Ghostty's upstream algorithm.
2156+
/// Reset to `.zero` so the first window seeds the point from its own position.
2157+
private var lastCascadePoint = NSPoint.zero
21542158
private var startupSessionSnapshot: AppSessionSnapshot?
21552159
private var didPrepareStartupSessionSnapshot = false
21562160
private var didAttemptStartupSessionRestore = false
@@ -5843,9 +5847,24 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
58435847
.environmentObject(sidebarSelectionState)
58445848
.environmentObject(cmuxConfigStore)
58455849

5850+
// Use the current key window's size for new windows so Cmd+Shift+N
5851+
// creates a window matching the previous one's dimensions.
5852+
let styleMask: NSWindow.StyleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView]
5853+
let existingFrame = preferredMainWindowContextForWorkspaceCreation(
5854+
debugSource: "createMainWindow.initialGeometry"
5855+
).flatMap { resolvedWindow(for: $0)?.frame }
5856+
let initialRect: NSRect
5857+
if sessionWindowSnapshot == nil, let existingFrame {
5858+
// Convert frame rect to content rect so the new window matches the
5859+
// source window's actual size (frame includes titlebar insets).
5860+
initialRect = NSWindow.contentRect(forFrameRect: existingFrame, styleMask: styleMask)
5861+
} else {
5862+
initialRect = NSRect(x: 0, y: 0, width: 460, height: 360)
5863+
}
5864+
58465865
let window = NSWindow(
5847-
contentRect: NSRect(x: 0, y: 0, width: 460, height: 360),
5848-
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
5866+
contentRect: initialRect,
5867+
styleMask: styleMask,
58495868
backing: .buffered,
58505869
defer: false
58515870
)
@@ -5859,6 +5878,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
58595878
window.setFrame(restoredFrame, display: false)
58605879
} else {
58615880
window.center()
5881+
// Cascade using the same algorithm as upstream Ghostty: seed from
5882+
// the window's own top-left on the first call, then advance the
5883+
// cascade point for each subsequent window.
5884+
if mainWindowContexts.count >= 1 {
5885+
lastCascadePoint = window.cascadeTopLeft(from: lastCascadePoint)
5886+
} else {
5887+
lastCascadePoint = window.cascadeTopLeft(from: NSPoint(x: window.frame.minX, y: window.frame.maxY))
5888+
}
58625889
}
58635890
window.contentView = MainWindowHostingView(rootView: root)
58645891

@@ -11074,6 +11101,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
1107411101
}
1107511102

1107611103
private func unregisterMainWindow(_ window: NSWindow) {
11104+
// Reset cascade point so the next new window appears near the closing
11105+
// window's position, matching upstream Ghostty behavior.
11106+
let frame = window.frame
11107+
lastCascadePoint = NSPoint(x: frame.minX, y: frame.maxY)
11108+
1107711109
// Keep geometry available as a fallback even if the full session snapshot
1107811110
// is removed when the last window closes.
1107911111
persistWindowGeometry(from: window)

0 commit comments

Comments
 (0)