Files
iris/tests/trace_unsettled.rs
T
iris-ai 8220a78d4a Carry a length as a rule beside a widget, not a widget around it
`.width()` built a `SetSize` whose whole job was to answer `size_hint`, so
every declared length cost a widget, an `ActiveData` and a link of chain to
say one number. It is now a `SizeRule` per axis on `WidgetData`, beside
`region_node`, resolved by `Painter` where the widget is drawn. `SetSize`
and `MaxSize` are gone; `MaxSize` had no caller but its own builders.

That settles which of two answers is the size. A rule wins on the axis it
names and the `Size` returned by `draw` answers the rest, applied once in
`draw_inner` rather than by each widget that could carry one -- so the
widget under a rule never learns of it. `Painter::size_hint` reads the rule
first for the same reason: a rule that beats what a widget would draw has
to beat what it says about itself.

`declared_lens` still falls back to a non-leftover `size_hint`, which is
how an image or a gap gets its own pixel size rather than the whole offer.
That is the offer's business rather than a declaration's, and it falls away
when a widget occupies its reported size inside the box it was offered.

`known` and `declared` are separate because a share is a length to whoever
divides one and not to whoever composes a box: `.width(leftover(3))` is
known without drawing but cannot narrow anything.

Checked: fmt, clippy, 85 tests, and 100 generated seeds agreeing warm
against cold in 67.6 s. `minimal`, `text` and `view` render byte-identical
at 1920x1200; `tabs` differs only in the widget count it prints about
itself, which is two wrapper types smaller.
2026-09-15 19:44:21 -04:00

146 lines
4.3 KiB
Rust

//! Traces the six-widget tree in `unsettled.rs`, to see what box its text is
//! actually drawn in on a first frame against a settled one.
#![cfg(feature = "layout-diagnostics")]
use iris::core::layout_diagnostics::{self as diag, TraceEvent};
use iris::harness::Harness;
use iris::prelude::*;
fn plant(h: &mut Harness) -> Vec<WidgetId> {
let plain = wtext("Wrapping").size(16).wrap(false).add(&mut h.rsc);
let wrapped = wtext("Wrapping shapes").size(16).wrap(true).add(&mut h.rsc);
let sized = wrapped.width(76).add(&mut h.rsc);
let aligned = Aligned {
inner: sized.add_strong(&mut h.rsc),
align: Align {
x: Some(AxisAlign::Pos),
y: Some(AxisAlign::Pos),
},
}
.add(&mut h.rsc);
let stack = Stack {
children: vec![plain.add_strong(&mut h.rsc), aligned.add_strong(&mut h.rsc)],
size: StackSize::Child(0),
}
.add(&mut h.rsc);
let root = (stack,).span(Dir::RIGHT).add(&mut h.rsc);
h.state.root = Some(root.add_strong(&mut h.rsc));
vec![
plain.id(),
wrapped.id(),
sized.id(),
aligned.id(),
stack.id(),
root.id(),
]
}
fn dump(label: &str, report: &diag::Report, text: WidgetId) {
println!("--- {label} ---");
for event in report.traces() {
match event {
TraceEvent::DrawRequest {
id,
region,
pixel_size,
..
} if *id == text => {
println!(
" draw in {:.2}x{:.2} region {region:?}",
pixel_size.x, pixel_size.y
)
}
TraceEvent::SizeReported { id, size } if *id == text => {
println!(" reported {size}")
}
TraceEvent::SizeRead { id, reader, size } if *id == text => {
println!(" size read by {reader:?}: {size}")
}
TraceEvent::RegionNode { id, parent, region } if *id == text => {
println!(" region node under {parent:?} at {region:?}")
}
TraceEvent::Reuse { id, outcome } if *id == text => println!(" reuse: {outcome:?}"),
_ => {}
}
}
}
#[test]
#[ignore = "a diagnostic, not a check"]
fn what_box_the_text_is_drawn_in() {
diag::clear_traced_widgets();
let _ = diag::take();
let mut h = Harness::new((640, 900));
let ids = plant(&mut h);
let text = ids[1];
diag::trace_widget(text);
let _ = diag::take();
h.frame();
dump("first frame", &diag::take(), text);
for _ in 0..2 {
for &id in &ids {
h.rsc.widgets_mut().get_dyn_mut(id);
}
let _ = diag::take();
h.frame();
dump("repaint", &diag::take(), text);
}
diag::clear_traced_widgets();
}
fn plant_fixed(h: &mut Harness) -> Vec<WidgetId> {
let words = "Wrapping shapes one source into as many lines as the box leaves";
let text = wtext(words).size(16).wrap(true).add(&mut h.rsc);
let aligned = Aligned {
inner: text.add_strong(&mut h.rsc),
align: Align {
x: Some(AxisAlign::Neg),
y: None,
},
}
.add(&mut h.rsc);
let inner = (aligned,).span(Dir::RIGHT).add(&mut h.rsc);
let sized = inner.sized((189, 176)).add(&mut h.rsc);
let filler = rect(Color::RED).add(&mut h.rsc);
let root = (filler, sized).span(Dir::RIGHT).add(&mut h.rsc);
h.state.root = Some(root.add_strong(&mut h.rsc));
vec![
text.id(),
aligned.id(),
inner.id(),
sized.id(),
filler.id(),
root.id(),
]
}
#[test]
#[ignore = "a diagnostic, not a check"]
fn what_box_the_fixed_text_is_drawn_in() {
diag::clear_traced_widgets();
let _ = diag::take();
let mut h = Harness::new((1920, 1200));
let ids = plant_fixed(&mut h);
let text = ids[0];
diag::trace_widget(text);
let _ = diag::take();
h.frame();
dump("first frame at 1920", &diag::take(), text);
h.resize((640, 900));
h.frame();
dump("after resize to 640", &diag::take(), text);
let mut cold = Harness::new((640, 900));
let cids = plant_fixed(&mut cold);
diag::clear_traced_widgets();
diag::trace_widget(cids[0]);
let _ = diag::take();
cold.frame();
dump("cold at 640", &diag::take(), cids[0]);
diag::clear_traced_widgets();
}