From 6c84b6f2cbfc9c4d47b8a06c557dde6a80df2939 Mon Sep 17 00:00:00 2001 From: iris-ai <4+iris-ai@noreply.localhost> Date: Sat, 19 Sep 2026 22:13:52 -0400 Subject: [PATCH] Read a slot's ends where they are used, not carry one between children `start` looked like a third accumulator beside `fixed` and `taken`, carried across iterations and assigned at three points. It was never independent: every assignment was `shared(fixed, taken)`, so it was those two read together. Reading it at each end of a slot instead drops the variable, drops two of the three calls per child, and leaves the gap added after the last child deriving nothing -- which was the thing that read as a bug, and is not one because no end is taken from it. Co-Authored-By: Claude Opus 5 --- src/widget/position/span.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/widget/position/span.rs b/src/widget/position/span.rs index 7fa2f7c..06fed6b 100644 --- a/src/widget/position/span.rs +++ b/src/widget/position/span.rs @@ -83,15 +83,14 @@ impl Widget for Span { let shrinks = !painter.has_exact_size(!axis); // What the fixed parts and the gaps before here take, which is a sum // of lengths and exact, and how much of the leftover weight is - // spoken for. A position is one from the other rather than a step - // from the last child: the share of the room is rounded, and taking - // each from the one before it would carry every rounding along the - // row. + // spoken for. Both ends of a slot are read from those two rather + // than stepped from the last child: the share of the room is + // rounded, and taking each end from the one before it would carry + // every rounding along the row. let mut fixed = Len::rel_min(); let mut taken = Weight::ZERO; - let mut start = Len::rel_min(); let mut ortho = LayoutLen::ZERO; - let shared = |fixed: Len, taken: Weight| match taken == Weight::ZERO { + let reached = |fixed: Len, taken: Weight| match taken == Weight::ZERO { true => fixed, false => fixed + room.scale(Rel::ratio(taken, total.leftover)), }; @@ -102,15 +101,14 @@ impl Widget for Span { if len.is_only_leftover() && !shares { painter.undraw(child); fixed.px += self.gap; - start = shared(fixed, taken); continue; } - let from = start; + let from = reached(fixed, taken); if len.leftover > Weight::ZERO && shares { taken += len.leftover; } fixed += len.without_leftover(); - start = shared(fixed, taken); + let to = reached(fixed, taken); // Along the row the span says where the child goes, and that slot // is the child's box outright rather than something to place an // answer inside again. A share is decided here and nowhere @@ -118,7 +116,7 @@ impl Widget for Span { // it, since a text wraps at the width it is actually given. A // fixed child's slot is its own answer, so a drawing made in the // room is put there as it is, and one not made yet is made here. - let slot = along(from, start); + let slot = along(from, to); let mut place = slot.shifted_desc().fills().on_axis(axis); if len.leftover > Weight::ZERO && shares { place = place.rel_base(axis, slot.len()); @@ -136,7 +134,6 @@ impl Widget for Span { } } fixed.px += self.gap; - start = shared(fixed, taken); } // Carried whole rather than collapsed to one share: a span that sizes