Skip to content

Commit 3445c9a

Browse files
authored
macOS: filter proper intrinsicContentSize when opening new window (ghostty-org#11257)
Fixes ghostty-org#11256, which is rather hard to reproduce on macOS 26, but after adding breaking points on size update, we can see that it happens when the `intrinsicContentSize` is not properly updated. <img width="998" height="556" alt="Xnip2026-03-09_11-38-40" src="https://github.com/user-attachments/assets/8ac1de91-5895-45fc-a443-002eb016a1ce" />
2 parents 1e981f8 + 3c93c35 commit 3445c9a

1 file changed

Lines changed: 36 additions & 13 deletions

File tree

macos/Sources/Features/Terminal/TerminalController.swift

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
5757
/// The notification cancellable for focused surface property changes.
5858
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
5959

60+
/// The KVO cancellable for `window.contentView.intrinsicContentSize` changes.
61+
private var contentIntrinsicContentSizeCancellable: AnyCancellable?
62+
6063
/// This will be set to the initial frame of the window from the xib on load.
6164
private var initialFrame: NSRect?
6265

@@ -1038,9 +1041,10 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
10381041
}
10391042

10401043
// Initialize our content view to the SwiftUI root
1041-
window.contentView = TerminalViewContainer {
1044+
let contentView = TerminalViewContainer {
10421045
TerminalView(ghostty: ghostty, viewModel: self, delegate: self)
10431046
}
1047+
window.contentView = contentView
10441048

10451049
// If we have a default size, we want to apply it.
10461050
if let defaultSize {
@@ -1049,17 +1053,21 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
10491053
// Frames can be applied immediately
10501054
defaultSize.apply(to: window)
10511055

1052-
case .contentIntrinsicSize:
1053-
// Content intrinsic size requires a short delay so that AppKit
1054-
// can layout our SwiftUI views.
1055-
DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(10_000)) { [weak self, weak window] in
1056-
guard let self, let window else { return }
1057-
defaultSize.apply(to: window)
1058-
if let screen = window.screen ?? NSScreen.main {
1059-
let frame = self.adjustForWindowPosition(frame: window.frame, on: screen)
1060-
window.setFrameOrigin(frame.origin)
1056+
case let .contentIntrinsicSize(minSize):
1057+
// We wait for the first proper content intrinsic size,
1058+
// after AppKit laid out our SwiftUI views.
1059+
contentIntrinsicContentSizeCancellable = contentView.publisher(for: \.intrinsicContentSize)
1060+
.filter { $0.width >= minSize.width && $0.height >= minSize.height }
1061+
.first()
1062+
.sink { [weak self, weak window] _ in
1063+
guard let self, let window else { return }
1064+
defaultSize.apply(to: window)
1065+
if let screen = window.screen ?? NSScreen.main {
1066+
let frame = self.adjustForWindowPosition(frame: window.frame, on: screen)
1067+
window.setFrameOrigin(frame.origin)
1068+
}
1069+
contentIntrinsicContentSizeCancellable = nil
10611070
}
1062-
}
10631071
}
10641072
}
10651073

@@ -1560,6 +1568,21 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
15601568
self.windowPositionX = config.windowPositionX
15611569
self.windowPositionY = config.windowPositionY
15621570
}
1571+
1572+
/// The minimum size a window can be resized to as of macOS 26.3, it should cover most cases.
1573+
///
1574+
/// We use this to filter incorrect intrinsicContentSize values when positioning a new window.
1575+
fileprivate var minimumWindowSize: CGSize {
1576+
switch macosTitlebarStyle {
1577+
case "native", "transparent":
1578+
CGSize(width: 105 + 1, height: 33 + 1)
1579+
case "tabs":
1580+
CGSize(width: 90 + 1, height: 48 + 1)
1581+
// hidden or others
1582+
default:
1583+
CGSize(width: 27 + 1, height: 40 + 1)
1584+
}
1585+
}
15631586
}
15641587
}
15651588

@@ -1607,7 +1630,7 @@ extension TerminalController {
16071630
case frame(NSRect)
16081631

16091632
/// A content size, set with `window.setContentSize`
1610-
case contentIntrinsicSize
1633+
case contentIntrinsicSize(_ minimum: CGSize)
16111634

16121635
func isChanged(for window: NSWindow) -> Bool {
16131636
switch self {
@@ -1644,7 +1667,7 @@ extension TerminalController {
16441667
} else if focusedSurface?.initialSize != nil {
16451668
// Initial size as requested by the configuration (e.g. `window-width`)
16461669
// takes next priority.
1647-
return .contentIntrinsicSize
1670+
return .contentIntrinsicSize(derivedConfig.minimumWindowSize)
16481671
} else if let initialFrame {
16491672
// The initial frame we had when we started otherwise.
16501673
return .frame(initialFrame)

0 commit comments

Comments
 (0)