Skip to content

Commit 597e8cf

Browse files
authored
macOS: fix window position when dragging split into a new window (ghostty-org#11429)
2 parents a91e747 + 5c51603 commit 597e8cf

2 files changed

Lines changed: 112 additions & 2 deletions

File tree

macos/GhosttyUITests/GhosttyWindowPositionUITests.swift

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
1515
@MainActor func testWindowCascading() async throws {
1616
try updateConfig(
1717
"""
18+
window-width = 30
19+
window-height = 10
1820
title = "GhosttyWindowPositionUITests"
1921
"""
2022
)
@@ -46,6 +48,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
4648

4749
XCTAssertEqual(windowFrame2.minX, windowFrame.minX + 30, accuracy: 5, "New window should be on the right")
4850

51+
XCTAssertEqual(windowFrame2.minY, windowFrame.minY + 30, accuracy: 5, "New window should be on the bottom right")
52+
4953
app.typeKey("n", modifierFlags: [.command])
5054

5155
let window3 = app.windows.firstMatch
@@ -55,6 +59,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
5559

5660
XCTAssertEqual(windowFrame3.minX, windowFrame2.minX + 30, accuracy: 5, "New window should be on the right")
5761

62+
XCTAssertEqual(windowFrame3.minY, windowFrame2.minY + 30, accuracy: 5, "New window should be on the bottom right")
63+
5864
app.typeKey("n", modifierFlags: [.command])
5965

6066
let window4 = app.windows.firstMatch
@@ -63,6 +69,111 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
6369
XCTAssertNotEqual(windowFrame3, windowFrame4, "New window should have moved")
6470

6571
XCTAssertEqual(windowFrame4.minX, windowFrame3.minX + 30, accuracy: 5, "New window should be on the right")
72+
73+
XCTAssertEqual(windowFrame4.minY, windowFrame3.minY + 30, accuracy: 5, "New window should be on the bottom right")
74+
}
75+
76+
@MainActor func testDragSplitWindowPosition() async throws {
77+
try updateConfig(
78+
"""
79+
window-width = 40
80+
window-height = 20
81+
title = "GhosttyWindowPositionUITests"
82+
macos-titlebar-style = hidden
83+
"""
84+
)
85+
86+
let app = try ghosttyApplication()
87+
// Suppress Restoration
88+
app.launchArguments += ["-NSQuitAlwaysKeepsWindows", "NO"]
89+
// Clean run
90+
app.launchEnvironment["GHOSTTY_CLEAR_USER_DEFAULTS"] = "YES"
91+
92+
app.launch() // window in the center
93+
94+
let window = app.windows.firstMatch
95+
XCTAssertTrue(window.waitForExistence(timeout: 5), "New window should appear")
96+
97+
// remove fixed size
98+
try updateConfig(
99+
"""
100+
title = "GhosttyWindowPositionUITests"
101+
macos-titlebar-style = hidden
102+
"""
103+
)
104+
app.typeKey(",", modifierFlags: [.command, .shift])
105+
106+
app.typeKey("d", modifierFlags: [.command])
107+
108+
let rightSplit = app.groups["Right pane"]
109+
let rightFrame = rightSplit.frame
110+
111+
let sourcePos = rightSplit.coordinate(withNormalizedOffset: .zero)
112+
.withOffset(.init(dx: rightFrame.size.width / 2, dy: 3))
113+
114+
let targetPos = rightSplit.coordinate(withNormalizedOffset: .zero)
115+
.withOffset(.init(dx: rightFrame.size.width + 100, dy: 0))
116+
117+
sourcePos.click(forDuration: 0.2, thenDragTo: targetPos)
118+
119+
let window2 = app.windows.firstMatch
120+
XCTAssertTrue(window2.waitForExistence(timeout: 5), "New window should appear")
121+
let windowFrame2 = window2.frame
122+
123+
try await Task.sleep(for: .seconds(0.5))
124+
125+
XCTAssertEqual(windowFrame2.minX, rightFrame.maxX + 100, accuracy: 5, "New window should be target position")
126+
XCTAssertEqual(windowFrame2.minY, rightFrame.minY, accuracy: 5, "New window should be target position")
127+
XCTAssertEqual(windowFrame2.width, rightFrame.width, accuracy: 5, "New window should use size from config")
128+
XCTAssertEqual(windowFrame2.height, rightFrame.height, accuracy: 5, "New window should use size from config")
129+
}
130+
131+
@MainActor func testDragSplitWindowPositionWithFixedSize() async throws {
132+
try updateConfig(
133+
"""
134+
window-width = 40
135+
window-height = 20
136+
title = "GhosttyWindowPositionUITests"
137+
macos-titlebar-style = hidden
138+
"""
139+
)
140+
141+
let app = try ghosttyApplication()
142+
// Suppress Restoration
143+
app.launchArguments += ["-NSQuitAlwaysKeepsWindows", "NO"]
144+
// Clean run
145+
app.launchEnvironment["GHOSTTY_CLEAR_USER_DEFAULTS"] = "YES"
146+
147+
app.launch() // window in the center
148+
149+
let window = app.windows.firstMatch
150+
XCTAssertTrue(window.waitForExistence(timeout: 5), "New window should appear")
151+
let windowFrame = window.frame
152+
153+
app.typeKey("d", modifierFlags: [.command])
154+
155+
let rightSplit = app.groups["Right pane"]
156+
let rightFrame = rightSplit.frame
157+
158+
let sourcePos = rightSplit.coordinate(withNormalizedOffset: .zero)
159+
.withOffset(.init(dx: rightFrame.size.width / 2, dy: 3))
160+
161+
let targetPos = rightSplit.coordinate(withNormalizedOffset: .zero)
162+
.withOffset(.init(dx: rightFrame.size.width + 100, dy: 0))
163+
164+
sourcePos.click(forDuration: 0.2, thenDragTo: targetPos)
165+
166+
let window2 = app.windows.firstMatch
167+
XCTAssertTrue(window2.waitForExistence(timeout: 5), "New window should appear")
168+
let windowFrame2 = window2.frame
169+
170+
try await Task.sleep(for: .seconds(0.5))
171+
172+
XCTAssertEqual(windowFrame2.minX, rightFrame.maxX + 100, accuracy: 5, "New window should be target position")
173+
XCTAssertEqual(windowFrame2.minY, rightFrame.minY, accuracy: 5, "New window should be target position")
174+
XCTAssertEqual(windowFrame2.width, windowFrame.width, accuracy: 5, "New window should use size from config")
175+
// We're still using right frame, because of the debug banner
176+
XCTAssertEqual(windowFrame2.height, rightFrame.height, accuracy: 5, "New window should use size from config")
66177
}
67178

68179
// MARK: - Restore round-trip per titlebar style

macos/Sources/Features/Terminal/TerminalController.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
320320
let treeSize: CGSize? = tree.root?.viewBounds()
321321

322322
DispatchQueue.main.async {
323+
c.showWindow(self)
323324
if let window = c.window {
324325
// If we have a tree size, resize the window's content to match
325326
if let treeSize, treeSize.width > 0, treeSize.height > 0 {
@@ -337,8 +338,6 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
337338
}
338339
}
339340
}
340-
341-
c.showWindow(self)
342341
}
343342

344343
// Setup our undo

0 commit comments

Comments
 (0)