Authored vector scrollbars

September 10, 2026 ยท View on GitHub

The retained document model supports a scrollbar control attached to a named scroll viewport. Its track and thumb are ordinary editable vector nodes. The runtime measures the actual Rml scroll range and track, derives thumb geometry, and clips content inside its viewport. Scroll offset is local view state and never becomes a settings value or an application action. The engine adapter accepts that local role without demanding an application action, while rejecting any scrollbar action, event or value output. Other controls retain the typed application validation.

The SYSTEM composition reserves a 36 dp target and an 8 dp gutter beside the settings body. A narrow dark trough and olive thumb preserve the Quake 4 45-degree motif; orange ink marks hover, focus and press. The header, action footer and frame remain outside the scrolling body. The source generator is tools/ui/update_system_scrollbar.py; repeated generation must be idempotent. The body wrapper has a zero initial height and grows into the remaining panel space, keeping percentage-height children from pushing the footer out of view. The preset band reserves room for the gutter while its labels and actions retain their minimum targets and can wrap with translated text.

Interaction and restoration

Thumb dragging retains the original grab fraction through layout changes. Track presses page with one line of overlap, and wheel, keyboard and gamepad navigation share bounded scroll steps. A wheel over content prefers a usable vertical bar, with horizontal fallback; a wheel directly over a bar uses that bar's axis. Scrolling with a pointer preserves the focused content control. Explicit navigation can focus a bar for line, page, start and end operations.

Every queued operation carries the owning source, modal and geometry identities. Hidden, fitting or unavailable scroll regions cannot accept stale operations. A zero-sized viewport invalidates layout and quarantines held navigation until release; restoring a valid size does not revive a canceled gesture. The actual viewport and projected track supply local coordinates, so density is applied once.

Documents with authored bars save logical scroll offsets in version-3 widget snapshots. Restore clamps them to the current measured range. A live density or viewport change preserves deliberate scrolling; a fresh focus request or a validation-driven field extent change can reveal its control. Legacy documents retain their previous focus-reveal and snapshot behavior. In mixed layouts, preservation applies only to the authored viewport's declared axis; unrelated, nested and perpendicular legacy scroll axes can still reveal focus. Restored focus is checked after the corresponding fresh layout bounds are available.

Qualification scope

The pure interaction and actual Runtime/RmlUi tests cover geometry, ownership, modal cancellation, snapshots, live density changes and invalid viewport recovery. Thumb border and padding participate in the measured minimum; a frame larger than its track is inert. Adjacent slider, choice and numeric-field size conversion also includes both border and padding, preserving the logical painted extent for content-box parts. Actual Rml geometry tests cover both box-sizing modes. The integrated engine captures and final suite results are recorded in .tmp/ui/production-retirement-integration/validation-evidence.json when complete. The first integrated Vulkan run exposed the adapter's unconditional action requirement; its failed capture is preserved beside the corrected qualification. These tests do not establish physical input, touch gestures, all transformed layouts, every screen migration or full platform and accessibility acceptance. The product requirement WID-010 remains incomplete until that work is qualified.