mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-18 13:30:29 +00:00
macOS: fix intrinsicContentSize race in windowDidLoad (#11256)
Add initialContentSize fallback on TerminalViewContainer so intrinsicContentSize returns the correct value immediately, without waiting for @FocusedValue to propagate. This removes the need for the DispatchQueue.main.asyncAfter 40ms delay. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1038,27 +1038,26 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
||||
}
|
||||
|
||||
// Initialize our content view to the SwiftUI root
|
||||
window.contentView = TerminalViewContainer {
|
||||
let container = TerminalViewContainer {
|
||||
TerminalView(ghostty: ghostty, viewModel: self, delegate: self)
|
||||
}
|
||||
|
||||
// Set the initial content size on the container so that
|
||||
// intrinsicContentSize returns the correct value immediately,
|
||||
// without waiting for @FocusedValue to propagate through the
|
||||
// SwiftUI focus chain.
|
||||
container.initialContentSize = focusedSurface?.initialSize
|
||||
|
||||
window.contentView = container
|
||||
|
||||
// If we have a default size, we want to apply it.
|
||||
if let defaultSize {
|
||||
switch defaultSize {
|
||||
case .frame:
|
||||
// Frames can be applied immediately
|
||||
defaultSize.apply(to: window)
|
||||
defaultSize.apply(to: window)
|
||||
|
||||
case .contentIntrinsicSize:
|
||||
// Content intrinsic size requires a short delay so that AppKit
|
||||
// can layout our SwiftUI views.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(40)) { [weak self, weak window] in
|
||||
guard let self, let window else { return }
|
||||
defaultSize.apply(to: window)
|
||||
if let screen = window.screen ?? NSScreen.main {
|
||||
let frame = self.adjustForWindowPosition(frame: window.frame, on: screen)
|
||||
window.setFrameOrigin(frame.origin)
|
||||
}
|
||||
if case .contentIntrinsicSize = defaultSize {
|
||||
if let screen = window.screen ?? NSScreen.main {
|
||||
let frame = self.adjustForWindowPosition(frame: window.frame, on: screen)
|
||||
window.setFrameOrigin(frame.origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,23 @@ class TerminalViewContainer: NSView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
/// To make ``TerminalController/DefaultSize/contentIntrinsicSize``
|
||||
/// work in ``TerminalController/windowDidLoad()``,
|
||||
/// we override this to provide the correct size.
|
||||
/// The initial content size to use as a fallback before the SwiftUI
|
||||
/// view hierarchy has completed layout (i.e. before @FocusedValue
|
||||
/// propagates `lastFocusedSurface`). Once the hosting view reports
|
||||
/// a valid intrinsic size, this fallback is no longer used.
|
||||
var initialContentSize: NSSize?
|
||||
|
||||
override var intrinsicContentSize: NSSize {
|
||||
terminalView.intrinsicContentSize
|
||||
let hostingSize = terminalView.intrinsicContentSize
|
||||
// The hosting view returns a valid size once SwiftUI has laid out
|
||||
// with the correct idealWidth/idealHeight. Before that (when
|
||||
// @FocusedValue hasn't propagated), it returns a tiny default.
|
||||
// Fall back to initialContentSize in that case.
|
||||
if let initialContentSize,
|
||||
hostingSize.width < initialContentSize.width || hostingSize.height < initialContentSize.height {
|
||||
return initialContentSize
|
||||
}
|
||||
return hostingSize
|
||||
}
|
||||
|
||||
private func setup() {
|
||||
|
||||
@@ -75,11 +75,11 @@ struct IntrinsicSizeTimingTests {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
|
||||
// TODO: Fix #11256 — set initialContentSize on the container so
|
||||
// intrinsicContentSize returns the correct value immediately.
|
||||
// await MainActor.run {
|
||||
// container.initialContentSize = expectedSize
|
||||
// }
|
||||
// Set initialContentSize so intrinsicContentSize returns the
|
||||
// correct value immediately, without waiting for @FocusedValue.
|
||||
await MainActor.run {
|
||||
container.initialContentSize = expectedSize
|
||||
}
|
||||
|
||||
let window = await NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
|
||||
@@ -111,10 +111,9 @@ struct IntrinsicSizeTimingTests {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
|
||||
// TODO: Fix #11256 — set initialContentSize on the container.
|
||||
// await MainActor.run {
|
||||
// container.initialContentSize = NSSize(width: 600, height: 400)
|
||||
// }
|
||||
await MainActor.run {
|
||||
container.initialContentSize = NSSize(width: 600, height: 400)
|
||||
}
|
||||
|
||||
let window = await NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
|
||||
@@ -156,12 +155,9 @@ struct IntrinsicSizeTimingTests {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
|
||||
// TODO: Fix #11256 — set initialContentSize on the container so
|
||||
// intrinsicContentSize returns the correct value immediately,
|
||||
// eliminating the need for the async delay.
|
||||
// await MainActor.run {
|
||||
// container.initialContentSize = NSSize(width: 600, height: 400)
|
||||
// }
|
||||
await MainActor.run {
|
||||
container.initialContentSize = NSSize(width: 600, height: 400)
|
||||
}
|
||||
|
||||
let window = await NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
|
||||
@@ -204,10 +200,9 @@ struct IntrinsicSizeTimingTests {
|
||||
OptionalIdealSizeView(idealWidth: nil, idealHeight: nil, titlebarStyle: titlebarStyle)
|
||||
}
|
||||
|
||||
// TODO: Fix #11256 — set initialContentSize on the container.
|
||||
// await MainActor.run {
|
||||
// container.initialContentSize = NSSize(width: 600, height: 400)
|
||||
// }
|
||||
await MainActor.run {
|
||||
container.initialContentSize = NSSize(width: 600, height: 400)
|
||||
}
|
||||
|
||||
let window = await NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 800, height: 600),
|
||||
|
||||
Reference in New Issue
Block a user