Prune commentary and stale Rust port notes
This commit is contained in:
1 parent
5428cd75c9
commit
25370731d0
193 files changed
+693
-16219
No files matched your search
+19
-81
@@ -1,8 +1,6 @@
|
||||
# Scrolling in iris
|
||||
|
||||
How anything in iris scrolls, as of 2026-09-09. This is the current
|
||||
design, not a history — the git log has the account of
|
||||
how it got here, and `docs/IRIS_TODO.md` has what is still open.
|
||||
This is the current scrolling design; `docs/IRIS_TODO.md` holds open work.
|
||||
|
||||
Read this before touching `iris/src/widget/position/scrollable.rs`,
|
||||
`scroll_area.rs`, `lazy_span.rs`, or anything that pans, flings or lays
|
||||
@@ -29,13 +27,8 @@ Two widgets have one, and they differ only in how they spend a delta:
|
||||
`.scrollable()` registers the same two senses against the controller it
|
||||
already has.
|
||||
|
||||
Do not give a widget its own fling, its own scroll amount, or a
|
||||
`RequestRedraw` handle. And do not add a scrolling method to the `Widget`
|
||||
trait: the three that used to be there (`scrolls_itself`, `apply_scroll`,
|
||||
`scroll_offset`) existed only so a `Scroll` could drive a `LazySpan` it
|
||||
had no business wrapping, and they are gone (Iris, 2026-09-08: "I don't
|
||||
like adding methods to widget, it seems like we can structure things
|
||||
better instead").
|
||||
Do not give a widget its own fling, scroll amount, or `RequestRedraw`
|
||||
handle, and do not add scrolling methods to the general `Widget` trait.
|
||||
|
||||
## One convention for a delta
|
||||
|
||||
@@ -45,25 +38,9 @@ positive delta — the finger's direction — and that is `Scroll::scroll`'s
|
||||
sign, `Scroll::fling`'s, and `Widget::apply_scroll`'s, from the gesture
|
||||
all the way down to a row's anchor.
|
||||
|
||||
**It is a screen direction, not a logical one** (Iris, 2026-09-08:
|
||||
"positive should always scroll up / left, and negative down / right ...
|
||||
that way it always works as the user would expect"). The earlier wording
|
||||
— "positive brings *earlier* content into view" — is true only of a span
|
||||
laid out forwards: a `Dir::UP` list's earlier content is *below*, so the
|
||||
same delta panned it the opposite way from every other scrollable in
|
||||
iris. `LazySpan::flip_delta` is the conversion into the walk's own
|
||||
direction-relative space, the exact counterpart of `flip_pos` for
|
||||
positions, and its `scroll` (private) is the only thing that speaks that
|
||||
space.
|
||||
|
||||
There used to be two public conventions under the same name, and every
|
||||
call site had to know which widget it was talking to. If you add a third
|
||||
scrolling thing, it takes this one. Two tests pin it, and neither is
|
||||
redundant: `a_negative_delta_moves_toward_the_end` follows the sign
|
||||
across the whole handoff, and `a_delta_moves_both_directions_the_same_
|
||||
way_on_screen` checks the two `dir`s against **where rows were drawn** —
|
||||
an assertion written in the walk's own space passes with the flip
|
||||
deleted, because it checks the bookkeeping against itself.
|
||||
It is a screen direction, not a logical content direction. A `Dir::UP`
|
||||
span's earlier content is below, so `LazySpan::flip_delta` converts public
|
||||
screen-space deltas into the walk's direction-relative space.
|
||||
|
||||
## The contract between a controller and its owner
|
||||
|
||||
@@ -98,8 +75,7 @@ on the clock its own velocity was measured on. Frames are presented on an
|
||||
even cadence whatever clock they are computed on, so sampling the spline
|
||||
at "whenever the callback got to run" moves the content unevenly between
|
||||
frames that are shown evenly -- a shimmer that no frame-time percentile
|
||||
can see, since no frame was late. Found 2026-09-09; docs/RUST.md's
|
||||
"The fling stutter" has the rest.
|
||||
can see, since no frame was late. `docs/RUST.md` records the measurements.
|
||||
|
||||
### Why a remainder was not enough
|
||||
|
||||
@@ -152,8 +128,6 @@ can say how far it may go, so nothing above it is in a position to.
|
||||
|
||||
### Why it is not a `Span` inside a `ScrollArea`
|
||||
|
||||
Measured 2026-09-08, and worth not re-deriving:
|
||||
|
||||
- A `Span` is skipped entirely in the steady state. When redrawn, it uses
|
||||
exact hints first, draws unknown fixed children forward from the cursor,
|
||||
and places retained drawings after flexible allocation. A child is
|
||||
@@ -179,14 +153,9 @@ A transcript is `Dir::DOWN` (oldest message is item 0, at the top) with
|
||||
`Pin::End` (the view sits at the bottom). Conflating the two would stand
|
||||
it on its head.
|
||||
|
||||
**`Pin` says it either way round**, because there are two questions and
|
||||
they are not the same one (Iris, 2026-09-08). `Start`/`End` are
|
||||
content-relative — the first row or the newest one, wherever the layout
|
||||
puts it — and `Neg`/`Pos` are axis-absolute: the top/left edge and the
|
||||
bottom/right one, whichever end of the content is there. They coincide for
|
||||
everything except a reversed `LazySpan`, where they are exact opposites,
|
||||
which is the whole reason both exist. The one question a scrollable acts
|
||||
on is `pinned_to_end`, and `dir` is what resolves a `Pin` into it.
|
||||
`Start`/`End` are content-relative; `Neg`/`Pos` are axis-absolute. They
|
||||
diverge for a reversed `LazySpan`. A scrollable acts on `pinned_to_end`,
|
||||
with `dir` resolving the chosen `Pin`.
|
||||
|
||||
### Two coordinate spaces, two conversion points
|
||||
|
||||
@@ -217,8 +186,8 @@ framework:
|
||||
old-children diff calls `remove_rec`, and the `ActiveData` — with its
|
||||
`size` — is freed. The framework's copy is gone for precisely the rows
|
||||
the walk has to pass through without drawing.
|
||||
2. **A widget may one day render in two places at once** (Iris,
|
||||
2026-09-08), so anything keyed by `WidgetId` alone that describes where
|
||||
2. **A widget may render in two places at once**, so anything keyed by
|
||||
`WidgetId` alone that describes where
|
||||
or how big a widget was drawn will be wrong then. Where and how big
|
||||
belongs to the owner that placed it.
|
||||
|
||||
@@ -242,8 +211,8 @@ inside the same frame**. `moved_by` counts that correction along with the
|
||||
move that caused it, which is why `amt` stays equal to what is on screen
|
||||
rather than drifting by every overshoot.
|
||||
|
||||
Layout is a pure function of the state, not of how many frames have been
|
||||
drawn (Iris, 2026-09-08). A correction that lands next frame is a frame
|
||||
Layout is a pure function of state, not of how many frames have been
|
||||
drawn. A correction that lands next frame is a frame
|
||||
drawn wrong, and there may be no next frame — a fling that stopped is not
|
||||
asking for one.
|
||||
|
||||
@@ -305,40 +274,9 @@ cannot pan; there is a `debug_assert` in `drag` naming that.
|
||||
rebased after 65,536 pixels to preserve `f32` precision, a rare O(visible)
|
||||
move-slot pass rather than steady-state work.
|
||||
|
||||
## Tests that pin the behaviour
|
||||
## Verification
|
||||
|
||||
In `lazy_span.rs`, all of these fail if the corresponding piece is undone:
|
||||
|
||||
- `a_negative_delta_moves_toward_the_end` — the sign, end to end.
|
||||
- `a_delta_moves_both_directions_the_same_way_on_screen` — the sign is a
|
||||
screen direction, checked against where rows were *drawn*.
|
||||
- `amt_counts_only_what_the_child_could_take` — why the owner reports what
|
||||
it did rather than the caller adding up what it asked for.
|
||||
- `a_fling_stops_at_the_first_row`,
|
||||
`scrolling_past_the_start_lands_on_it_in_the_same_frame` — the walls,
|
||||
with no settling frame drawn on purpose.
|
||||
- `a_dir_up_span_grows_upward_from_item_zero`,
|
||||
`a_reversed_span_hit_tests_in_screen_space` — the position conversions
|
||||
(`flip_pos`), as `a_delta_moves_both_directions_the_same_way_on_screen`
|
||||
is the delta one (`flip_delta`).
|
||||
- `a_registered_fling_is_driven_by_tick_animations_and_then_unregisters` —
|
||||
a fling that nothing registers never moves, whatever its velocity.
|
||||
|
||||
In `app-rust/tests/` (layer 1, no window or GPU):
|
||||
|
||||
- `top_edge.rs`'s `scrolling_past_the_first_row_settles_on_it` /
|
||||
`scrolling_past_the_last_row_settles_on_it` — both ends, no settling
|
||||
frame.
|
||||
- `phone_screen.rs`'s `a_recorded_flick_releases_with_a_velocity_and_
|
||||
flings_the_list` — the velocity against
|
||||
`benches/velocity_reference.py`'s number, and the fling's travel against
|
||||
`benches/fling_spline_reference.py`'s.
|
||||
- `phone_screen.rs`'s `a_long_press_and_drag_selects_text` — what caught
|
||||
two `DragGesture`s fighting over the transcript.
|
||||
- `catch_a_fling.rs`, `gesture_cancel.rs`, `fence_fling.rs` — press-catches
|
||||
a coasting area, cancels, and a code fence panning sideways
|
||||
independently of the transcript.
|
||||
|
||||
`docs/RUST.md`'s "Three test layers" says which layer answers what. Test
|
||||
at the cheapest one that can answer the question; the emulator is for JNI,
|
||||
the IME, insets and one verification run, not for iterating on layout.
|
||||
The unit and headless integration tests exercise direction, both walls,
|
||||
reversed hit-testing, fling registration, cancellation, nested horizontal
|
||||
pans, and transcript selection. `docs/RUST.md` defines the three test layers;
|
||||
use the cheapest layer that can observe the behavior under test.
|
||||
Reference in new issue
Block a user