Size a widget while drawing it, not in a pass of its own
`desired_width`/`desired_height`, `WidgetAxisFns`, `SizeCtx` and the size cache are gone. A widget states what it used with `Painter::set_size` while it draws, and `Painter::widget` hands back a `DrawResult` whose `size()` both reads the child and records that this widget's size depends on it. Reading nothing keeps the parent independent of what the child came to. `Span` is what the change is for. It takes each child's `size_hint` where there is one, draws only the children that cannot answer, allocates the flexible space, then places everything -- which deletes `desired_ortho`, whose own comment said it "literally copies draw ... which makes this slow and not cool". Invalidation follows the dependency edges the draw recorded: a widget that needs redrawing hands off to the highest ancestor that read its size, instead of re-running a measurement to find out whether anything changed. `Widget::size_dependence(axis)` says how much of its box a widget's drawing depends on -- none of it, its own extent, or the whole box -- so the retained path can keep a drawing and write a new box into it. Asked per axis, because wrapped text depends on the width it is offered and not on the height. `Internal` does not yet buy more than `External`: keeping a drawing when only the room around it changed is a translation, which waits for the move chain. `tests/retained.rs` covers the second frame rather than the first, which is where the bugs were: a placed child that was not recorded as one got pruned as departed on the next draw. `examples/text.rs` is new, since wrapping was the one thing here with no way to see it on its own.
This commit is contained in:
1 parent
43ce8c7d02
commit
f192f75b25
24 files changed
+610
-523
No files matched your search
@@ -0,0 +1,63 @@
|
||||
//! Text sizing: wrapped text reads the width it is offered, fixed text does
|
||||
//! not, and both report a height their container lays out around.
|
||||
|
||||
use iris::prelude::*;
|
||||
|
||||
fn main() {
|
||||
DefaultApp::<State>::run();
|
||||
}
|
||||
|
||||
#[derive(DefaultUiState)]
|
||||
struct State {
|
||||
ui_state: DefaultUiState,
|
||||
}
|
||||
|
||||
const SAMPLE: &str = "Wrapping shapes one source into as many lines as its container \
|
||||
leaves room for, so the height of a paragraph is an answer rather than a setting, and \
|
||||
the same words in a narrower box come back taller. Resize the window and watch the \
|
||||
text below reflow into a different number of lines while nothing about it changes.";
|
||||
|
||||
impl DefaultAppState for State {
|
||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, _: Proxy<Self>) -> Self {
|
||||
let panel = || rect(Color::WHITE.darker(0.85));
|
||||
|
||||
let wrapped = wtext(SAMPLE)
|
||||
.size(28)
|
||||
.wrap(true)
|
||||
.text_align(Align::LEFT)
|
||||
.pad(16)
|
||||
.background(panel());
|
||||
|
||||
let aligned = (
|
||||
wtext("left").size(24).text_align(Align::LEFT),
|
||||
wtext("centred").size(24).text_align(Align::CENTER),
|
||||
wtext("right").size(24).text_align(Align::RIGHT),
|
||||
)
|
||||
.span(Dir::DOWN)
|
||||
.gap(8)
|
||||
.pad(16)
|
||||
.background(panel());
|
||||
|
||||
// The same words in half the width, which is a different number of
|
||||
// lines and so a different height. A declared width only holds along
|
||||
// a span's own axis, hence the row.
|
||||
let narrow = (
|
||||
wtext(SAMPLE)
|
||||
.size(20)
|
||||
.wrap(true)
|
||||
.pad(16)
|
||||
.background(panel())
|
||||
.align(Align::TOP)
|
||||
.width(rel(0.5)),
|
||||
rect(Color::WHITE.darker(0.95)),
|
||||
)
|
||||
.span(Dir::RIGHT);
|
||||
|
||||
(wrapped, aligned, narrow)
|
||||
.span(Dir::DOWN)
|
||||
.gap(12)
|
||||
.pad(12)
|
||||
.set_root(rsc, &mut ui_state);
|
||||
Self { ui_state }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user