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

+38 -44
View File
@@ -1,5 +1,5 @@
use super::*;
use crate::{UiNum, util::impl_op};
use crate::{Px, Rel, UiNum, Weight, util::impl_op};
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Size {
@@ -7,11 +7,14 @@ pub struct Size {
pub y: Len,
}
#[derive(Debug, Clone, Copy, PartialEq)]
/// What a widget asks for along one axis: pixels, a fraction of the box it
/// is given, and a share of whatever is left over once everything fixed has
/// been taken. The three add up rather than choosing between one another.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Len {
pub px: f32,
pub rel: f32,
pub leftover: f32,
pub px: Px,
pub rel: Rel,
pub leftover: Weight,
}
impl<N: UiNum> From<N> for Len {
@@ -97,41 +100,44 @@ impl Size {
impl Len {
pub const ZERO: Self = Self {
px: 0.0,
rel: 0.0,
leftover: 0.0,
px: Px::ZERO,
rel: Rel::ZERO,
leftover: Weight::ZERO,
};
pub const LEFTOVER: Self = Self {
px: 0.0,
rel: 0.0,
leftover: 1.0,
px: Px::ZERO,
rel: Rel::ZERO,
leftover: Weight::ONE,
};
/// The whole of what is left over counts as the whole box, which is what
/// a length means to something that is not dividing a box between
/// siblings -- a scroll asking how long its content is.
pub fn apply_leftover(&self) -> UiScalar {
let share = if self.leftover > 0.0 { 1.0 } else { 0.0 };
UiScalar::new(self.rel + share, self.px)
let share = match self.leftover > Weight::ZERO {
true => Rel::ONE,
false => Rel::ZERO,
};
UiScalar::from_parts(self.rel.add(share), self.px)
}
pub fn px(px: impl UiNum) -> Self {
Self {
px: px.to_f32(),
rel: 0.0,
leftover: 0.0,
px: Px::from_num(px),
..Self::ZERO
}
}
pub fn rel(rel: impl UiNum) -> Self {
Self {
px: 0.0,
rel: rel.to_f32(),
leftover: 0.0,
rel: Rel::from_num(rel),
..Self::ZERO
}
}
pub fn leftover(ratio: impl UiNum) -> Self {
Self {
px: 0.0,
rel: 0.0,
leftover: ratio.to_f32(),
leftover: Weight::from_num(ratio),
..Self::ZERO
}
}
}
@@ -140,33 +146,21 @@ pub mod len_fns {
use super::*;
pub fn px(px: impl UiNum) -> Len {
Len {
px: px.to_f32(),
rel: 0.0,
leftover: 0.0,
}
Len::px(px)
}
pub fn rel(rel: impl UiNum) -> Len {
Len {
px: 0.0,
rel: rel.to_f32(),
leftover: 0.0,
}
Len::rel(rel)
}
pub fn leftover(ratio: impl UiNum) -> Len {
Len {
px: 0.0,
rel: 0.0,
leftover: ratio.to_f32(),
}
Len::leftover(ratio)
}
}
impl_op!(Len Add add; px rel leftover);
impl_op!(Len Sub sub; px rel leftover);
impl_op!(same Len Add add; px rel leftover);
impl_op!(same Len Sub sub; px rel leftover);
impl_op!(Size Add add; x y);
impl_op!(Size Sub sub; x y);
impl_op!(same Size Add add; x y);
impl_op!(same Size Sub sub; x y);
impl Default for Len {
fn default() -> Self {
@@ -182,13 +176,13 @@ impl std::fmt::Display for Size {
impl std::fmt::Display for Len {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.px != 0.0 {
if self.px != Px::ZERO {
write!(f, "{} px;", self.px)?;
}
if self.rel != 0.0 {
if self.rel != Rel::ZERO {
write!(f, "{} rel;", self.rel)?;
}
if self.leftover != 0.0 {
if self.leftover != Weight::ZERO {
write!(f, "{} leftover;", self.leftover)?;
}
Ok(())