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.
This commit is contained in:
1 parent
0283c9d6c7
commit
8220a78d4a
21 files changed
+252
-214
No files matched your search
@@ -158,6 +158,13 @@ impl Harness {
|
||||
self.render.resize(size);
|
||||
}
|
||||
|
||||
/// Changes a length rule after the fact, the way `.width()` sets one.
|
||||
pub fn set_len(&mut self, id: impl IdLike, axis: Axis, len: impl Into<Len>) {
|
||||
self.rsc
|
||||
.widgets_mut()
|
||||
.set_size_rule(id, axis, SizeRule::Exact(len.into()));
|
||||
}
|
||||
|
||||
/// Sets the root and lays it out, so a pointer event has something to hit.
|
||||
pub fn set_root<T>(&mut self, widget: impl WidgetLike<DefaultRsc<HarnessState>, T>) {
|
||||
widget.set_root(&mut self.rsc, &mut self.state);
|
||||
|
||||
+9
-11
@@ -8,14 +8,14 @@
|
||||
use crate::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The declared lengths of one `SetSize`, by axis.
|
||||
/// The declared lengths of one widget carrying a size rule, by axis.
|
||||
pub type Lens = [Option<Len>; 2];
|
||||
|
||||
/// What a test changes between two trees grown from the same seed, so the
|
||||
/// warm one can be mutated and the cold one grown that way to begin with.
|
||||
#[derive(Default)]
|
||||
pub struct Edits {
|
||||
/// Declared sizes, by the order the `SetSize` wrappers were made.
|
||||
/// Declared sizes, by the order the rules were put on.
|
||||
pub sizes: HashMap<usize, Lens>,
|
||||
/// Which children a span has, by the order the spans were made.
|
||||
pub spans: HashMap<usize, SpanEdit>,
|
||||
@@ -75,7 +75,7 @@ const WORDS: &str = "Wrapping shapes one source into as many lines as the box \
|
||||
#[derive(Default)]
|
||||
pub struct Tree {
|
||||
pub ids: Vec<WidgetId>,
|
||||
pub sized: Vec<WeakWidget<SetSize>>,
|
||||
pub sized: Vec<WidgetId>,
|
||||
pub spans: Vec<Spanned>,
|
||||
pub scrolls: Vec<WeakWidget<Scroll>>,
|
||||
/// Children a `SpanEdit` took out, held so that dropping the last share
|
||||
@@ -201,15 +201,13 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
|
||||
let idx = self.tree.sized.len();
|
||||
let lens = [self.len(), self.len()];
|
||||
let lens = self.edits.sizes.get(&idx).copied().unwrap_or(lens);
|
||||
let id = SetSize {
|
||||
inner,
|
||||
x: lens[0],
|
||||
y: lens[1],
|
||||
}
|
||||
.add(self.rsc);
|
||||
let id = inner.id();
|
||||
self.rsc
|
||||
.ui_mut()
|
||||
.widgets
|
||||
.set_size_rules(id, lens[0], lens[1]);
|
||||
self.tree.sized.push(id);
|
||||
self.tree.ids.push(id.id());
|
||||
id.add_strong(self.rsc)
|
||||
inner
|
||||
}
|
||||
|
||||
fn node(&mut self, depth: usize) -> StrongWidget {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct MaxSize {
|
||||
pub inner: StrongWidget,
|
||||
pub x: Option<Len>,
|
||||
pub y: Option<Len>,
|
||||
}
|
||||
|
||||
impl Widget for MaxSize {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
let child = painter.widget(&self.inner).size();
|
||||
let own = painter.px_size();
|
||||
Size {
|
||||
x: capped(child.x, self.x, own.x),
|
||||
y: capped(child.y, self.y, own.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn capped(len: Len, max: Option<Len>, own: f32) -> Len {
|
||||
match max {
|
||||
Some(max) if len.apply_leftover().to_px(own) > max.apply_leftover().to_px(own) => max,
|
||||
_ => len,
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,15 @@
|
||||
mod align;
|
||||
mod layer;
|
||||
mod max_size;
|
||||
mod offset;
|
||||
mod pad;
|
||||
mod scroll;
|
||||
mod set_size;
|
||||
mod span;
|
||||
mod stack;
|
||||
|
||||
pub use align::*;
|
||||
pub use layer::*;
|
||||
pub use max_size::*;
|
||||
pub use offset::*;
|
||||
pub use pad::*;
|
||||
pub use scroll::*;
|
||||
pub use set_size::*;
|
||||
pub use span::*;
|
||||
pub use stack::*;
|
||||
@@ -1,30 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct SetSize {
|
||||
pub inner: StrongWidget,
|
||||
pub x: Option<Len>,
|
||||
pub y: Option<Len>,
|
||||
}
|
||||
|
||||
impl Widget for SetSize {
|
||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||
// Nothing to apply: a declared length is taken where this widget is
|
||||
// drawn, so the box it has already is that length, and `leftover` is a
|
||||
// share only whoever divides a length can work out. Both reach them
|
||||
// through `size_hint`.
|
||||
let child = painter.widget(&self.inner).size();
|
||||
Size {
|
||||
x: self.x.unwrap_or(child.x),
|
||||
y: self.y.unwrap_or(child.y),
|
||||
}
|
||||
}
|
||||
|
||||
/// A declared axis is known without looking at the child, which is what
|
||||
/// lets a span lay out around `.height(leftover(1))` without drawing it.
|
||||
fn size_hint(&self, axis: Axis) -> Option<Len> {
|
||||
match axis {
|
||||
Axis::X => self.x,
|
||||
Axis::Y => self.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-33
@@ -39,48 +39,38 @@ widget_trait! {
|
||||
}
|
||||
}
|
||||
|
||||
fn sized(self, size: impl Into<Size>) -> impl WidgetFn<Rsc, SetSize> {
|
||||
fn sized(self, size: impl Into<Size>) -> impl WidgetIdFn<Rsc, WL::Widget> {
|
||||
let size = size.into();
|
||||
move |state| SetSize {
|
||||
inner: self.add_strong(state),
|
||||
x: Some(size.x),
|
||||
y: Some(size.y),
|
||||
move |state| {
|
||||
let id = self.add(state);
|
||||
let widgets = &mut state.ui_mut().widgets;
|
||||
widgets.set_size_rule(id, Axis::X, SizeRule::Exact(size.x));
|
||||
widgets.set_size_rule(id, Axis::Y, SizeRule::Exact(size.y));
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
fn max_width(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, MaxSize> {
|
||||
fn width(self, len: impl Into<Len>) -> impl WidgetIdFn<Rsc, WL::Widget> {
|
||||
let len = len.into();
|
||||
move |state| MaxSize {
|
||||
inner: self.add_strong(state),
|
||||
x: Some(len),
|
||||
y: None,
|
||||
move |state| {
|
||||
let id = self.add(state);
|
||||
state
|
||||
.ui_mut()
|
||||
.widgets
|
||||
.set_size_rule(id, Axis::X, SizeRule::Exact(len));
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
fn max_height(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, MaxSize> {
|
||||
fn height(self, len: impl Into<Len>) -> impl WidgetIdFn<Rsc, WL::Widget> {
|
||||
let len = len.into();
|
||||
move |state| MaxSize {
|
||||
inner: self.add_strong(state),
|
||||
x: None,
|
||||
y: Some(len),
|
||||
}
|
||||
}
|
||||
|
||||
fn width(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, SetSize> {
|
||||
let len = len.into();
|
||||
move |state| SetSize {
|
||||
inner: self.add_strong(state),
|
||||
x: Some(len),
|
||||
y: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn height(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, SetSize> {
|
||||
let len = len.into();
|
||||
move |state| SetSize {
|
||||
inner: self.add_strong(state),
|
||||
x: None,
|
||||
y: Some(len),
|
||||
move |state| {
|
||||
let id = self.add(state);
|
||||
state
|
||||
.ui_mut()
|
||||
.widgets
|
||||
.set_size_rule(id, Axis::Y, SizeRule::Exact(len));
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user