`.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.
146 lines
4.3 KiB
Rust
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();
|
|
}
|