From 3db5b3da752b07d3877ab9682f660c76320e82a6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 10 Jun 2025 14:31:41 -0700 Subject: [PATCH] macos: hidden titlebar windows should cascade on new tab Windows with `macos-titlebar-style = hidden` create new windows when the new tab binding is pressed. This behavior has existed for a long time. However, these windows did not cascade, meaning they'd appear overlapped directly on top of the previous window, which is kind of nasty. This commit changes it so that new windows created via new tab from a hidden titlebar window will cascade. --- .../Terminal/TerminalController.swift | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index a984952f9..5916c5921 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -278,11 +278,6 @@ class TerminalController: BaseTerminalController { tg.removeWindow(window) } - // Our windows start out invisible. We need to make it visible. If we - // don't do this then various features such as window blur won't work because - // the macOS APIs only work on a visible window. - controller.showWindow(self) - // If we have the "hidden" titlebar style we want to create new // tabs as windows instead, so just skip adding it to the parent. if (ghostty.config.macosTitlebarStyle != "hidden") { @@ -303,7 +298,19 @@ class TerminalController: BaseTerminalController { } } - window.makeKeyAndOrderFront(self) + // We're dispatching this async because otherwise the lastCascadePoint doesn't + // take effect. Our best theory is there is some next-event-loop-tick logic + // that Cocoa is doing that we need to be after. + DispatchQueue.main.async { + // Only cascade if we aren't fullscreen and are alone in the tab group. + if !window.styleMask.contains(.fullScreen) && + window.tabGroup?.windows.count ?? 1 == 1 { + Self.lastCascadePoint = window.cascadeTopLeft(from: Self.lastCascadePoint) + } + + controller.showWindow(self) + window.makeKeyAndOrderFront(self) + } // It takes an event loop cycle until the macOS tabGroup state becomes // consistent which causes our tab labeling to be off when the "+" button