You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Context: found while reviewing the ScrollView adapter change (#105). Decided since: legacy scroll bars keep their current behavior here, and laying the content out beside them is #111. Whether macOS snaps the offset to pixels is #110.
Goal
The viewport is logical geometry, and pixel rounding is a rendering detail that shouldn't change it. On macOS, visibleSize and the size the content lays out for should be exact, not rounded to whole pixels: a 99.2 pt wide view should report and lay out for 99.2, as on iOS.
Observation
NSClipView rounds its own size to whole backing pixels inside setFrameSize(_:). In a probe on a 2x display, AppKit's tiling passed 99.2 and the clip view stored 99, and 99.8 became 100. It rounds even outside a window, and overriding tile() to set the exact size doesn't stick, so the clip view can't be made exact.
visibleSize is the clip view's bounds size, so it's rounded.
visibleSize: report the size AppKit asks the clip view for, before the clip view rounds it, for example by recording it in a clip view subclass's setFrameSize(_:). That's the view's size minus the space legacy scroll bars take.
renderBounds(): use the view's exact bounds size, which also drops the pixel conversion each render pass does now.
The half-pixel difference remains where AppKit's real scroll range matters:
Content that fits must not scroll by the leftover fraction: a 99.2 pt document in a 99 pt clip view would scroll by 0.2 pt. Along an axis the content fits, round the document view's size the way NSClipView rounds its own.
updateScrollElasticity() and the gesture routing in ScrollSession, including the out-of-range check from [scroll] keep a set contentOffset as set on macOS, instead of clamping it into the scrollable range #106, compare against AppKit's range, so they need a half-pixel tolerance or AppKit's own clip view geometry. Without one, a nested view resting at AppKit's real end, a fraction past the exact maxOffsetY, would count as out of range and stop passing gestures to its parent.
When the clip view rounds down, up to half a pixel of content at the edge is clipped. That's an accepted rendering detail.
To verify (probe first)
The size a clip view subclass sees in setFrameSize(_:) at fractional view sizes, with legacy scroll bars, content insets, scaled bounds and a rotated view.
Rounding the document view's size keeps fitting content from scrolling at fractional sizes and positions, including the rotated cases in ComposeView+RenderBoundsTests.
To do
Record the exact clip view size for visibleSize, make renderBounds() exact, round the document view's size along fitting axes, and add the tolerance to the elasticity check and the gesture routing.
Tests: visibleSize and the render bounds are exact at fractional sizes (99.2 stays 99.2), fitting content at a fractional size doesn't scroll, a nested view resting at its end still passes gestures on, and the elasticity follows AppKit's range. Update the tests that expect rounded sizes.
Context: found while reviewing the
ScrollViewadapter change (#105). Decided since: legacy scroll bars keep their current behavior here, and laying the content out beside them is #111. Whether macOS snaps the offset to pixels is #110.Goal
The viewport is logical geometry, and pixel rounding is a rendering detail that shouldn't change it. On macOS,
visibleSizeand the size the content lays out for should be exact, not rounded to whole pixels: a 99.2 pt wide view should report and lay out for 99.2, as on iOS.Observation
NSClipViewrounds its own size to whole backing pixels insidesetFrameSize(_:). In a probe on a 2x display, AppKit's tiling passed 99.2 and the clip view stored 99, and 99.8 became 100. It rounds even outside a window, and overridingtile()to set the exact size doesn't stick, so the clip view can't be made exact.visibleSizeis the clip view's bounds size, so it's rounded.ComposeView.renderBounds()rounds the bounds the same way, so the layout matches the clip view ([render] size the render bounds from the view's bounds instead of hiding the scrollers #91, [render] Allow for floating-point noise when deciding whether content overflows the viewport #92).Direction
visibleSize: report the size AppKit asks the clip view for, before the clip view rounds it, for example by recording it in a clip view subclass'ssetFrameSize(_:). That's the view's size minus the space legacy scroll bars take.renderBounds(): use the view's exact bounds size, which also drops the pixel conversion each render pass does now.NSClipViewrounds its own.updateScrollElasticity()and the gesture routing inScrollSession, including the out-of-range check from [scroll] keep a set contentOffset as set on macOS, instead of clamping it into the scrollable range #106, compare against AppKit's range, so they need a half-pixel tolerance or AppKit's own clip view geometry. Without one, a nested view resting at AppKit's real end, a fraction past the exactmaxOffsetY, would count as out of range and stop passing gestures to its parent.To verify (probe first)
setFrameSize(_:)at fractional view sizes, with legacy scroll bars, content insets, scaled bounds and a rotated view.ComposeView+RenderBoundsTests.To do
visibleSize, makerenderBounds()exact, round the document view's size along fitting axes, and add the tolerance to the elasticity check and the gesture routing.visibleSizeand the render bounds are exact at fractional sizes (99.2 stays 99.2), fitting content at a fractional size doesn't scroll, a nested view resting at its end still passes gestures on, and the elasticity follows AppKit's range. Update the tests that expect rounded sizes.Related: #38, #91, #92, #106, #110, #111.