//! A seeded random widget tree, for tests and for looking at. //! //! One seed is one tree, on any machine and after any upgrade, so a test can //! grow the same tree twice and a failing seed is reproduced by its number. //! `examples/random.rs` draws one; `tests/generated.rs` checks that laying one //! out again lands where growing it from scratch would. use crate::prelude::*; use std::collections::HashMap; /// The declared lengths of one widget carrying a size rule, by axis. pub type Lens = [Option; 2]; /// Where one widget carrying an alignment sits, by axis. `None` uses the /// centered default. pub type Aligns = [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 rules were put on. pub sizes: HashMap, /// Which children a span has, by the order the spans were made. pub spans: HashMap, /// Alignments, by the order they were put on. pub aligns: HashMap, /// Which widgets own a movable region, by the order they were offered /// one. Region nodes change what a move writes and how deep a primitive's /// chain is, so a tree that never grows one leaves both untested. pub nodes: HashMap, /// Whether a [`Branch`] takes the side it would take at any measurement, /// rather than the side the one it made says. The oracle wants the /// measured side -- that is the whole point of a branch, and how a widget /// believing a measurement a cold start would not have given it becomes a /// different tree. A rig measuring cost wants this instead: a fixture /// whose shape moves with the thing being measured cannot be compared /// with itself across a change to it, and seed 1 at depth 8 went from 88 /// drawn widgets and 2,298 primitive writes a frame to 115 and 8,209 /// across fixed point, which is three and a half times the work behind a /// number read as three and a half times the cost. pub fixed_branches: bool, } #[derive(Default, Clone)] pub struct SpanEdit { /// Children to leave out, by index among the ones grown. pub detach: Vec, /// How many of the span's spares are in it, appended in order. pub attach: usize, } /// xorshift64, written out rather than taken from a crate so that a seed /// keeps meaning the same tree. pub struct Rng(u64); impl Rng { pub fn new(seed: u64) -> Self { Self(seed | 1) } pub fn bits(&mut self) -> u64 { self.0 ^= self.0 << 13; self.0 ^= self.0 >> 7; self.0 ^= self.0 << 17; self.0 } pub fn below(&mut self, n: usize) -> usize { (self.bits() % n as u64) as usize } pub fn chance(&mut self) -> bool { self.bits() & 1 == 0 } } const COLORS: [UiColor; 6] = [ UiColor::RED, UiColor::GREEN, UiColor::BLUE, UiColor::YELLOW, UiColor::CYAN, UiColor::MAGENTA, ]; /// Leaves grown beside every span, for a test to put into it. const SPARES: usize = 3; const WORDS: &str = "Wrapping shapes one source into as many lines as the box \ leaves room for, so a paragraph's height is an answer and not a setting."; /// What growing a tree gives back: every widget in creation order, so two /// trees from one seed line up index for index, and the declared sizes, which /// are what a test changes to watch the change propagate. #[derive(Default)] pub struct Tree { pub ids: Vec, pub sized: Vec, pub aligned: Vec, pub nodes: Vec, pub spans: Vec, pub scrolls: Vec>, } /// Branches on a child's measured length. Comparing boxes catches a widget /// that moved; this catches one that believed a measurement a cold start /// would not have given it, by turning that into a different tree. Its own /// configuration never changes, so which side draws is a property of the /// layout alone. pub struct Branch { pub probe: StrongWidget, pub wide: StrongWidget, pub narrow: StrongWidget, pub threshold: f32, } impl Widget for Branch { fn draw(&mut self, painter: &mut Painter) -> Size { let cut = Len::from_parts(Rel::ZERO, Px::from_int(40)); let top = UiSpan::new(Len::ZERO, cut).shifted_desc(); let measured = painter .widget_at(&self.probe, top.on_axis(Axis::Y)) .len(Axis::X); let len = measured.apply_leftover(); let px = painter.to_px(len, Axis::X); // The range it actually branched on, said the way a container says // one: pinning the window instead would redraw this widget on every // resize, which is a fixture that never exercises reuse. let threshold = Px::from_f32(self.threshold); let holds = match px > threshold { true => Holds::from(threshold + Px::STEP..=Px::MAX), false => Holds::from(Px::MIN..=threshold), }; painter.window_holds(Axis::X, holds.through(len)); let below = UiSpan::new(cut, painter.region_len(Axis::Y)).shifted_desc(); let place = below.on_axis(Axis::Y); match px > threshold { true => painter.widget_at(&self.wide, place), false => painter.widget_at(&self.narrow, place), }; Size::LEFTOVER } fn size_hint(&self, _: Axis) -> Option { Some(LayoutLen::LEFTOVER) } } pub struct Spanned { pub id: WeakWidget, /// Everything made for this span that it does not hold -- spares never /// attached and children detached alike. A widget belongs to one parent, /// and one that belongs to nobody still has to be held here: dropping /// the last share of it frees its id for the next widget to be given, /// which puts two trees out of step. pub spares: Vec, /// How many children it was grown with, before any edit. pub grown: usize, } /// A tree described rather than built: [`plan`] turns a seed into one of /// these and [`build`] turns it into widgets, where growing did both at once. /// /// The split is what makes a counterexample readable. A failing seed used to /// be the entire record of one, because a grower that makes widgets as it /// draws leaves nothing to take apart -- a shrinker could only grow its own /// trees and hope to meet the same shape, which in practice it does not. A /// plan is reduced by [`Plan::smaller`] and built again, so any seed that /// fails can be cut down until what is left is small enough to read. #[derive(Clone, Debug, PartialEq)] pub struct Plan { pub kind: Kind, /// The declared size this widget carries. Whoever grows a widget offers /// it one and the offer is taken or declined; a second offer to the same /// widget is dropped, because two rules on one widget would settle in the /// order they were applied rather than in grow order. pub size: Option, /// The alignment it carries, under the same one-offer rule. pub align: Option, /// Whether it was offered a movable region of its own and what it /// answered. `Some(false)` is an offer declined, which still uses up the /// one offer, where `None` is an offer never made. pub region_node: Option, } #[derive(Clone, Debug, PartialEq)] pub enum Kind { /// Wrapped and unwrapped text, because only one of them reads the width /// it is given and so only one has to be drawn again for a new one. Wrapped, OneLine, Rect { color: usize, alpha: u8, }, /// Scrolling reads the pixel length of its box, which nothing else here /// does, and gives its child a box longer than its own. Scroll { axis: Axis, inner: Box, }, /// All three sides are grown either way, so a tree that draws one has the /// same ids as a tree that draws another. Branch { probe: Box, wide: Box, narrow: Box, threshold: f32, }, /// Each side its own, since a padding that is the same all round hides /// anything that treats one edge differently from another. Pad { padding: [i32; 4], inner: Box, }, Stack { children: Vec, }, Span { dir: usize, gap: i32, /// Grown for this span, in the order they are made. children: Vec, /// Grown beside it whether or not they end up in it, so the widget /// after them has the same id in a tree that leaves them out as in /// one that puts them in. spares: Vec, /// Which of `children` then `spares` are actually in the span, and /// in what order -- kept apart from the two lists above so that a /// tree which detaches, attaches or reorders its children still /// makes the same widgets in the same order, and two builds line up /// index for index. Anything not named here is built and held /// rather than dropped, since freeing an id hands it to the next /// widget and puts two trees out of step. order: Vec, }, } impl Plan { /// A widget carrying nothing anybody has offered it yet. fn bare(kind: Kind) -> Self { Self { kind, size: None, align: None, region_node: None, } } /// How many widgets building it makes, spares and detached children /// included, since those are made either way. pub fn size(&self) -> usize { 1 + match &self.kind { Kind::Scroll { inner, .. } | Kind::Pad { inner, .. } => inner.size(), Kind::Branch { probe, wide, narrow, .. } => probe.size() + wide.size() + narrow.size(), Kind::Stack { children } => children.iter().map(Plan::size).sum(), Kind::Span { children, spares, .. } => children.iter().chain(spares).map(Plan::size).sum(), _ => 0, } } /// The trees to try instead of this one when reducing a counterexample, /// biggest cut first: a shrinker takes the first that still fails, so /// offering "this subtree alone" before "this subtree with one child /// fewer" is what gets from six hundred widgets to six rather than to /// five hundred and ninety. /// /// Every one of these is a tree the generator could have grown, so a /// reduced plan is a counterexample in its own right rather than a /// special case only the shrinker can make. pub fn smaller(&self) -> Vec { let mut out = Vec::new(); // Standing in for the whole of it, which is the largest cut there is. for kid in self.kids() { out.push(kid.clone()); } // Then what it carries, which costs nothing to put back if it was // not the thing that mattered. for dropped in [ self.region_node.map(|_| Plan { region_node: None, ..self.clone() }), self.align.map(|_| Plan { align: None, ..self.clone() }), self.size.map(|_| Plan { size: None, ..self.clone() }), ] .into_iter() .flatten() { out.push(dropped); } out.extend(self.kind.smaller().into_iter().map(|kind| Plan { kind, ..self.clone() })); out } /// Visits every widget in the order [`build`] makes them, so a count /// kept by the visitor indexes the same widget as the matching [`Tree`] /// vector does. pub fn walk_mut(&mut self, at: &mut impl FnMut(&mut Plan)) { match &mut self.kind { Kind::Scroll { inner, .. } | Kind::Pad { inner, .. } => inner.walk_mut(at), Kind::Branch { probe, wide, narrow, .. } => { probe.walk_mut(at); wide.walk_mut(at); narrow.walk_mut(at); } Kind::Stack { children } => { for child in children { child.walk_mut(at); } } Kind::Span { children, spares, .. } => { for child in children.iter_mut().chain(spares) { child.walk_mut(at); } } _ => {} } at(self); } /// The same tree with `edits` applied, by the indices the generator would /// have used for them. /// /// [`plan`] resolves edits while drawing, which needs a seed. A scenario /// needs them applied to a tree that already exists -- one it has built, /// and one a shrinker may already have cut down, where no seed grows it /// any more. Both routes take the same [`Edits`], so a case written /// against one reads the same against the other. pub fn edited(&self, edits: &Edits) -> Plan { let mut out = self.clone(); let (mut sized, mut aligned, mut nodes, mut spans) = (0, 0, 0, 0); out.walk_mut(&mut |plan| { if let Kind::Span { children, spares, order, .. } = &mut plan.kind { if let Some(edit) = edits.spans.get(&spans) { *order = span_edited(order, children.len(), spares.len(), edit); } spans += 1; } if let Kind::Branch { threshold, .. } = &mut plan.kind && edits.fixed_branches { *threshold = f32::MIN; } if plan.size.is_some() { if let Some(lens) = edits.sizes.get(&sized) { plan.size = Some(*lens); } sized += 1; } if plan.align.is_some() { if let Some(align) = edits.aligns.get(&aligned) { plan.align = Some(*align); } aligned += 1; } if plan.region_node.is_some() { if let Some(take) = edits.nodes.get(&nodes) { plan.region_node = Some(*take); } nodes += 1; } }); out } fn kids(&self) -> Vec<&Plan> { match &self.kind { Kind::Scroll { inner, .. } | Kind::Pad { inner, .. } => vec![inner], Kind::Branch { probe, wide, narrow, .. } => vec![probe, wide, narrow], Kind::Stack { children } => children.iter().collect(), Kind::Span { children, .. } => children.iter().collect(), _ => Vec::new(), } } } impl Kind { /// Simplifications of the shape alone, leaving what the widget carries to /// [`Plan::smaller`]. Replacing a node with one of its children is there /// rather than here, since it answers with a whole `Plan`. fn smaller(&self) -> Vec { let mut out = Vec::new(); /// One child reduced at a time, rebuilt into the same shape. Every /// answer has the same number of children as it was given, so it is /// for the shapes whose child count is part of what they are. fn reduced(kids: &[Plan], rebuild: &dyn Fn(Vec) -> Kind) -> Vec { let mut out = Vec::new(); for (i, kid) in kids.iter().enumerate() { for small in kid.smaller() { let mut next = kids.to_vec(); next[i] = small; out.push(rebuild(next)); } } out } /// One child dropped, then [`reduced`]. For the shapes that hold any /// number of children, where dropping one is the cut that matters. fn each(kids: &[Plan], rebuild: &dyn Fn(Vec) -> Kind) -> Vec { let mut out = Vec::new(); for i in 0..kids.len() { if kids.len() > 1 { let mut less = kids.to_vec(); less.remove(i); out.push(rebuild(less)); } } out.extend(reduced(kids, rebuild)); out } match self { // The one leaf that reads the width it is given, then the one // that does not, then the one that measures nothing at all. Kind::Wrapped => out.push(Kind::OneLine), Kind::OneLine => out.push(Kind::Rect { color: 0, alpha: 255, }), Kind::Rect { .. } => {} Kind::Scroll { axis, inner } => { let axis = *axis; out.extend(each(std::slice::from_ref(inner), &|mut k| Kind::Scroll { axis, inner: Box::new(k.remove(0)), })); } Kind::Branch { probe, wide, narrow, threshold, } => { let threshold = *threshold; // All three sides stay: a branch is the widget that draws // one of two on a measurement, and one with a side missing // is a different widget rather than a smaller one. Dropping // the branch for a side is offered by `Plan::smaller`. let sides = [(**probe).clone(), (**wide).clone(), (**narrow).clone()]; out.extend(reduced(&sides, &|k| Kind::Branch { probe: Box::new(k[0].clone()), wide: Box::new(k[1].clone()), narrow: Box::new(k[2].clone()), threshold, })); } Kind::Pad { padding, inner } => { let padding = *padding; if padding != [0; 4] { out.push(Kind::Pad { padding: [0; 4], inner: inner.clone(), }); } out.extend(each(std::slice::from_ref(inner), &|mut k| Kind::Pad { padding, inner: Box::new(k.remove(0)), })); } Kind::Stack { children } => { out.extend(each(children, &|children| Kind::Stack { children })) } Kind::Span { dir, gap, children, spares, order, } => { let (dir, gap, n) = (*dir, *gap, children.len()); let span = |children: Vec, spares: Vec, order: Vec| Kind::Span { dir, gap, children, spares, order, }; let identity: Vec = (0..n).collect(); // An order the generator did not choose is part of the tree, // so take that off before taking the tree apart. if *order != identity { out.push(span(children.clone(), spares.clone(), identity)); } // Spares exist to be attached; with none attached they are // widgets the span never holds. if !spares.is_empty() && order.iter().all(|&i| i < n) { out.push(span(children.clone(), Vec::new(), order.clone())); } if gap != 0 { out.push(Kind::Span { dir, gap: 0, children: children.clone(), spares: spares.clone(), order: order.clone(), }); } for k in 0..n { if n > 1 { let mut less = children.clone(); less.remove(k); // Everything after it shifts down, spares included, // since they are indexed past the children. let order = order .iter() .filter(|&&i| i != k) .map(|&i| if i > k { i - 1 } else { i }) .collect(); out.push(span(less, spares.clone(), order)); } } for (i, kid) in children.iter().enumerate() { for small in kid.smaller() { let mut next = children.clone(); next[i] = small; out.push(span(next, spares.clone(), order.clone())); } } } } out } } /// A [`SpanEdit`] applied to the order a span already holds its children in. /// /// `detach` names positions in that order and `attach` takes from the front /// of what the span is not holding, both of which is what a test changing a /// live span does -- so an edit means the same thing said to a tree and said /// to the plan it was built from. On a span nobody has edited the order is /// the children in the order they were grown, and this is then "leave these /// out and put that many spares on the end". fn span_edited(order: &[usize], children: usize, spares: usize, edit: &SpanEdit) -> Vec { let mut detach = edit.detach.clone(); detach.sort_unstable(); detach.dedup(); let mut next: Vec = order .iter() .enumerate() .filter(|(at, _)| !detach.contains(at)) .map(|(_, &which)| which) .collect(); // What the span is not holding, in the order it hands them back: what it // was already not holding first, in the order the widgets were made, and // what this edit takes out after that, highest position first. A child // just detached goes to the back rather than straight back in, which is // what makes detaching one and attaching one a trade. let mut free: Vec = (0..children + spares) .filter(|i| !order.contains(i)) .collect(); free.extend(detach.iter().rev().filter_map(|&at| order.get(at).copied())); next.extend(free.into_iter().take(edit.attach)); next } /// Plans the tree `seed` describes, `edits` replacing what it would otherwise /// have given the widgets that carry them. /// /// The edits are resolved here rather than at build time, so that a plan is /// the whole of what a tree is and building one has nothing left to decide. pub fn plan(seed: u64, depth: usize, edits: &Edits) -> Plan { let mut sow = Sow { rng: Rng::new(seed), edits, sized: 0, aligned: 0, nodes: 0, spans: 0, }; sow.node(depth) } /// Grows the tree `seed` describes, `edits` replacing the declared sizes it /// would otherwise have given those wrappers. pub fn grow( rsc: &mut Rsc, seed: u64, depth: usize, edits: &Edits, ) -> (StrongWidget, Tree) { build(rsc, &plan(seed, depth, edits)) } /// Draws a plan out of the random stream. Every draw happens in the order it /// always has and before the decision it feeds, including the decisions that /// are then dropped, because a seed has to keep meaning the same tree. struct Sow<'a> { rng: Rng, edits: &'a Edits, sized: usize, aligned: usize, nodes: usize, spans: usize, } impl Sow<'_> { fn leaf(&mut self) -> Plan { Plan::bare(match self.rng.below(4) { 0 => Kind::Wrapped, 1 => Kind::OneLine, _ => { let color = self.rng.below(COLORS.len()); let alpha = (self.rng.below(5) * 63) as u8; Kind::Rect { color, alpha } } }) } fn len(&mut self) -> Option { match self.rng.below(4) { 0 => Some(LayoutLen::px(20.0 + self.rng.below(180) as f32)), 1 => Some(LayoutLen::LEFTOVER), _ => None, } } fn align(&mut self) -> Aligns { let axis = |s: &mut Self| match s.rng.below(4) { 0 => None, 1 => Some(AxisAlign::NEG), 2 => Some(AxisAlign::CENTER), _ => Some(AxisAlign::POS), }; let (x, y) = (axis(self), axis(self)); // Aligning on neither axis leaves the branch unexercised. match x.is_none() && y.is_none() { true => [Some(AxisAlign::CENTER), y], false => [x, y], } } /// A declared size over half the tree, kept where a test can change it. fn sized(&mut self, inner: &mut Plan) { let take = self.rng.chance(); let lens = [self.len(), self.len()]; if !take || inner.size.is_some() { return; } let idx = self.sized; self.sized += 1; inner.size = Some(self.edits.sizes.get(&idx).copied().unwrap_or(lens)); } /// An alignment over some of the tree, kept where a test can change it. fn aligned(&mut self, inner: &mut Plan) { let align = self.align(); if inner.align.is_some() { return; } let idx = self.aligned; self.aligned += 1; inner.align = Some(self.edits.aligns.get(&idx).copied().unwrap_or(align)); } /// A movable region of its own over some of the tree. What it changes is /// how a move is written and how long a primitive's chain is, neither of /// which any other branch here varies. fn noded(&mut self, inner: &mut Plan) { let take = self.rng.below(4) == 0; if inner.region_node.is_some() { return; } let idx = self.nodes; self.nodes += 1; inner.region_node = Some(self.edits.nodes.get(&idx).copied().unwrap_or(take)); } fn offered(&mut self, inner: &mut Plan) { self.sized(inner); self.noded(inner); } fn node(&mut self, depth: usize) -> Plan { if depth == 0 { return self.leaf(); } let positioned = self.rng.below(6); if positioned == 0 { let mut inner = self.node(depth - 1); self.offered(&mut inner); let axis = if self.rng.chance() { Axis::X } else { Axis::Y }; return Plan::bare(Kind::Scroll { axis, inner: Box::new(inner), }); } if positioned == 2 { let probe = self.node(depth - 1); let wide = self.node(depth - 1); let narrow = self.node(depth - 1); // Drawn either way, so the side a fixed branch takes is still a // side the generator chose -- and it consumes the same randomness // as a measured one, so the two grow the same ids. let measured = self.rng.below(500) as f32; let threshold = match self.edits.fixed_branches { true => f32::MIN, false => measured, }; return Plan::bare(Kind::Branch { probe: Box::new(probe), wide: Box::new(wide), narrow: Box::new(narrow), threshold, }); } if positioned == 1 { // Carries an alignment and makes no widget of its own, so the // plan for it is the child it aligned. let mut inner = self.node(depth - 1); self.offered(&mut inner); self.aligned(&mut inner); return inner; } if self.rng.below(4) == 0 { let mut inner = self.node(depth - 1); self.offered(&mut inner); let side = |s: &mut Self| s.rng.below(24) as i32; let padding = [side(self), side(self), side(self), side(self)]; return Plan::bare(Kind::Pad { padding, inner: Box::new(inner), }); } let grown = 2 + self.rng.below(3); let mut children = Vec::with_capacity(grown); for _ in 0..grown { let mut child = self.node(depth - 1); self.offered(&mut child); children.push(child); } if self.rng.chance() { return Plan::bare(Kind::Stack { children }); } let spares: Vec = (0..SPARES).map(|_| self.leaf()).collect(); let idx = self.spans; self.spans += 1; let edit = self.edits.spans.get(&idx).cloned().unwrap_or_default(); let dir = self.rng.below(4); // A row takes the height it is given rather than its tallest child, // which is a rule beside it. Derived from an existing choice and // consuming no randomness: a seed must keep growing the same tree // when the generator gains another configuration. let gap = self.rng.below(3) as i32 * 4; let grown: Vec = (0..children.len()).collect(); let order = span_edited(&grown, children.len(), spares.len(), &edit); Plan::bare(Kind::Span { dir, gap, children, spares, order, }) } } /// Builds a plan's widgets in the order it describes them, so two builds of /// one plan line up index for index and their boxes can be compared. pub fn build(rsc: &mut Rsc, plan: &Plan) -> (StrongWidget, Tree) { let mut build = Build { rsc, tree: Tree::default(), }; let root = build.node(plan); (root, build.tree) } struct Build<'a, Rsc> { rsc: &'a mut Rsc, tree: Tree, } impl Build<'_, Rsc> { fn node(&mut self, plan: &Plan) -> StrongWidget { let built = self.kind(&plan.kind); let id = built.id(); if let Some(lens) = plan.size { self.rsc .ui_mut() .widgets .set_size_rules(id, lens[0], lens[1]); self.tree.sized.push(id); } if let Some(align) = plan.align { let widgets = &mut self.rsc.ui_mut().widgets; for (axis, align) in [Axis::X, Axis::Y].into_iter().zip(align) { widgets.set_alignment(id, axis, align.unwrap_or_default()); } self.tree.aligned.push(id); } if let Some(take) = plan.region_node { self.rsc.ui_mut().widgets.set_region_node(id, take); self.tree.nodes.push(id); } built } fn kind(&mut self, kind: &Kind) -> StrongWidget { let id: StrongWidget = match kind { Kind::Wrapped => wtext(WORDS).size(16).wrap(true).add_strong(self.rsc), Kind::OneLine => wtext("one line, overflowing whatever it is given") .size(16) .wrap(false) .add_strong(self.rsc), Kind::Rect { color, alpha } => rect(COLORS[*color].alpha(*alpha)).add_strong(self.rsc), Kind::Scroll { axis, inner } => { let inner = self.node(inner); let id = Scroll::new(inner, *axis).add(self.rsc); self.tree.scrolls.push(id); self.tree.ids.push(id.id()); return id.add_strong(self.rsc); } Kind::Branch { probe, wide, narrow, threshold, } => { let probe = self.node(probe); let wide = self.node(wide); let narrow = self.node(narrow); let id = Branch { probe, wide, narrow, threshold: *threshold, } .add(self.rsc); self.tree.ids.push(id.id()); return id.add_strong(self.rsc); } Kind::Pad { padding, inner } => { let inner = self.node(inner); let [left, right, top, bottom] = padding.map(Px::from_int); let padding = Padding { left, right, top, bottom, }; Pad { padding, inner }.add_strong(self.rsc) } Kind::Stack { children } => { let children = children.iter().map(|c| self.node(c)).collect(); Stack { children, size: StackSize::Child(0), } .add_strong(self.rsc) } Kind::Span { dir, gap, children, spares, order, } => { let grown = children.len(); // Every one of them is made, in this order, whether or not // the span ends up holding it. let made: Vec = children .iter() .chain(spares) .map(|c| self.node(c)) .collect(); let mut left: Vec> = made.into_iter().map(Some).collect(); let children: Vec = order .iter() .filter_map(|&i| left.get_mut(i).and_then(Option::take)) .collect(); // What the span does not hold is still held here: dropping // the last share of a widget frees its id for the next one // to be given, which puts two trees out of step. let spares: Vec = left.into_iter().flatten().collect(); let dir = [Dir::RIGHT, Dir::DOWN, Dir::LEFT, Dir::UP][*dir % 4]; let id = Span { children, dir, gap: Px::from_int(*gap), } .add(self.rsc); if dir.axis == Axis::X { self.rsc .widgets_mut() .set_size_rules(id, None, Some(LayoutLen::rel(1.0))); } self.tree.ids.push(id.id()); self.tree.spans.push(Spanned { id, spares, grown }); return id.add_strong(self.rsc); } }; self.tree.ids.push(id.id()); id } }