macOS: Update core surface size when scroller style changes (#9523)

Listen to changes to the OS preferred scroller style and use it to
trigger updates the core surface size such that the scrollbar margin
kept in sync. Otherwise, the margin update would be deferred until the
next window or surface resize/split event.

In the video I'm turning my external bluetooth mouse off and on again,
triggering these updates.


https://github.com/user-attachments/assets/15c0ee40-2c1a-419a-8b07-8270e9f7a12f
pull/9525/head
Lukas 2025-11-09 08:11:02 +01:00 committed by GitHub
commit 2592b96f6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 28 additions and 13 deletions

View File

@ -104,6 +104,14 @@ class SurfaceScrollView: NSView {
self?.handleLiveScroll()
})
observers.append(NotificationCenter.default.addObserver(
forName: NSScroller.preferredScrollerStyleDidChangeNotification,
object: nil,
queue: .main
) { [weak self] _ in
self?.handleScrollerStyleChange()
})
// Listen for frame change events. See the docstring for
// handleFrameChange for why this is necessary.
observers.append(NotificationCenter.default.addObserver(
@ -154,19 +162,7 @@ class SurfaceScrollView: NSView {
// When our scrollview changes make sure our scroller and surface views are synchronized
synchronizeScrollView()
synchronizeSurfaceView()
// 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.
//
// Only update the pty 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 width = surfaceContentWidth()
let height = surfaceView.frame.height
if width > 0 && height > 0 {
surfaceView.sizeDidChange(CGSize(width: width, height: height))
}
synchronizeCoreSurface()
}
// MARK: Scrolling
@ -186,6 +182,20 @@ class SurfaceScrollView: NSView {
surfaceView.frame.origin = visibleRect.origin
}
/// 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.
private func synchronizeCoreSurface() {
// Only update the pty 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 width = surfaceContentWidth()
let height = surfaceView.frame.height
if width > 0 && height > 0 {
surfaceView.sizeDidChange(CGSize(width: width, height: height))
}
}
/// Sizes the document view and scrolls the content view according to the scrollbar state
private func synchronizeScrollView() {
// Update the document height to give our scroller the correct proportions
@ -217,6 +227,11 @@ class SurfaceScrollView: NSView {
private func handleScrollChange(_ notification: Notification) {
synchronizeSurfaceView()
}
/// Handles scrollbar style changes
private func handleScrollerStyleChange() {
synchronizeCoreSurface()
}
/// Handles live scroll events (user actively dragging the scrollbar).
///