Only use macOS 26.0 workarounds on macOS 26.0 (#10083)

Following up on my comment here about ripping out the NSScrollPocket
frame change observer in SurfaceScrollView once it's no longer needed:
https://github.com/ghostty-org/ghostty/pull/9446#issuecomment-3505314976

I tested the full macOS {26.0, 26.1, 26.2} times Xcode {26.0, 26.1,
26.2} matrix, and the fix was actually on the macOS side, i.e., the
workaround is always needed on macOS 26.0 and never on macOS 26.1+,
regardless of Xcode version. So if we want to avoid this kludge when
it's not needed, we have to use `#available` predicates rather than
removing the code outright.

I'll let maintainers judge whether the juice is worth the squeeze.

I also looked into the other 26.0-only workaround, pertaining to the
window title in the tabs titlebar, but I found that even though the
behavior with an empty view is OK on 26.1+, a warning about ambiguous
content size is still logged, so I figured this should be left as-is. I
updated the comment accordingly.
This commit is contained in:
Mitchell Hashimoto
2025-12-29 06:51:30 -08:00
committed by GitHub
2 changed files with 20 additions and 14 deletions

View File

@@ -322,7 +322,8 @@ extension TitlebarTabsTahoeTerminalWindow {
} else {
// 1x1.gif strikes again! For real: if we render a zero-sized
// view here then the toolbar just disappears our view. I don't
// know. This appears fixed in 26.1 Beta but keep it safe for 26.0.
// know. On macOS 26.1+ the view no longer disappears, but the
// toolbar still logs an ambiguous content size warning.
Color.clear.frame(width: 1, height: 1)
}
}

View File

@@ -120,18 +120,20 @@ class SurfaceScrollView: NSView {
self?.handleScrollerStyleChange()
})
// Listen for frame change events. See the docstring for
// handleFrameChange for why this is necessary.
observers.append(NotificationCenter.default.addObserver(
forName: NSView.frameDidChangeNotification,
object: nil,
// Since this observer is used to immediately override the event
// that produced the notification, we let it run synchronously on
// the posting thread.
queue: nil
) { [weak self] notification in
self?.handleFrameChange(notification)
})
// Listen for frame change events on macOS 26.0. See the docstring for
// handleFrameChangeForNSScrollPocket for why this is necessary.
if #unavailable(macOS 26.1) { if #available(macOS 26.0, *) {
observers.append(NotificationCenter.default.addObserver(
forName: NSView.frameDidChangeNotification,
object: nil,
// Since this observer is used to immediately override the event
// that produced the notification, we let it run synchronously on
// the posting thread.
queue: nil
) { [weak self] notification in
self?.handleFrameChangeForNSScrollPocket(notification)
})
}}
// Listen for derived config changes to update scrollbar settings live
surfaceView.$derivedConfig
@@ -328,7 +330,10 @@ class SurfaceScrollView: NSView {
/// and reset their frame to zero.
///
/// See also https://developer.apple.com/forums/thread/798392.
private func handleFrameChange(_ notification: Notification) {
///
/// This bug is only present in macOS 26.0.
@available(macOS, introduced: 26.0, obsoleted: 26.1)
private func handleFrameChangeForNSScrollPocket(_ notification: Notification) {
guard let window = window as? HiddenTitlebarTerminalWindow else { return }
guard !window.styleMask.contains(.fullScreen) else { return }
guard let view = notification.object as? NSView else { return }