iris: a Scroll measures and places its content in the same frame

Follows Iris on the previous commit: "nothing in the framework should
ever self heal because it should not be drawn incorrectly in the first
place. If you need 2 draws to get something into the correct position
then that should happen within the same frame. Layout should never be
frame dependent, it should be a pure function of the state."

So `Scroll::draw` no longer places its child against last frame's
content length and asks for a corrective frame. It draws the child once
at that length purely to measure it, then places it at the length just
measured, with the end-pin and the clamp applied only to the second
placement -- the measure-then-place idiom `Span::draw` and `List::place`
already use. Last frame's length survives as a hint that keeps the
common case cheap: when the content's length did not change the two
regions are identical, so the first call is `draw_inner`'s O(1) `mov`
and the second returns at its first line. Nothing drawn depends on the
hint.

Reverts the frame-loop change from the previous commit (a frame that
left anything dirty asked for another), which existed only to deliver
that corrective frame and would have made any widget marking itself
dirty spin at full rate.

Knock-on: an end-anchored Scroll now sits at its end on its first drawn
frame rather than its second, since the end-pin no longer waits for a
length. Two layout tests that scroll down from what they assumed was the
top now build their area with `at_end: false`, which is what they meant.

`List::clamp_to_content` is the only next-frame correction left. Its
comment cited Scroll's lag as precedent, which no longer exists; it now
says it is a deviation from the rule, and docs/IRIS_TODO.md carries it.

Verified: the layer-1 test draws no settling frame and still passes; on
the emulator the caret's bottom is 1509 against a bar edge of 1535, 26px
inside a 31px padding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-08 17:12:26 -04:00
1 parent ba57086361
commit a00376994e
9 files changed
+181 -131

No files matched your search

+18
View File
@@ -7,6 +7,24 @@ order and what "done" looks like. Tick and date them in place.
## Fix
- [ ] **`List::clamp_to_content` still corrects on the next frame
(2026-09-08).** Iris's rule, stated while the composer's caret was
being fixed: "nothing in the framework should ever self heal because
it should not be drawn incorrectly in the first place. If you need 2
draws to get something into the correct position then that should
happen within the same frame. Layout should never be frame dependent,
it should be a pure function of the state." `Scroll::draw` was brought
to that rule the same day (it measures its content and places it
again in the one frame, IRIS.md's entry). `List::clamp_to_content` is
the one place left that has not been: it discovers a fling has run
past the content's end, calls `scroll(gap)` and `Painter::draw_again`,
and asks its own `RequestRedraw` handle for a frame -- so one frame is
drawn with the content past its end and the next one snaps it back.
The fix is the same shape as `Scroll`'s: re-place inside the draw that
found the gap. Not done in the same change because `List::place` is a
larger piece of machinery than `Scroll::draw` and this deserves its
own before/after on the phone.
- [x] **`request_device` asked for compute-shader limits it never uses
(2026-09-05).** `Limits::default()` (both `iris/src/android/render.rs`
and `iris/src/default/render.rs`) requests desktop-tier compute limits