Put lengths, padding, gaps and alignment on the grid too

`Len` is `Px` beside `Rel` beside `Weight`, so the seam `4e28f10` left in
`Span` -- a float length added to a fixed-point cursor -- is gone, and the
sum a span compares against its box is exact.

`Weight` is its own scale, `Fixed<16>`, because a share of what is left over
is not a fraction of anything: a list divides its room by the total of them,
so the range has to hold a whole list's worth while the precision only has to
tell two weights apart. `Rel::ratio` turns two weights into a share on the
finer grid, which is what a span needs and what dividing them on their own
grid would round away.

`AxisAlign` holds a `Rel` rather than a float, which is what the layout was
reading out of it anyway. `Padding` and `Span::gap` hold `Px`, converted
where they are built instead of on every frame. `RegionAlign::rel` is gone;
its one caller wanted a position, and now builds one.

`Fixed` gains `from_num` for a number as it is written in source, `mul_int`
for a length repeated a whole number of times, and `ratio`.

Checked: fmt, clippy, 101 tests, 100 generated seeds in 86 s, all five
shrinker cases at 300 seeds, and all five examples byte-identical at
1920x1200 against `4e28f10`.

With the fuzzer comparing for equality rather than within 0.05 px, four of
the five cases now pass 100 seeds -- `resize-repaint` joins the other three.
`reorder` still fails one seed by one step, so the last of it is in what a
box is measured *in*: `px_len` and the window are still floats.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-16 01:40:42 -04:00
1 parent 4e28f1047e
commit bd6de71a55
15 files changed
+199 -158

No files matched your search

+4 -4
View File
@@ -111,7 +111,7 @@ pub struct Branch {
impl Widget for Branch {
fn draw(&mut self, painter: &mut Painter) -> Size {
let mut top = UiRegion::FULL;
top.y.end = top.y.start.offset(40.0);
top.y.end = top.y.start.offset(Px::from_int(40));
let measured = painter.widget_within(&self.probe, top).len(Axis::X);
let px = measured
.apply_leftover()
@@ -119,7 +119,7 @@ impl Widget for Branch {
.to_f32();
let mut below = UiRegion::FULL;
below.y.start = below.y.start.offset(40.0);
below.y.start = below.y.start.offset(Px::from_int(40));
match px > self.threshold {
true => painter.widget_within(&self.wide, below),
false => painter.widget_within(&self.narrow, below),
@@ -312,7 +312,7 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
let inner = self.noded(inner);
// Each side its own, since a padding that is the same all round
// hides anything that treats one edge differently from another.
let mut side = || self.rng.below(24) as f32;
let mut side = || Px::from_int(self.rng.below(24) as i32);
let padding = Padding {
left: side(),
right: side(),
@@ -360,7 +360,7 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
let id = Span {
children,
dir,
gap: self.rng.below(3) as f32 * 4.0,
gap: Px::from_int(self.rng.below(3) as i32 * 4),
// Derive this from an existing choice: a seed must keep growing
// the same tree when the generator gains another configuration.
ortho: match dir.axis {