From cdec29351a4b831ee1ddddd360e5a3a4490f4b5c Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Mon, 14 Sep 2026 14:35:20 -0400 Subject: [PATCH] Grow scrolling into the random trees Scrolling is the one thing in these trees that reads the pixel length of its box, and the one that hands its child a box longer than its own, so a warm layout under it has to be rebuilt where the rest can be carried over. A sixth of the nodes at each level is now a scroll over a subtree, on either axis. Four of a hundred seeds now grow nothing but wrappers, so `reshuffled` returns early where there is no span to shuffle: a case with nothing to do is not the same as a shuffle that had no effect, which is what the assertion below it is for. 50 tests, and the ignored sweep over 100 seeds and eight scenarios, 800 comparisons. Co-Authored-By: Claude Opus 5 --- src/random.rs | 12 ++++++++++++ tests/generated.rs | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/src/random.rs b/src/random.rs index 385b154..ca5b9c4 100644 --- a/src/random.rs +++ b/src/random.rs @@ -77,6 +77,7 @@ pub struct Tree { pub ids: Vec, pub sized: Vec>, pub spans: Vec, + pub scrolls: Vec>, /// Children a `SpanEdit` took out, held so that dropping the last share /// of one does not free its id for the next widget to be given -- which /// would put the two trees' `ids` out of step. @@ -170,6 +171,17 @@ impl Grow<'_, Rsc> { if depth == 0 { return self.leaf(); } + if self.rng.below(6) == 0 { + // Scrolling reads the pixel length of its box, which nothing + // else here does, and gives its child a box longer than its own. + let inner = self.node(depth - 1); + let inner = self.sized(inner); + let axis = if self.rng.chance() { Axis::X } else { Axis::Y }; + 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); + } if self.rng.below(4) == 0 { let inner = self.node(depth - 1); let inner = self.sized(inner); diff --git a/tests/generated.rs b/tests/generated.rs index 6b665b2..a212064 100644 --- a/tests/generated.rs +++ b/tests/generated.rs @@ -188,6 +188,11 @@ fn changed_size(seed: u64) { fn reshuffled(seed: u64, shuffle: Shuffle) { let mut warm = Harness::new((900, 1200)); let mut grown = plant(&mut warm, seed, &Edits::default()); + // Some seeds grow nothing but wrappers, and a shuffle with no span to + // shuffle is not the same thing as one that had no effect. + if grown.spans.is_empty() { + return; + } let before: Vec<_> = grown.ids.iter().map(|id| warm.region(id)).collect(); let (spans, _held) = reshuffle(&mut warm, &mut grown, shuffle);