iris: drive animation from painter frame time

This commit is contained in:
iris committed 2026-09-12 21:37:47 -04:00
1 parent 9fe6aca1f1
commit b1416d86bf
19 files changed
+171 -133

No files matched your search

+15 -7
View File
@@ -15,7 +15,7 @@ that struct (`scrollable.rs`); there is exactly one `Flinger` and one
`DragGesture` implementation in the crate's widgets. A widget with one
implements `Scrollable`, whose one required pair of methods hands the
controller back, and gets `scroll`, `fling`, `drag`, `amt`,
`is_scrolling`, `tick_fling` and the pin as default methods.
`is_scrolling` and the pin as default methods.
Two widgets have one, and they differ only in how they spend a delta:
@@ -30,6 +30,14 @@ Two widgets have one, and they differ only in how they spend a delta:
Do not give a widget its own fling, scroll amount, or platform wake
handle, and do not add scrolling methods to the general `Widget` trait.
There is no animation registry or animation callback on `Widget`. The
platform passes the frame's presentation time through `Painter`; a scrolling
widget advances its controller at the beginning of `draw` and calls
`Painter::request_next_frame` while the fling remains active. That request is
staged until the current retained redraw finishes, then dirties the requesting
widget for the following frame. Staging matters because the ordinary dirty set
is drained to completion within one frame.
## One convention for a delta
**Positive scrolls the reader up or left; negative down or right.** The
@@ -60,14 +68,14 @@ means "the end is not in sight". That last is not a placeholder — a lazy
layout genuinely cannot say how far its content runs without walking
there, and `clamp` takes the answer with no branch of its own.
**Why the delta is banked rather than applied where it arrives.** A wheel
event, a drag frame and a fling tick all land between draws, and none of
them can know whether there is content to move into. Applying them at the
layout that follows is also what keeps layout a pure function of the state
(Iris, 2026-09-08). The visible consequence, and the thing that catches a
**Why the delta is banked rather than applied where it arrives.** A wheel or
drag lands between draws, and advancing a fling happens at the top of a draw;
none of them can know whether there is content to move into until layout runs.
Applying them in that layout is also what keeps layout a pure function of the
state (Iris, 2026-09-08). The visible consequence, and the thing that catches a
test out: **`amt` does not move until the next draw.**
**Which clock a fling is ticked on.** The vsync the frame callback
**Which clock advances a fling.** The vsync the frame callback
carries, not `Instant::now()` -- on Android `do_frame`'s
`frame_time_nanos`, converted through the view's one `DeviceClock`
(`sense.rs`), which also dates every touch sample, so a fling is advanced