diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index 58302b7..4269f5f 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -2,7 +2,7 @@ use crate::layout_diagnostics::{self as diag, Counter}; use crate::{ Axis, Holds, Len, RenderedText, Size, StrongWidget, TextAttrs, TextBuffer, TextData, - TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, Widget, WidgetId, + TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId, Widgets, render::{ GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst, PrimitiveKind, TexturePrimitive, @@ -111,16 +111,12 @@ impl<'a> Painter<'a> { self.widget_at(id, region) } - /// What a widget declares its lengths to be, which whoever draws it - /// resolves into its box. `leftover` is not among them: a part of what is - /// left over is only a length to the widget dividing one, so it passes - /// up in the size instead. Reading it depends on nothing -- the box that - /// comes of it is kept on the child, and `redraw` compares it there. + /// What a widget's rules declare its lengths to be, which whoever draws + /// it resolves into its box. Reading them depends on nothing -- the box + /// that comes of them is kept on the child, and `redraw` compares it + /// there. fn declared_lens(&self, id: &StrongWidget) -> [Option; 2] { - let Some(widget) = self.rsc.widgets().get_dyn(id.id()) else { - return [None; 2]; - }; - AXES.map(|axis| declared_len(widget, axis)) + declared_lens(self.rsc.widgets(), id.id()) } /// Takes back a child that was drawn only to find out how long it is. @@ -201,11 +197,14 @@ impl<'a> Painter<'a> { /// What a child says its length is without being drawn, if it can say. /// Asking counts as reading its size. pub fn size_hint(&mut self, id: &StrongWidget, axis: Axis) -> Option { - let hint = self - .rsc - .widgets() - .get_dyn(id.id()) - .and_then(|widget| widget.size_hint(axis)); + let widgets = self.rsc.widgets(); + // A rule is the answer where there is one: it wins over whatever the + // widget would draw, so it has to win over what the widget says too. + let hint = widgets.size_rules(id.id()).axis(axis).known().or_else(|| { + widgets + .get_dyn(id.id()) + .and_then(|widget| widget.size_hint(axis)) + }); #[cfg(feature = "layout-diagnostics")] diag::hint_read(id.id(), self.id, axis, hint); match hint { @@ -439,8 +438,21 @@ impl PrimitiveLike for &TextureHandle { /// What a widget declares a length of its box to be. `leftover` is not one: a /// share of what is left over is only a length to the widget dividing one, /// so it passes up in the size instead. -pub(crate) fn declared_len(widget: &dyn Widget, axis: Axis) -> Option { - widget.size_hint(axis).filter(|len| len.leftover == 0.0) +pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option; 2] { + let rules = widgets.size_rules(id); + let widget = widgets.get_dyn(id); + AXES.map(|axis| { + rules.axis(axis).declared().or_else(|| { + // A hint still narrows the box where no rule does, which is how a + // widget with a natural pixel size -- an image, a gap -- gets that + // size rather than the whole offer. That is the offer's business + // rather than a declaration's, and this falls away once a widget + // occupies its reported size inside the box it was offered. + widget + .and_then(|widget| widget.size_hint(axis)) + .filter(|len| len.leftover == 0.0) + }) + }) } /// Takes a widget's declared lengths in the box `region` is given in, since a diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 6c67702..709c2e0 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -1,6 +1,6 @@ #[cfg(feature = "layout-diagnostics")] use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind}; -use crate::ui::painter::declared_len; +use crate::ui::painter::declared_lens; use crate::{ ActiveData, Axis, DrawLayers, Holds, IdLike, Len, MaskIdx, MoveIdx, Moves, Painter, PixelRegion, Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, WidgetId, Widgets, @@ -256,6 +256,14 @@ impl UiRenderState { "'{}' ({id:?}) drew a size its size_hint disagrees with", rsc.widgets().label(id) ); + // A rule wins on the axis it names, and the draw answers the rest. + // Applied here so it is one place rather than every widget that could + // carry one, and so the widget under a rule never learns of it. + let rules = rsc.widgets().size_rules(id); + let size = Size { + x: rules.x.apply(size.x), + y: rules.y.apply(size.y), + }; let holds = [own[0].and(under[0]), own[1].and(under[1])]; debug_assert!( holds[0].contains(px.x) && holds[1].contains(px.y), @@ -739,11 +747,7 @@ impl UiRenderState { // whether to draw it at all, so a change to either is the parent's // to draw -- with the mark left on, so the parent draws it rather // than keeping it. - let declared_changed = rsc.widgets().get_dyn(id).is_some_and(|widget| { - AXES.into_iter() - .zip(active.declared) - .any(|(axis, was)| declared_len(widget, axis) != was) - }); + let declared_changed = declared_lens(rsc.widgets(), id) != active.declared; if let Some(parent) = active.parent && (declared_changed || !active.drawn) { diff --git a/core/src/widget/data.rs b/core/src/widget/data.rs index fc0abbd..59d557a 100644 --- a/core/src/widget/data.rs +++ b/core/src/widget/data.rs @@ -1,9 +1,10 @@ -use crate::Widget; +use crate::{SizeRules, Widget}; pub struct WidgetData { pub widget: Box, pub label: String, pub(super) region_node: bool, + pub(super) size: SizeRules, /// dynamic borrow checking pub borrowed: bool, } @@ -18,6 +19,7 @@ impl WidgetData { widget: Box::new(widget), label, region_node: false, + size: SizeRules::default(), borrowed: false, } } diff --git a/core/src/widget/mod.rs b/core/src/widget/mod.rs index fb31bc4..f88f411 100644 --- a/core/src/widget/mod.rs +++ b/core/src/widget/mod.rs @@ -4,6 +4,7 @@ use std::any::Any; mod data; mod handle; mod like; +mod size_rule; mod tag; mod view; mod widgets; @@ -11,6 +12,7 @@ mod widgets; pub use data::*; pub use handle::*; pub use like::*; +pub use size_rule::*; pub use tag::*; pub use view::*; pub use widgets::*; diff --git a/core/src/widget/size_rule.rs b/core/src/widget/size_rule.rs new file mode 100644 index 0000000..5550913 --- /dev/null +++ b/core/src/widget/size_rule.rs @@ -0,0 +1,86 @@ +use crate::{Axis, Len}; + +/// What a widget's length on one axis is, as a rule its parent applies where +/// it draws it rather than an answer the widget gives about itself. +/// +/// A rule and a drawn size are not two opinions to reconcile: a rule wins on +/// the axis it names, and the `Size` returned by `draw` answers only the axes +/// with no rule. That is what lets a span divide its space around a length +/// nobody has drawn yet, and it is why a rule lives beside the widget rather +/// than inside it -- the widget under the rule never has to know about it. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub enum SizeRule { + /// Whatever the widget reports from drawing. + #[default] + Free, + /// This length, whatever the widget reports. + Exact(Len), +} + +impl SizeRule { + /// The length this rule gives without the widget being drawn, if it can + /// give one. `leftover` is never among them: a share is a length only to + /// whoever divides one, so it passes up in the reported size instead and + /// is resolved there. + pub fn declared(&self) -> Option { + match self { + Self::Exact(len) if len.leftover == 0.0 => Some(*len), + _ => None, + } + } + + /// The length this rule fixes, whether or not it can narrow a box. A + /// share is a length the widget's parent still has to divide, so it is + /// known here and resolved there -- unlike `declared`, which is only the + /// ones that give a box directly. + pub fn known(&self) -> Option { + match self { + Self::Free => None, + Self::Exact(len) => Some(*len), + } + } + + /// The length a widget reporting `reported` ends up with. + pub fn apply(&self, reported: Len) -> Len { + match self { + Self::Free => reported, + Self::Exact(len) => *len, + } + } +} + +impl From for SizeRule { + fn from(len: Len) -> Self { + Self::Exact(len) + } +} + +impl From> for SizeRule { + fn from(len: Option) -> Self { + len.map_or(Self::Free, Self::Exact) + } +} + +/// One rule per axis, which is how a widget carries a length on one axis and +/// leaves the other to whatever it draws. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct SizeRules { + pub x: SizeRule, + pub y: SizeRule, +} + +impl SizeRules { + pub fn axis(&self, axis: Axis) -> SizeRule { + match axis { + Axis::X => self.x, + Axis::Y => self.y, + } + } + + pub fn axis_mut(&mut self, axis: Axis) -> &mut SizeRule { + match axis { + Axis::X => &mut self.x, + Axis::Y => &mut self.y, + } + } +} diff --git a/core/src/widget/widgets.rs b/core/src/widget/widgets.rs index 3e5f04e..474f393 100644 --- a/core/src/widget/widgets.rs +++ b/core/src/widget/widgets.rs @@ -1,7 +1,7 @@ use std::sync::mpsc::{Receiver, Sender, channel}; use crate::{ - IdLike, StrongWidget, WeakWidget, Widget, WidgetData, WidgetId, + Axis, IdLike, SizeRule, SizeRules, StrongWidget, WeakWidget, Widget, WidgetData, WidgetId, util::{DynBorrower, HashSet, SlotVec, forget_mut, to_mut}, }; @@ -118,6 +118,36 @@ impl Widgets { self.needs_redraw.insert(id); } + /// The length rules whoever draws this widget applies to its box. + pub fn size_rules(&self, id: impl IdLike) -> SizeRules { + self.data(id).unwrap().size + } + + /// Sets one axis's rule. The widget is marked rather than its parent + /// because the parent is not known here; `redraw` escalates a changed + /// declared length to whoever resolves it. + pub fn set_size_rule(&mut self, id: impl IdLike, axis: Axis, rule: SizeRule) { + let id = id.id(); + let data = self.data_mut(id).unwrap(); + if *data.size.axis_mut(axis) == rule { + return; + } + *data.size.axis_mut(axis) = rule; + self.needs_redraw.insert(id); + } + + /// Both axes at once, for a caller holding a pair. + pub fn set_size_rules( + &mut self, + id: impl IdLike, + x: impl Into, + y: impl Into, + ) { + let id = id.id(); + self.set_size_rule(id, Axis::X, x.into()); + self.set_size_rule(id, Axis::Y, y.into()); + } + pub fn data_mut(&mut self, id: impl IdLike) -> Option<&mut WidgetData> { self.vec.get_mut(id.id()) } diff --git a/src/harness.rs b/src/harness.rs index 63cebec..4b7f569 100644 --- a/src/harness.rs +++ b/src/harness.rs @@ -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) { + 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(&mut self, widget: impl WidgetLike, T>) { widget.set_root(&mut self.rsc, &mut self.state); diff --git a/src/random.rs b/src/random.rs index 47f7321..30e9258 100644 --- a/src/random.rs +++ b/src/random.rs @@ -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; 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, /// Which children a span has, by the order the spans were made. pub spans: HashMap, @@ -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, - pub sized: Vec>, + pub sized: Vec, pub spans: Vec, pub scrolls: Vec>, /// Children a `SpanEdit` took out, held so that dropping the last share @@ -201,15 +201,13 @@ impl 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 { diff --git a/src/widget/position/max_size.rs b/src/widget/position/max_size.rs deleted file mode 100644 index 1ca5e28..0000000 --- a/src/widget/position/max_size.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::prelude::*; - -pub struct MaxSize { - pub inner: StrongWidget, - pub x: Option, - pub y: Option, -} - -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, own: f32) -> Len { - match max { - Some(max) if len.apply_leftover().to_px(own) > max.apply_leftover().to_px(own) => max, - _ => len, - } -} diff --git a/src/widget/position/mod.rs b/src/widget/position/mod.rs index 12d6614..9957d3c 100644 --- a/src/widget/position/mod.rs +++ b/src/widget/position/mod.rs @@ -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::*; diff --git a/src/widget/position/set_size.rs b/src/widget/position/set_size.rs deleted file mode 100644 index dffdb50..0000000 --- a/src/widget/position/set_size.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::prelude::*; - -pub struct SetSize { - pub inner: StrongWidget, - pub x: Option, - pub y: Option, -} - -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 { - match axis { - Axis::X => self.x, - Axis::Y => self.y, - } - } -} diff --git a/src/widget/trait_fns.rs b/src/widget/trait_fns.rs index 4fd3d05..13988e3 100644 --- a/src/widget/trait_fns.rs +++ b/src/widget/trait_fns.rs @@ -39,48 +39,38 @@ widget_trait! { } } - fn sized(self, size: impl Into) -> impl WidgetFn { + fn sized(self, size: impl Into) -> impl WidgetIdFn { 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) -> impl WidgetFn { + fn width(self, len: impl Into) -> impl WidgetIdFn { 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) -> impl WidgetFn { + fn height(self, len: impl Into) -> impl WidgetIdFn { let len = len.into(); - move |state| MaxSize { - inner: self.add_strong(state), - x: None, - y: Some(len), - } - } - - fn width(self, len: impl Into) -> impl WidgetFn { - let len = len.into(); - move |state| SetSize { - inner: self.add_strong(state), - x: Some(len), - y: None, - } - } - - fn height(self, len: impl Into) -> impl WidgetFn { - 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 } } diff --git a/tests/drift.rs b/tests/drift.rs index fe2059b..9c11702 100644 --- a/tests/drift.rs +++ b/tests/drift.rs @@ -10,7 +10,7 @@ use iris::prelude::*; /// A row of a fixed height under a bar, so changing the bar's height moves the /// row without changing the box it is given: the move path, repeatedly. -fn plant(h: &mut Harness, bar_height: f32) -> (WeakWidget, WeakWidget) { +fn plant(h: &mut Harness, bar_height: f32) -> (WeakWidget, WeakWidget) { let bar = rect(Color::RED).height(bar_height).add(&mut h.rsc); let inner = rect(Color::BLUE).add(&mut h.rsc); let row = (inner, rect(Color::GREEN)).span(Dir::RIGHT).height(100); @@ -33,7 +33,7 @@ fn a_subtree_moved_many_times_stays_where_a_cold_layout_puts_it() { let mut height = 40.0; for step in 0..MOVES { height = 40.0 + (step % 300) as f32 * 0.37; - warm.rsc[bar].y = Some(Len::px(height)); + warm.set_len(bar, Axis::Y, height); warm.frame(); } diff --git a/tests/generated.rs b/tests/generated.rs index dc7e08a..0569b8a 100644 --- a/tests/generated.rs +++ b/tests/generated.rs @@ -61,9 +61,9 @@ fn resize_one(h: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> Lens { Some(Len::px(20.0 + rng.below(180) as f32)), Some(Len::px(20.0 + rng.below(180) as f32)), ]; - let sized = &mut h.rsc[tree.sized[idx]]; - sized.x = lens[0]; - sized.y = lens[1]; + h.rsc + .widgets_mut() + .set_size_rules(tree.sized[idx], lens[0], lens[1]); lens } @@ -174,18 +174,25 @@ fn reshuffle( /// written out by hand. A fuzz failure is a lead; the fast test that replaces /// it has to be buildable from what the failure printed. fn describe(id: WidgetId, h: &Harness) -> String { + let rules = h.rsc.widgets().size_rules(id); + let rule = |r: SizeRule| match r.known() { + Some(len) => format!("{len}"), + None => "-".into(), + }; + // A size rule is a property of whatever carries it, so it prints with + // that widget rather than as one of its own. + match (rules.x, rules.y) { + (SizeRule::Free, SizeRule::Free) => describe_widget(id, h), + (x, y) => format!("{}[x:{},y:{}]", describe_widget(id, h), rule(x), rule(y)), + } +} + +fn describe_widget(id: WidgetId, h: &Harness) -> String { let label = h.rsc.widgets().label(id).to_string(); let Some(widget) = h.rsc.widgets().get_dyn(id) else { return label; }; let any: &dyn std::any::Any = widget; - let len = |l: &Option| match l { - Some(l) => format!("{l}"), - None => "-".into(), - }; - if let Some(w) = any.downcast_ref::() { - return format!("SetSize{{x:{},y:{}}}", len(&w.x), len(&w.y)); - } if let Some(w) = any.downcast_ref::() { let sign = if w.dir.sign == Sign::Neg { "-" } else { "+" }; return format!( diff --git a/tests/layout.rs b/tests/layout.rs index 9157106..9e91161 100644 --- a/tests/layout.rs +++ b/tests/layout.rs @@ -81,7 +81,7 @@ fn a_child_drawn_twice_moves_once() { h.set_root((left, centered).span(Dir::RIGHT)); assert_corners!(h, inner, (100, 0), (300, 200)); - h.rsc[left].x = Some(Len::px(150)); + h.set_len(left, Axis::X, 150); h.frame(); assert_corners!(h, inner, (150, 0), (350, 200)); @@ -131,7 +131,7 @@ fn a_fixed_box_is_drawn_again_rather_than_stretched() { h.set_root(stack.align(Align::TOP)); assert_corners!(h, panel, (0, 0), (400, 100)); - h.rsc[leaf].y = Some(Len::px(250)); + h.set_len(leaf, Axis::Y, 250); h.frame(); assert_corners!(h, panel, (0, 0), (400, 250)); @@ -146,7 +146,7 @@ fn a_moved_subtree_takes_its_children_with_it() { h.set_root((first, row).span(Dir::DOWN)); assert_corners!(h, inner, (10, 50), (390, 70)); - h.rsc[first].y = Some(Len::px(80)); + h.set_len(first, Axis::Y, 80); h.frame(); // The row opted into one movable region, so its descendants follow one @@ -167,7 +167,7 @@ fn a_fixed_length_child_keeps_it_when_the_box_around_it_grows() { assert_corners!(h, fixed, (100, 0), (150, 200)); assert_corners!(h, leftover, (150, 0), (400, 200)); - h.rsc[bar].x = Some(Len::px(200)); + h.set_len(bar, Axis::X, 200); h.frame(); // The panel's box is 100 shorter, so the fixed child is the same 50 wide @@ -195,7 +195,7 @@ fn a_box_with_a_fixed_length_can_be_stretched_on_its_other_axis() { h.set_root((bar, column).span(Dir::RIGHT)); assert_corners!(h, inner, (110, 10), (390, 30)); - h.rsc[bar].x = Some(Len::px(200)); + h.set_len(bar, Axis::X, 200); h.frame(); assert_corners!(h, inner, (210, 10), (390, 30)); @@ -282,24 +282,14 @@ fn drawn_edges(h: &Harness, id: WidgetId, axis: Axis) -> (f32, f32) { } fn hairline(h: &mut Harness, marks: &mut Vec) -> StrongWidget { - let inner = rect(Color::RED).add_strong(&mut h.rsc); - let mark = SetSize { - inner, - x: Some(Len::px(1.0)), - y: None, - } - .add_strong(&mut h.rsc); + let mark = rect(Color::RED).width(1).add_strong(&mut h.rsc); marks.push(mark.id()); mark } fn share(h: &mut Harness, inner: StrongWidget, ratio: f32) -> StrongWidget { - SetSize { - inner, - x: Some(Len::leftover(ratio)), - y: None, - } - .add_strong(&mut h.rsc) + h.set_len(&inner, Axis::X, Len::leftover(ratio)); + inner } /// Shares in weights no binary fraction lands on, a padding on one branch @@ -402,18 +392,15 @@ fn only_a_pure_leftover_child_disappears_when_nothing_is_left() { // An undrawn child remains a dependency of the span, so making room for // it draws it without rebuilding the tree. - h.rsc[fixed].x = Some(Len::px(60)); + h.set_len(fixed, Axis::X, 60); h.frame(); assert_corners!(h, leftover, (60, 0), (100, 20)); let mut h = Harness::new((100, 20)); let fixed = rect(Color::RED).width(100).add(&mut h.rsc); - let mixed = SetSize { - inner: rect(Color::BLUE).add_strong(&mut h.rsc), - x: Some(Len::px(20) + Len::LEFTOVER), - y: None, - } - .add(&mut h.rsc); + let mixed = rect(Color::BLUE) + .width(Len::px(20) + Len::LEFTOVER) + .add(&mut h.rsc); h.set_root((fixed, mixed).span(Dir::RIGHT)); // Pixels and fractions still overflow; only a child whose entire length @@ -432,7 +419,7 @@ fn leftover_children_disappear_at_the_exact_fixed_content_boundary() { assert!(h.region(&a).is_some()); assert!(h.region(&b).is_some()); - h.rsc[first].y = Some(Len::px(96.0)); + h.set_len(first, Axis::Y, 96.0); h.frame(); assert!(h.region(&a).is_none()); diff --git a/tests/layout_diagnostics.rs b/tests/layout_diagnostics.rs index 2eb4eca..5dae327 100644 --- a/tests/layout_diagnostics.rs +++ b/tests/layout_diagnostics.rs @@ -216,7 +216,11 @@ fn layout_cost() { trace_selected(&tree); let sized = tree.sized[0]; run("size", frames, &mut harness, move |harness, frame| { - harness.rsc[sized].x = Some(Len::px(100.0 + (frame % 2) as f32 * 40.0)); + let len = Len::px(100.0 + (frame % 2) as f32 * 40.0); + harness + .rsc + .widgets_mut() + .set_size_rule(sized, Axis::X, SizeRule::Exact(len)); }); } diff --git a/tests/replace_cost.rs b/tests/replace_cost.rs index c422196..82310cf 100644 --- a/tests/replace_cost.rs +++ b/tests/replace_cost.rs @@ -35,7 +35,7 @@ fn remapping_rows_every_frame() { } h.set_root(span); for i in 0..FRAMES { - h.rsc[first].y = Some(Len::px(40.0 + (i % 2) as f32)); + h.set_len(first, Axis::Y, 40.0 + (i % 2) as f32); h.frame(); } } diff --git a/tests/retained.rs b/tests/retained.rs index 611ef86..fe69e12 100644 --- a/tests/retained.rs +++ b/tests/retained.rs @@ -236,7 +236,7 @@ fn a_parent_that_only_read_a_hint_relays_out_when_the_hint_changes() { h.set_root(parent); assert_corners!(h, inner, (0, 0), (400, 80)); - h.rsc[inner].y = Some(Len::px(120)); + h.set_len(inner, Axis::Y, 120); h.frame(); assert_corners!(h, inner, (0, 0), (400, 120)); @@ -484,7 +484,7 @@ fn stretching_a_subtree_carries_the_children_in_it() { let settled = draws.get(); assert_corners!(h, inner, (0, 40), (400, 400)); - h.rsc[first].y = Some(Len::px(80)); + h.set_len(first, Axis::Y, 80); h.frame(); assert_eq!( @@ -508,7 +508,7 @@ fn a_widened_row_redraws_what_reads_its_length_and_nothing_else() { h.set_root((bar, row).span(Dir::RIGHT)); let (settled_wrap, settled_back) = (wrap_draws.get(), back_draws.get()); - h.rsc[bar].x = Some(Len::px(200)); + h.set_len(bar, Axis::X, 200); h.frame(); // The span reads every child's size, so redrawing one takes the span @@ -534,7 +534,7 @@ fn a_declared_length_child_is_not_redrawn_when_the_box_around_it_grows() { h.set_root((bar, row).span(Dir::RIGHT)); let settled = draws.get(); - h.rsc[bar].x = Some(Len::px(200)); + h.set_len(bar, Axis::X, 200); h.frame(); assert_eq!(draws.get(), settled, "its own length did not change"); diff --git a/tests/shrink.rs b/tests/shrink.rs index 213d9f2..2a37f50 100644 --- a/tests/shrink.rs +++ b/tests/shrink.rs @@ -159,12 +159,8 @@ impl Node { } Node::Sized(x, y, kid) => { let inner = kid.build(h, out, spans); - SetSize { - inner, - x: *x, - y: *y, - } - .add_strong(&mut h.rsc) + h.rsc.widgets_mut().set_size_rules(&inner, *x, *y); + inner } Node::Scroll(down, kid) => { let inner = kid.build(h, out, spans); diff --git a/tests/trace_unsettled.rs b/tests/trace_unsettled.rs index 6150514..b9e81ac 100644 --- a/tests/trace_unsettled.rs +++ b/tests/trace_unsettled.rs @@ -10,12 +10,7 @@ use iris::prelude::*; fn plant(h: &mut Harness) -> Vec { 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 = SetSize { - inner: wrapped.add_strong(&mut h.rsc), - x: Some(Len::px(76.0)), - y: None, - } - .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 { @@ -108,12 +103,7 @@ fn plant_fixed(h: &mut Harness) -> Vec { } .add(&mut h.rsc); let inner = (aligned,).span(Dir::RIGHT).add(&mut h.rsc); - let sized = SetSize { - inner: inner.add_strong(&mut h.rsc), - x: Some(Len::px(189.0)), - y: Some(Len::px(176.0)), - } - .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)); diff --git a/tests/unsettled.rs b/tests/unsettled.rs index ee1deab..3a5f735 100644 --- a/tests/unsettled.rs +++ b/tests/unsettled.rs @@ -15,12 +15,7 @@ use iris::prelude::*; fn plant(h: &mut Harness) -> Vec { 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 = SetSize { - inner: wrapped.add_strong(&mut h.rsc), - x: Some(Len::px(76.0)), - y: None, - } - .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 { @@ -110,12 +105,7 @@ fn plant_fixed(h: &mut Harness) -> Vec { } .add(&mut h.rsc); let inner = (aligned,).span(Dir::RIGHT).add(&mut h.rsc); - let sized = SetSize { - inner: inner.add_strong(&mut h.rsc), - x: Some(Len::px(189.0)), - y: Some(Len::px(176.0)), - } - .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)); @@ -235,12 +225,7 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec, [WeakWidget } .add(&mut h.rsc); let block = rect(Color::RED).add(&mut h.rsc); - let fixed = SetSize { - inner: block.add_strong(&mut h.rsc), - x: Some(Len::px(87.0)), - y: None, - } - .add(&mut h.rsc); + let fixed = block.width(87).add(&mut h.rsc); let mut outer_children: Vec = vec![fixed.add_strong(&mut h.rsc), inner.add_strong(&mut h.rsc)]; if swapped { @@ -253,12 +238,9 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec, [WeakWidget ortho: OrthoSize::Children, } .add(&mut h.rsc); - let through = SetSize { - inner: outer.add_strong(&mut h.rsc), - x: None, - y: None, - } - .add(&mut h.rsc); + // Carried no rule even before rules were a property: it is here to be a + // widget between the span and the scroll, not to declare anything. + let through = (outer,).span(Dir::RIGHT).add(&mut h.rsc); let scroll = Scroll::new(through.add_strong(&mut h.rsc), Axis::X).add(&mut h.rsc); h.state.root = Some(scroll.add_strong(&mut h.rsc)); (