iris: a Rect is not size-independent, and P1a's block appearance verified

The defect P1a's screenshots found, and the one that mattered:
`Rect::is_size_independent()` answered `true`. A `Rect` fills whatever
region it is handed, so its content *is* the region -- and
`draw_inner`'s fast path, which rewrites a widget's primitives with
`r.outside(&from).within(&region)` instead of redrawing it, cannot
reproduce that once a region carries both `rel` and `abs`. What it
looked like: a fenced code block's background kept the height of the
provisional full-region draw `Span` does in its first phase, so one
fence's panel covered every block below it and every row below that,
with the text underneath laid out correctly. Likely the same cause as
RUST.md's older "the composer bar's grey background is not drawn".

Also here: a quote's bar is a `Stack` background behind padded text
rather than a two-child `Span(Dir::RIGHT)` (one widget fewer and no
provisional pass), and `transcript-ui`'s `transcript` example gains a
row holding one of every block kind -- the fixture's own heading,
paragraph, fence and table source, plus a list and a quote, which the
fixture has neither of.

docs/bench/p1a-2026-09-06/ has the pairs and docs/RUST.md's P1a box
names what still differs. The iris half is from the desktop backend
because this emulator cannot draw iris's glyphs at all (solid boxes,
reproduced on the previous commit, with Compose drawing text correctly
on the same AVD); both routes to Vulkan on this AVD were tried and both
fail. Bench stream phase, assertions live, no abort: p50 53.0ms p90
108.6ms p99 132.0ms against 52.8/108.1/137.3 before -- unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-06 19:30:39 -04:00
1 parent 64f64b54e5
commit 69525bd131
11 files changed
+355 -34

No files matched your search

+23 -1
View File
@@ -47,8 +47,30 @@ impl Widget for Rect {
Size::REST // fills whatever it was given -- used == available
}
/// **No** -- despite drawing one primitive and nothing else.
///
/// `is_size_independent` asks whether the widget's *content* is
/// unaffected by how big a region it was given, so that
/// `draw_inner` may keep the primitives it already has and rewrite
/// their regions in place. A `Rect`'s content **is** its region: it
/// returns `Size::REST` and fills whatever it was handed, so the fast
/// path's `r.outside(&from).within(&region)` remap has to reproduce
/// the whole of `draw` -- and it does not, because a region carries
/// `rel` and `abs` components that the round trip cannot recover
/// separately.
///
/// What that looked like: a fenced code block's background
/// (`transcript-ui`'s `BlockFrame::Verbatim`, a `Rect` behind a
/// `Pad` in a `Stack`) kept the height of the *provisional* full-
/// region draw `Span` does in its first phase, so one fence's panel
/// covered every block below it -- and every row below that -- while
/// the text itself was laid out correctly. Visible in
/// `docs/bench/p1a-2026-09-06/`'s history and reproduced by this
/// crate's `transcript` example. Answering `false` costs a redraw of
/// one primitive when a rect is resized, which is what the fast path
/// was saving.
fn is_size_independent(&self) -> bool {
true // content never depends on region size
false
}
}
+39 -5
View File
@@ -91,14 +91,48 @@ fn synthetic_rows() -> Vec<FoldedRow> {
},
]),
msg(6, true, "Looks good, thanks!"),
msg(
7,
false,
"You're welcome. Let me know if you'd like anything else.",
),
// Every block kind `client_core::markdown_blocks` names, in one
// row, so P1a's appearance can be looked at against the Compose
// app's without a server (docs/RUST.md's P1a box). The heading,
// paragraph, fence and table are the *same source* the bench
// fixture carries (`app/bench-fixture/generate.py`), so the two
// screenshots differ only in the renderer; the list and the quote
// are extra, because the fixture has neither.
msg(7, false, BLOCK_SAMPLER),
]
}
/// One of each markdown block, for the P1a screenshot pair. See
/// [`synthetic_rows`].
const BLOCK_SAMPLER: &str = "\
## What changed
Iris **fold** render measure session window anchor context transcript \
iris measure iris scroll call transcript layout *cursor* context, and a \
[bench](https://example.com/bench) link.
```rust
fn fold_event(items: Vec<Item>, seq: u64) -> Vec<Item> {
// a comment worth keeping: this is the fold the app's own screen runs
let mut out = items;
out.push(Item::new(seq));
out
}
```
| column | value |
|---|---|
| a | measure place draw tool call token context window anchor |
- one bullet
- another, with `inline code`
- nested one level
1. first numbered
2. second numbered
> A quoted line, to show the bar and the indent.
";
impl DefaultAppState for Client {
fn new(
mut ui_state: DefaultUiState,
+14 -6
View File
@@ -273,12 +273,20 @@ where
.width(rest(1))
.add_strong(rsc)
.any(),
BlockFrame::Quote => (
rect(crate::markdown::QUOTE_BAR_COLOR).width(dp(QUOTE_BAR_DP)),
field.width(rest(1)),
)
.span(Dir::RIGHT)
.gap(dp(FRAME_PAD_DP))
// A `Stack` (through `background`) rather than a two-child
// `Span(Dir::RIGHT)`: the bar is drawn behind text padded past
// it, which is the same picture with one widget fewer and
// without `Span`'s provisional full-region pass. That pass is
// also what first surfaced the `mov`-then-`reposition` assert
// docs/RUST.md's P1a box records as still open, so the shape
// with fewer passes is the one to prefer here.
BlockFrame::Quote => field
.width(rest(1))
.pad(Padding {
left: dp(QUOTE_BAR_DP + FRAME_PAD_DP),
..Padding::ZERO
})
.background(rect(crate::markdown::QUOTE_BAR_COLOR).width(dp(QUOTE_BAR_DP)))
.width(rest(1))
.add_strong(rsc)
.any(),