From d6d6fe4e5800f48846815a6cb2401c495e9ca57c Mon Sep 17 00:00:00 2001 From: Lukas <134181853+bo2themax@users.noreply.github.com> Date: Thu, 12 Mar 2026 18:58:37 +0100 Subject: [PATCH 1/4] macOS: update window cascading Make it smaller and add comparisons between y values --- macos/GhosttyUITests/GhosttyWindowPositionUITests.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift b/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift index 7204472f3..d326c5954 100644 --- a/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift +++ b/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift @@ -15,6 +15,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase { @MainActor func testWindowCascading() async throws { try updateConfig( """ + window-width = 30 + window-height = 10 title = "GhosttyWindowPositionUITests" """ ) @@ -46,6 +48,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase { XCTAssertEqual(windowFrame2.minX, windowFrame.minX + 30, accuracy: 5, "New window should be on the right") + XCTAssertEqual(windowFrame2.minY, windowFrame.minY + 30, accuracy: 5, "New window should be on the bottom right") + app.typeKey("n", modifierFlags: [.command]) let window3 = app.windows.firstMatch @@ -55,6 +59,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase { XCTAssertEqual(windowFrame3.minX, windowFrame2.minX + 30, accuracy: 5, "New window should be on the right") + XCTAssertEqual(windowFrame3.minY, windowFrame2.minY + 30, accuracy: 5, "New window should be on the bottom right") + app.typeKey("n", modifierFlags: [.command]) let window4 = app.windows.firstMatch @@ -63,6 +69,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase { XCTAssertNotEqual(windowFrame3, windowFrame4, "New window should have moved") XCTAssertEqual(windowFrame4.minX, windowFrame3.minX + 30, accuracy: 5, "New window should be on the right") + + XCTAssertEqual(windowFrame4.minY, windowFrame3.minY + 30, accuracy: 5, "New window should be on the bottom right") } // MARK: - Restore round-trip per titlebar style From 3022aa05ea82296adb598d340735f8339f5bf753 Mon Sep 17 00:00:00 2001 From: Lukas <134181853+bo2themax@users.noreply.github.com> Date: Thu, 12 Mar 2026 19:54:12 +0100 Subject: [PATCH 2/4] macOS: add test cases for drag-split --- .../GhosttyWindowPositionUITests.swift | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift b/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift index d326c5954..99f7b5627 100644 --- a/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift +++ b/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift @@ -73,6 +73,109 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase { XCTAssertEqual(windowFrame4.minY, windowFrame3.minY + 30, accuracy: 5, "New window should be on the bottom right") } + @MainActor func testDragSplitWindowPosition() async throws { + try updateConfig( + """ + window-width = 40 + window-height = 20 + title = "GhosttyWindowPositionUITests" + macos-titlebar-style = hidden + """ + ) + + let app = try ghosttyApplication() + // Suppress Restoration + app.launchArguments += ["-NSQuitAlwaysKeepsWindows", "NO"] + // Clean run + app.launchEnvironment["GHOSTTY_CLEAR_USER_DEFAULTS"] = "YES" + + app.launch() // window in the center + + let window = app.windows.firstMatch + XCTAssertTrue(window.waitForExistence(timeout: 5), "New window should appear") + + // remove fixe size + try updateConfig( + """ + title = "GhosttyWindowPositionUITests" + macos-titlebar-style = hidden + """ + ) + app.typeKey(",", modifierFlags: [.command, .shift]) + + app.typeKey("d", modifierFlags: [.command]) + + let rightSplit = app.groups["Right pane"] + let rightFrame = rightSplit.frame + + let sourcePos = rightSplit.coordinate(withNormalizedOffset: .zero) + .withOffset(.init(dx: rightFrame.size.width / 2, dy: 3)) + + let targetPos = rightSplit.coordinate(withNormalizedOffset: .zero) + .withOffset(.init(dx: rightFrame.size.width + 100, dy: 0)) + + sourcePos.click(forDuration: 0.2, thenDragTo: targetPos) + + let window2 = app.windows.firstMatch + XCTAssertTrue(window2.waitForExistence(timeout: 5), "New window should appear") + let windowFrame2 = window2.frame + + try await Task.sleep(for: .seconds(0.5)) + + XCTAssertEqual(windowFrame2.minX, rightFrame.maxX + 100, accuracy: 5, "New window should be target position") + XCTAssertEqual(windowFrame2.minY, rightFrame.minY, accuracy: 5, "New window should be target position") + XCTAssertEqual(windowFrame2.width, rightFrame.width, accuracy: 5, "New window should use size from config") + XCTAssertEqual(windowFrame2.height, rightFrame.height, accuracy: 5, "New window should use size from config") + } + + @MainActor func testDragSplitWindowPositionWithFixedSize() async throws { + try updateConfig( + """ + window-width = 40 + window-height = 20 + title = "GhosttyWindowPositionUITests" + macos-titlebar-style = hidden + """ + ) + + let app = try ghosttyApplication() + // Suppress Restoration + app.launchArguments += ["-NSQuitAlwaysKeepsWindows", "NO"] + // Clean run + app.launchEnvironment["GHOSTTY_CLEAR_USER_DEFAULTS"] = "YES" + + app.launch() // window in the center + + let window = app.windows.firstMatch + XCTAssertTrue(window.waitForExistence(timeout: 5), "New window should appear") + let windowFrame = window.frame + + app.typeKey("d", modifierFlags: [.command]) + + let rightSplit = app.groups["Right pane"] + let rightFrame = rightSplit.frame + + let sourcePos = rightSplit.coordinate(withNormalizedOffset: .zero) + .withOffset(.init(dx: rightFrame.size.width / 2, dy: 3)) + + let targetPos = rightSplit.coordinate(withNormalizedOffset: .zero) + .withOffset(.init(dx: rightFrame.size.width + 100, dy: 0)) + + sourcePos.click(forDuration: 0.2, thenDragTo: targetPos) + + let window2 = app.windows.firstMatch + XCTAssertTrue(window2.waitForExistence(timeout: 5), "New window should appear") + let windowFrame2 = window2.frame + + try await Task.sleep(for: .seconds(0.5)) + + XCTAssertEqual(windowFrame2.minX, rightFrame.maxX + 100, accuracy: 5, "New window should be target position") + XCTAssertEqual(windowFrame2.minY, rightFrame.minY, accuracy: 5, "New window should be target position") + XCTAssertEqual(windowFrame2.width, windowFrame.width, accuracy: 5, "New window should use size from config") + // We're still using right frame, because of the debug banner + XCTAssertEqual(windowFrame2.height, rightFrame.height, accuracy: 5, "New window should use size from config") + } + // MARK: - Restore round-trip per titlebar style @MainActor func testRestoredNative() throws { try runRestoreTest(titlebarStyle: "native") } From 07bc8886822bdc19932efea54e6d01bd230078cc Mon Sep 17 00:00:00 2001 From: Lukas <134181853+bo2themax@users.noreply.github.com> Date: Thu, 12 Mar 2026 19:54:41 +0100 Subject: [PATCH 3/4] macOS: fix window position when dragging split into a new window --- macos/Sources/Features/Terminal/TerminalController.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index f06da571c..7ade0e38d 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -318,8 +318,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr // Calculate the target frame based on the tree's view bounds let treeSize: CGSize? = tree.root?.viewBounds() - + DispatchQueue.main.async { + c.showWindow(self) if let window = c.window { // If we have a tree size, resize the window's content to match if let treeSize, treeSize.width > 0, treeSize.height > 0 { @@ -337,8 +338,6 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr } } } - - c.showWindow(self) } // Setup our undo From 5c51603b0b82a33c7461384e27ee67edbf3818fd Mon Sep 17 00:00:00 2001 From: Lukas <134181853+bo2themax@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:02:23 +0100 Subject: [PATCH 4/4] chore: make ci happy --- macos/GhosttyUITests/GhosttyWindowPositionUITests.swift | 2 +- macos/Sources/Features/Terminal/TerminalController.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift b/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift index 99f7b5627..399c2531a 100644 --- a/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift +++ b/macos/GhosttyUITests/GhosttyWindowPositionUITests.swift @@ -94,7 +94,7 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase { let window = app.windows.firstMatch XCTAssertTrue(window.waitForExistence(timeout: 5), "New window should appear") - // remove fixe size + // remove fixed size try updateConfig( """ title = "GhosttyWindowPositionUITests" diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 7ade0e38d..56b0b40ad 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -318,7 +318,7 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr // Calculate the target frame based on the tree's view bounds let treeSize: CGSize? = tree.root?.viewBounds() - + DispatchQueue.main.async { c.showWindow(self) if let window = c.window {