Redesign span layout around retained placement
This commit is contained in:
1 parent
ae0af8f5e3
commit
0aa03cf621
30 files changed
+501
-1321
No files matched your search
+18
-20
@@ -21,8 +21,8 @@ controller back, and gets `scroll`, `fling`, `drag`, `amt`,
|
||||
|
||||
Two widgets have one, and they differ only in how they spend a delta:
|
||||
|
||||
- **`ScrollArea`** (`scroll_area.rs`) — a fixed child, measured whole and
|
||||
then slid about as a lump, which is what makes a scroll tick an O(1)
|
||||
- **`ScrollArea`** (`scroll_area.rs`) — a fixed child, drawn and then
|
||||
slid about as a lump, which is what makes a scroll tick an O(1)
|
||||
move of one subtree. `.scrollable(axis, pin)` wraps anything in one.
|
||||
- **`LazySpan`** (`lazy_span.rs`) — lays its own rows out from an anchor,
|
||||
so it cannot be a lump and is not wrapped in anything. Its own
|
||||
@@ -126,21 +126,18 @@ The same direction for both owners, and a different origin:
|
||||
A scrollbar needs a real content length before it can use either, and a
|
||||
lazy span has none. Do not invent one.
|
||||
|
||||
## `ScrollArea::draw` — measure, then place
|
||||
## `ScrollArea::draw` — draw, then place
|
||||
|
||||
1. `take_delta`, and move to where it asks.
|
||||
2. Draw the child in a box as long as **last frame's** length, to measure
|
||||
it. This is free in the common case: the same region as last frame
|
||||
means `draw_inner` returns immediately.
|
||||
3. Apply the pin and clamp against the length just measured.
|
||||
4. Draw the child again, at that length and position.
|
||||
2. Offer the child **last frame's** length and read the size it reports.
|
||||
An unchanged child returns from `draw_inner` without running `draw`.
|
||||
3. Apply the pin and clamp against that size.
|
||||
4. Place the retained drawing at its exact length and position. It is
|
||||
redrawn only if its reported size does not fit that box.
|
||||
|
||||
Only the second draw decides anything, and a frame on which the content
|
||||
did change pays one real extra draw — a frame on which it was being
|
||||
redrawn anyway. Placing against the hint and letting the next frame fix it
|
||||
is what hung the composer's text half a line outside its box on Iris's
|
||||
phone: **layout is a pure function of the state, not of how many frames
|
||||
have been drawn**, and there may be no next frame.
|
||||
There is no measurement mode and no discarded drawing. Placing against
|
||||
the old length and letting the next frame correct it is not valid: layout
|
||||
must finish from the current state even if no later frame arrives.
|
||||
|
||||
The pin only re-pins on a frame with **no delta of its own**: the pin
|
||||
means "stay flush with the end as the content grows", and a reader who has
|
||||
@@ -157,11 +154,10 @@ can say how far it may go, so nothing above it is in a position to.
|
||||
|
||||
Measured 2026-09-08, and worth not re-deriving:
|
||||
|
||||
- A `Span` is skipped entirely in the steady state, but **when it is
|
||||
redrawn it costs two draws per child** (21 draws for 10 children):
|
||||
phase 1 offers each child the ambient region to learn its length,
|
||||
phase 2 offers it its real share. So any mutation of a `Span` redraws
|
||||
all of it — 24 draws for 11 children after one prepend.
|
||||
- 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
|
||||
redrawn only when its final box changes size.
|
||||
- A `ScrollArea`'s efficiency and virtualisation pull opposite ways: a
|
||||
scroll tick offers a same-size moved region, `draw_inner` takes the
|
||||
`mov` path, and the child's `draw` never runs. A virtualising child
|
||||
@@ -282,7 +278,9 @@ cannot pan; there is a `debug_assert` in `drag` naming that.
|
||||
even enter the widget. This is the number any "store the edges and only
|
||||
recompute what changed" optimisation would have to beat, and it is why
|
||||
the walk was left alone.
|
||||
- `Span`, redrawn: two draws per child (see above).
|
||||
- A fully hinted `Span` draws each child once. Unknown fixed children draw
|
||||
provisionally and move; region-dependent children redraw if their final
|
||||
box has a different size.
|
||||
- The one design that would collapse those 31 moves into a single delta
|
||||
write is moving the content as a unit, which needs a content length —
|
||||
which a lazy layout cannot supply.
|
||||
|
||||
Reference in new issue
Block a user