Repository navigation
fast: Keep the cursor following the pointer while hover is frozen - #72
Conversation
Clearing the frame hit test during a wheel scroll also cleared the cursor, so the platform cursor flipped between the text cursor and the arrow on every frame. Hover styling still reports nothing hovered. The hit test stays the geometry under the pointer. The wheel test reads the platform cursor: it stays the text cursor while hover styling is held still, and becomes the arrow once the pointer leaves the text.
huacnlee
left a comment
There was a problem hiding this comment.
Moving the freeze from the hit test into hover is the right direction: the cursor goes through is_hovered_ignoring_last_input, so it follows the geometry. A few things need to change before this can merge.
1. A click within 100 ms of a wheel event, without moving the pointer, is dropped
While frozen, HitboxId::is_hovered now returns false for every hitbox. frozen() only checks the pointer position, and a mouse down at the same position doesn't change it. The div mouse down/up/click listeners all require hitbox.is_hovered(window) (elements/div.rs:135, :152, :168, …), so that click never reaches its handler.
Before this PR clicks still worked, because dispatch_mouse_event recomputes mouse_hit_test before dispatching (window.rs:6006). Two possible fixes:
- end the freeze on any non-wheel pointer input (mouse down/up), or
- apply the freeze only to hover styling while drawing, not to event dispatch.
Please add a test: wheel, then mouse down/up at the same position, and assert that the click handler runs.
2. script/check-upstream fails (2 violations)
platform/test/platform.rs:428:TestPlatform::active_cursor_styleis a new method in an upstream file.window.rs:3018:Window::set_owns_pointer(plus its doc comment) is a new method in an upstream file.window.rsis also over the budget (+112 > 99, −37 > 36).
Methods on upstream types belong in an impl block under fast/ (e.g. a #[cfg(test)] impl in fast/tests/ or the topic module). The upstream side should only bump visibility where needed (active_cursor, active, hovered → pub(crate)).
3. Nit: the freeze check is duplicated
HitboxId::is_hovered (window.rs) and hovered_now (fast/retained.rs) now both repeat last_input_was_keyboard() || hover_freeze::frozen(window). A single fast/ helper called from both places would keep the window.rs hook to one call.
While hover was held still after a wheel event, `HitboxId::is_hovered` answered false outside drawing too. A mouse listener asks it before it runs, so a click under a still pointer within 100 ms of the wheel never reached its handler. Holding is now for drawing only: event dispatch reads the geometry under the pointer, as it did before the hit test was left intact. `hover_freeze::hides_hover` is the one check, for `is_hovered` and for what a retained subtree records of hover, so `window.rs` keeps a single hook. `hover_freeze::hit_test` had become upstream's own line, so that hook is back to upstream's. The test-only `TestPlatform::active_cursor_style` and `Window::set_owns_pointer` move to `fast/tests/support.rs`; upstream files only make `active_cursor`, `active` and `hovered` `pub(crate)`. A new test clicks right after the wheel at the same position and expects the handler to run. The two wheel tests no longer ask `is_hovered` between frames, where it now reports the geometry; that hover styling is held still is shown by no row being rendered for its hover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, addressed in bdfeda0:
The two wheel tests no longer assert |
longbridge#73 keeps a list row's render- and layout-time element states while its layer keeps it. longbridge#71 moves hover-freeze and profiling onto scheduler's clock for WASM. longbridge#72 stops emptying the hit test while hover is frozen, so the cursor follows the pointer and a click right after the wheel reaches its handler. The fork's hover freeze keeps hovered what has not moved since the wheel began (the panel around a list), which upstream's drops. With longbridge#72 the two meet in `hides_hover`: the hit test stays real, and while a frame is drawn with hover held still only what hover holds where it was reads as hovered. Both sides' tests are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r as drawn in retained checks longbridge#72 moved the freeze into hover itself, so every `is_hovered` while a frame was drawn asked `frozen`, which reads the clock. A wheel frame of a scrolled child view cost 18 % more instructions (0.82 M to 0.97 M, gpui_perf --headless). The frame now takes whether hover is held as it begins, with what it holds, and hover reads that. `hovered_now`, what a retained subtree records of a hover and the check that the recorded hovers still hold, now reads hover as a frame draws it whatever the phase: that check runs as a frame begins, before its phases, where the freeze would otherwise read as a changed hover. The child-view wheel frame is now 2.6 % over the fork before longbridge#72 (0.821 M to 0.843 M instructions), what the cursor following the pointer costs; the median scenario moves 0.1 %. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#57 holds hover styling still for 100 ms after a wheel event while the pointer stays put. It did this by emptying the window's hit test (
mouse_hit_test). The cursor is resolved from that same hit test, so every frozen frame set the arrow over text, and the next wheel event restored the text cursor: the cursor flickered while scrolling.Change
hover_freeze::begin_drawis removed andhover_freeze::hit_testalways returns the real hit test.HitboxId::is_hovered(andhovered_now, its retained-view twin) returnsfalsewhilehover_freeze::frozenis true.is_hovered_ignoring_last_input, which doesn't check the freeze, so the cursor follows the geometry under the pointer.reset_cursor_styleis unchanged.fast/layers/lists.rs), so rows aren't rendered again for hover.Tests
the_cursor_follows_the_text_under_a_still_pointer_while_hover_is_frozen. The text cursor stays over text throughout the freeze, and the arrow comes back once the pointer leaves the text.hover_held_still_while_the_wheel_scrolls_renders_no_row_for_itnow asserts that the hit test is non-empty while no hitbox reports hovered.TestPlatform::active_cursor_styleandWindow::set_owns_pointer(#[cfg(test)]).