macos: make terminal smaller to account for legacy scrollbar (#9255)

https://github.com/ghostty-org/ghostty/discussions/9254

When the preferred scrollbar style is "legacy", the scrollbar takes up
space that offsets the actual terminal. To prevent reflow, we detect
this before the scrollbar becomes visible and shrink our terminal width
to prepare for it.

This doesn't account for the style changing at runtime, yet.

## Demo

Notice even before scrollbars, the `gh` output doesn't wrap.


https://github.com/user-attachments/assets/8716ff4d-0660-48b1-aadb-0d31e0b70fcd
pull/9256/head
Mitchell Hashimoto 2025-10-17 20:18:10 -07:00 committed by GitHub
commit 5bf05dfe31
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 27 additions and 10 deletions

View File

@ -142,18 +142,35 @@ class SurfaceScrollView: NSView {
// Only update sizes if we have a valid (non-zero) content size. The content size
// can be zero when this is added early to a view, or to an invisible hierarchy.
// Practically, this happened in the quick terminal.
let contentSize = scrollView.contentSize
if contentSize.width > 0 && contentSize.height > 0 {
// Keep document width synchronized with content width
documentView.setFrameSize(CGSize(
width: contentSize.width,
height: documentView.frame.height
))
// Inform the actual pty of our size change
surfaceView.sizeDidChange(contentSize)
var contentSize = scrollView.contentSize
guard contentSize.width > 0 && contentSize.height > 0 else {
synchronizeSurfaceView()
return
}
// If we have a legacy scrollbar and its not visible, then we account for that
// in advance, because legacy scrollbars change our contentSize and force reflow
// of our terminal which is not desirable.
// See: https://github.com/ghostty-org/ghostty/discussions/9254
let style = scrollView.verticalScroller?.scrollerStyle ?? NSScroller.preferredScrollerStyle
if style == .legacy {
if (scrollView.verticalScroller?.isHidden ?? true) {
let scrollerWidth = NSScroller.scrollerWidth(for: .regular, scrollerStyle: .legacy)
contentSize.width -= scrollerWidth
}
}
// Keep document width synchronized with content width
documentView.setFrameSize(CGSize(
width: contentSize.width,
height: documentView.frame.height
))
// Inform the actual pty of our size change. This doesn't change the actual view
// frame because we do want to render the whole thing, but it will prevent our
// rows/cols from going into the non-content area.
surfaceView.sizeDidChange(contentSize)
// When our scrollview changes make sure our surface view is synchronized
synchronizeSurfaceView()
}