text_selection: drag-autoscroll never moved a virtualized list - #2948
text_selection: drag-autoscroll never moved a virtualized list#2948kossoy wants to merge 1 commit into
Conversation
|
Thanks for chasing this down — the symptom you instrumented (726 deltas dispatched, The synthetic wheel does move a virtualized listThe repo already has
The reason is that gpui's So the participant notification is not replacing a dead path — it is running alongside a live one. Consequence: two timers drive the same listAfter this change a scrollable
Autoscroll therefore runs at roughly double its previous speed (the table above), and because one writer is relative-from-now and the other absolute-from-last-frame, they race across repaints: when the participant timer ticks twice between paints, the wheel's stale base snaps the list backwards. That is visible jitter, not just a speed change. The dispatch needs to be exclusive — notify the participant or send the wheel. The natural seam is the The delta uses the wrong rectangle for a self-scrolling participant
Concretely, a scrollable The participant notification should use Smaller things
On the original symptomNone of this explains your field repro, and I'd rather not see it papered over. The wheel path works in the harness, so something in your layout keeps the list's hitbox out of the hit-test set at the clamped synthetic position — another hitbox painted above it, or the list not present in the frame being hit-tested. Worth identifying before locking in a fix, since the exclusive-dispatch design above depends on knowing which participants can rely on the wheel at all. One note for whoever lands this: the test at 🤖 Review assisted by Claude Code |
…g auto-scroll `update_auto_scroll` synthesized a wheel event at a position clamped inside the anchor participant's content mask. For a scrollable `TextView` whose list ends inside that mask (vertical padding on the view, or any layout where the view does not reach its clipping ancestor's edge), the clamped position lands in the band between the list's bottom and the mask's bottom, the list hitbox is not in the hit-test set, and the wheel never scrolls it. Registrations now carry a `self_scroll` flag; `TextView` sets it when `scrollable`. Dispatch is exclusive: a self-scrolling participant is notified through `TextSelectionEvent::AutoScroll` with a delta measured against its own bounds (as `update_participant_auto_scroll` already did), and the synthetic wheel is reserved for participants that scroll through an ancestor. Exactly one timer writes the list. `update_auto_scroll` takes `&Window`; the anchor lookup is shared by `anchor_participant` / `anchor_registration`. Tests: the existing harness test asserts a magnitude bound (ticks x per-frame delta) so a second writer fails it; a new test reproduces the padded reader layout and fails on main.
c6e8e2f to
bee77ea
Compare
|
Thanks for the measurements. Reproduced your table and reworked the branch (force-pushed, now Measurement
The double-write was real; the mechanism I described in the PR body was wrong. Changes per point
Field reproFound. The reader in the field layout is a clipping row containing a Probe on New test Verified: |
A drag held at the bottom of a padded, scrollable
TextViewcan leave its virtualized list stationary. The synthetic wheel position is clamped inside the nearest content mask, but the inner List ends above that edge. The event then lands in the padding, outside the List's hitbox.This change sends auto-scroll to self-scrolling participants through their own list API and reserves synthetic wheel events for participants that scroll through an ancestor. Dispatch is exclusive, so exactly one path drives the list.
Reproduction and measurements
Use a clipped row containing a
scrollable(true)TextView withpy_3(). Anchor a selection in the visible text, drag to the clipping edge and hold.In the padded harness on base
928c3eb776a3d733d9b771f7dea27a6a79242ced, the List ends at y=288 while the content mask ends at y=300. Over four ticks, a pointer at y=286, inside the List, moves it 52.33 px; y=298, in the padding, moves it 0 px.The maintainer's measurement and reproduction and rework results establish the cause and check the scroll magnitude:
Synthetic wheel events work when their position hits the List. Pointer capture is not the cause of the padded-reader failure.
Implementation
TextSelectionRegistrationcarries aself_scrollflag, set fromTextView::scrollable.update_auto_scrollroutes self-scrolling participants throughupdate_participant_auto_scrolland returns. That path measures against the participant's own bounds; only the ancestor-wheel path uses the content mask.update_auto_scrolltakes&Window;anchor_participantandanchor_registrationshare the anchor lookup.padded_reader_text_view_auto_scrolls_at_the_clipping_edgecovers the padding failure.Changes are in base text selection and the component selection harness. GPUI core is unchanged.
Validation
For current head
bee77eaa61ae23e5661a07edb9913e708f3b223c, the rework results record:cargo check -p gpui-base -p gpui-component.cargo test -p gpui-component --lib window_selection: 52 passed.text_selection,text::, andauto_scrolltests: 150 passed.All nine GitHub check runs on this head reported success when inspected on 2026-09-08. Maintainer review of the reworked head remains pending.