Give a length with no share in it its own type again

`UiScalar` was `Len` without the `leftover` weight, which is the separation
canonical `main` already had as `Len` beside `LayoutLen` and this branch
collapsed. It is needed back for the queued clamp: a cap may not contain a
share, because a cap has to read the report a rule otherwise makes moot, and
a share puts the container's division into the same equation -- two
self-consistent assignments, which is the multiple-fixed-point failure
generated seed 13 punished for orthogonal sizing. `min(report, cap)` is not
a `LayoutLen` either: it is a sum of parts, and the smaller of two of them
is not one.

So `UiScalar` is `Len`, what was `Len` is `LayoutLen`, and the two say in
their docs which is which: a `Len` is pixels plus a fraction of a box -- a
position being the length from the box's start, which is why a span is two
of them -- and a `LayoutLen` is a `Len` plus a claim only a container
dividing its room can answer. `From<Len> for LayoutLen` is the one-way step
between them.

Names only; the shader's `UiScalar` is renamed with them. Checked: fmt,
clippy, 105 tests, and `tabs`, `minimal`, `view`, `text` and `random`
byte-identical at 1920x1200.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-16 13:40:15 -04:00
1 parent 4f5e27cba9
commit a8898aaa54
27 files changed
+218 -202

No files matched your search

+3 -3
View File
@@ -15,7 +15,7 @@
//! reuse, size, placement, and text events for one suspicious widget. The //! reuse, size, placement, and text events for one suspicious widget. The
//! selection is a set and survives [`take`] until cleared. //! selection is a set and survives [`take`] until cleared.
use crate::{Axis, Len, PxVec2, Size, UiRegion, WidgetId}; use crate::{Axis, LayoutLen, PxVec2, Size, UiRegion, WidgetId};
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@@ -287,7 +287,7 @@ pub enum TraceEvent {
id: WidgetId, id: WidgetId,
reader: WidgetId, reader: WidgetId,
axis: Axis, axis: Axis,
hint: Option<Len>, hint: Option<LayoutLen>,
}, },
TextRendered { TextRendered {
id: WidgetId, id: WidgetId,
@@ -389,7 +389,7 @@ pub(crate) fn size_read(id: WidgetId, reader: WidgetId, size: Size) {
trace(id, TraceEvent::SizeRead { id, reader, size }); trace(id, TraceEvent::SizeRead { id, reader, size });
} }
pub(crate) fn hint_read(id: WidgetId, reader: WidgetId, axis: Axis, hint: Option<Len>) { pub(crate) fn hint_read(id: WidgetId, reader: WidgetId, axis: Axis, hint: Option<LayoutLen>) {
trace( trace(
id, id,
TraceEvent::HintRead { TraceEvent::HintRead {
+6 -6
View File
@@ -172,14 +172,14 @@ impl Vec2 {
} }
} }
impl UiScalar { impl Len {
pub const fn align(&self, align: AxisAlign) -> UiSpan { pub const fn align(&self, align: AxisAlign) -> UiSpan {
let rel = align.rel(); let rel = align.rel();
let rest = Rel::ONE.sub(rel); let rest = Rel::ONE.sub(rel);
let at = UiScalar::from_parts(rel, Px::ZERO); let at = Len::from_parts(rel, Px::ZERO);
UiSpan { UiSpan {
start: UiScalar::from_parts(at.rel.sub(self.rel.mul(rel)), at.px.sub(self.px.mul(rel))), start: Len::from_parts(at.rel.sub(self.rel.mul(rel)), at.px.sub(self.px.mul(rel))),
end: UiScalar::from_parts(at.rel.add(self.rel.mul(rest)), at.px.add(self.px.mul(rest))), end: Len::from_parts(at.rel.add(self.rel.mul(rest)), at.px.add(self.px.mul(rest))),
} }
} }
} }
@@ -221,8 +221,8 @@ impl From<CardinalAlign> for Align {
const impl From<RegionAlign> for UiVec2 { const impl From<RegionAlign> for UiVec2 {
fn from(align: RegionAlign) -> Self { fn from(align: RegionAlign) -> Self {
Self::new( Self::new(
UiScalar::from_parts(align.x.rel(), Px::ZERO), Len::from_parts(align.x.rel(), Px::ZERO),
UiScalar::from_parts(align.y.rel(), Px::ZERO), Len::from_parts(align.y.rel(), Px::ZERO),
) )
} }
} }
+54 -37
View File
@@ -3,23 +3,28 @@ use crate::{Px, PxVec2, Rel, UiNum, Weight, util::impl_op};
#[derive(Debug, Default, Clone, Copy, PartialEq)] #[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct Size { pub struct Size {
pub x: Len, pub x: LayoutLen,
pub y: Len, pub y: LayoutLen,
} }
/// What a widget asks for along one axis: pixels, a fraction of the box it /// What a widget asks for along one axis: a [`Len`] -- pixels and a fraction
/// is given, and a share of whatever is left over once everything fixed has /// of the box it is given -- plus a share of whatever is left over once
/// been taken. The three add up rather than choosing between one another. /// everything fixed has been taken. The parts add up rather than choosing
/// between one another.
///
/// Only a container dividing its room can answer a share, so a length nobody
/// divides is a `Len`: a position, a padding, a cap, anything already
/// resolved.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Len { pub struct LayoutLen {
pub px: Px, pub px: Px,
pub rel: Rel, pub rel: Rel,
pub leftover: Weight, pub leftover: Weight,
} }
impl<N: UiNum> From<N> for Len { impl<N: UiNum> From<N> for LayoutLen {
fn from(value: N) -> Self { fn from(value: N) -> Self {
Len::px(value.to_f32()) LayoutLen::px(value.to_f32())
} }
} }
@@ -32,21 +37,33 @@ impl<Nx: UiNum, Ny: UiNum> From<(Nx, Ny)> for Size {
} }
} }
impl From<Len> for Size { /// A length with no share in it is a length a container does not have to
fn from(value: Len) -> Self { /// divide, which is one it can always give.
impl From<Len> for LayoutLen {
fn from(len: Len) -> Self {
Self {
px: len.px,
rel: len.rel,
leftover: Weight::ZERO,
}
}
}
impl From<LayoutLen> for Size {
fn from(value: LayoutLen) -> Self {
Self { x: value, y: value } Self { x: value, y: value }
} }
} }
impl Size { impl Size {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
x: Len::ZERO, x: LayoutLen::ZERO,
y: Len::ZERO, y: LayoutLen::ZERO,
}; };
pub const LEFTOVER: Self = Self { pub const LEFTOVER: Self = Self {
x: Len::LEFTOVER, x: LayoutLen::LEFTOVER,
y: Len::LEFTOVER, y: LayoutLen::LEFTOVER,
}; };
/// From something measured outside layout -- a texture, a shaped line -- /// From something measured outside layout -- a texture, a shaped line --
@@ -57,28 +74,28 @@ impl Size {
pub const fn from_px(v: PxVec2) -> Self { pub const fn from_px(v: PxVec2) -> Self {
Self { Self {
x: Len { x: LayoutLen {
px: v.x, px: v.x,
..Len::ZERO ..LayoutLen::ZERO
}, },
y: Len { y: LayoutLen {
px: v.y, px: v.y,
..Len::ZERO ..LayoutLen::ZERO
}, },
} }
} }
pub fn rel(v: Vec2) -> Self { pub fn rel(v: Vec2) -> Self {
Self { Self {
x: Len::rel(v.x), x: LayoutLen::rel(v.x),
y: Len::rel(v.y), y: LayoutLen::rel(v.y),
} }
} }
pub fn leftover(v: Vec2) -> Self { pub fn leftover(v: Vec2) -> Self {
Self { Self {
x: Len::leftover(v.x), x: LayoutLen::leftover(v.x),
y: Len::leftover(v.y), y: LayoutLen::leftover(v.y),
} }
} }
@@ -89,7 +106,7 @@ impl Size {
} }
} }
pub fn from_axis(axis: Axis, aligned: Len, ortho: Len) -> Self { pub fn from_axis(axis: Axis, aligned: LayoutLen, ortho: LayoutLen) -> Self {
match axis { match axis {
Axis::X => Self { Axis::X => Self {
x: aligned, x: aligned,
@@ -102,7 +119,7 @@ impl Size {
} }
} }
pub fn axis(&self, axis: Axis) -> Len { pub fn axis(&self, axis: Axis) -> LayoutLen {
match axis { match axis {
Axis::X => self.x, Axis::X => self.x,
Axis::Y => self.y, Axis::Y => self.y,
@@ -110,7 +127,7 @@ impl Size {
} }
} }
impl Len { impl LayoutLen {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
px: Px::ZERO, px: Px::ZERO,
rel: Rel::ZERO, rel: Rel::ZERO,
@@ -126,12 +143,12 @@ impl Len {
/// The whole of what is left over counts as the whole box, which is what /// 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 /// a length means to something that is not dividing a box between
/// siblings -- a scroll asking how long its content is. /// siblings -- a scroll asking how long its content is.
pub fn apply_leftover(&self) -> UiScalar { pub fn apply_leftover(&self) -> Len {
let share = match self.leftover > Weight::ZERO { let share = match self.leftover > Weight::ZERO {
true => Rel::ONE, true => Rel::ONE,
false => Rel::ZERO, false => Rel::ZERO,
}; };
UiScalar::from_parts(self.rel.add(share), self.px) Len::from_parts(self.rel.add(share), self.px)
} }
pub fn px(px: impl UiNum) -> Self { pub fn px(px: impl UiNum) -> Self {
@@ -157,24 +174,24 @@ impl Len {
pub mod len_fns { pub mod len_fns {
use super::*; use super::*;
pub fn px(px: impl UiNum) -> Len { pub fn px(px: impl UiNum) -> LayoutLen {
Len::px(px) LayoutLen::px(px)
} }
pub fn rel(rel: impl UiNum) -> Len { pub fn rel(rel: impl UiNum) -> LayoutLen {
Len::rel(rel) LayoutLen::rel(rel)
} }
pub fn leftover(ratio: impl UiNum) -> Len { pub fn leftover(ratio: impl UiNum) -> LayoutLen {
Len::leftover(ratio) LayoutLen::leftover(ratio)
} }
} }
impl_op!(same Len Add add; px rel leftover); impl_op!(same LayoutLen Add add; px rel leftover);
impl_op!(same Len Sub sub; px rel leftover); impl_op!(same LayoutLen Sub sub; px rel leftover);
impl_op!(same Size Add add; x y); impl_op!(same Size Add add; x y);
impl_op!(same Size Sub sub; x y); impl_op!(same Size Sub sub; x y);
impl Default for Len { impl Default for LayoutLen {
fn default() -> Self { fn default() -> Self {
Self::leftover(1.0) Self::leftover(1.0)
} }
@@ -186,7 +203,7 @@ impl std::fmt::Display for Size {
} }
} }
impl std::fmt::Display for Len { impl std::fmt::Display for LayoutLen {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.px != Px::ZERO { if self.px != Px::ZERO {
write!(f, "{} px;", self.px)?; write!(f, "{} px;", self.px)?;
+42 -38
View File
@@ -6,41 +6,41 @@ use crate::{Px, PxVec2, Rel, UiNum, util::impl_op};
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)]
pub struct UiVec2 { pub struct UiVec2 {
pub x: UiScalar, pub x: Len,
pub y: UiScalar, pub y: Len,
} }
impl UiVec2 { impl UiVec2 {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
x: UiScalar::ZERO, x: Len::ZERO,
y: UiScalar::ZERO, y: Len::ZERO,
}; };
pub const fn new(x: UiScalar, y: UiScalar) -> Self { pub const fn new(x: Len, y: Len) -> Self {
Self { x, y } Self { x, y }
} }
pub const fn px(px: impl const Into<Vec2>) -> Self { pub const fn px(px: impl const Into<Vec2>) -> Self {
let px = px.into(); let px = px.into();
Self { Self {
x: UiScalar::px(px.x), x: Len::px(px.x),
y: UiScalar::px(px.y), y: Len::px(px.y),
} }
} }
/// From lengths already on the grid, with no fraction of a box. /// From lengths already on the grid, with no fraction of a box.
pub const fn from_px(px: PxVec2) -> Self { pub const fn from_px(px: PxVec2) -> Self {
Self { Self {
x: UiScalar::from_parts(Rel::ZERO, px.x), x: Len::from_parts(Rel::ZERO, px.x),
y: UiScalar::from_parts(Rel::ZERO, px.y), y: Len::from_parts(Rel::ZERO, px.y),
} }
} }
pub const fn rel(rel: impl const Into<Vec2>) -> Self { pub const fn rel(rel: impl const Into<Vec2>) -> Self {
let rel = rel.into(); let rel = rel.into();
Self { Self {
x: UiScalar::rel(rel.x), x: Len::rel(rel.x),
y: UiScalar::rel(rel.y), y: Len::rel(rel.y),
} }
} }
@@ -61,14 +61,14 @@ impl UiVec2 {
} }
} }
pub fn axis_mut(&mut self, axis: Axis) -> &mut UiScalar { pub fn axis_mut(&mut self, axis: Axis) -> &mut Len {
match axis { match axis {
Axis::X => &mut self.x, Axis::X => &mut self.x,
Axis::Y => &mut self.y, Axis::Y => &mut self.y,
} }
} }
pub fn axis(&self, axis: Axis) -> UiScalar { pub fn axis(&self, axis: Axis) -> Len {
match axis { match axis {
Axis::X => self.x, Axis::X => self.x,
Axis::Y => self.y, Axis::Y => self.y,
@@ -83,7 +83,7 @@ impl UiVec2 {
pub const FULL_SIZE: Self = Self::rel(Vec2::ONE); pub const FULL_SIZE: Self = Self::rel(Vec2::ONE);
pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self { pub const fn from_axis(axis: Axis, aligned: Len, ortho: Len) -> Self {
match axis { match axis {
Axis::X => Self { Axis::X => Self {
x: aligned, x: aligned,
@@ -129,21 +129,27 @@ where
} }
} }
/// A position along one axis, as a fraction of the box it sits in plus an /// A length along one axis: a fraction of the box it is measured in plus an
/// offset: `rel * len + px`. Both parts are fixed point, so composing one /// offset, `rel * box + px`. A position is the same number -- the length from
/// through a chain of boxes rounds only where it multiplies and lands on the /// the start of the box to the point -- which is why a [`UiSpan`] is two of
/// same number as any other route to the same place. /// these. Both parts are fixed point, so composing one through a chain of
/// boxes rounds only where it multiplies, and lands on the same number as any
/// other route to the same place.
///
/// It carries no claim on what a container has left over. That is
/// [`crate::LayoutLen`], which is this plus a weight, and which means nothing
/// to anyone but whoever divides the room.
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, Default, bytemuck::Zeroable)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, Default, bytemuck::Zeroable)]
pub struct UiScalar { pub struct Len {
pub rel: Rel, pub rel: Rel,
pub px: Px, pub px: Px,
} }
impl_op!(same UiScalar Add add; rel px); impl_op!(same Len Add add; rel px);
impl_op!(same UiScalar Sub sub; rel px); impl_op!(same Len Sub sub; rel px);
impl UiScalar { impl Len {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
rel: Rel::ZERO, rel: Rel::ZERO,
px: Px::ZERO, px: Px::ZERO,
@@ -192,10 +198,8 @@ impl UiScalar {
} }
} }
/// Both channels by the same factor, which is what a fraction of a /// Both parts by the same fraction, which is what a part of a length
/// length means when the length is part pixels and part a share. /// means when the length is part pixels and part a fraction of a box.
/// Both channels by the same fraction, which is what a part of a length
/// means when the length is part pixels and part a share.
pub const fn scale(&self, by: Rel) -> Self { pub const fn scale(&self, by: Rel) -> Self {
Self { Self {
rel: self.rel.mul(by), rel: self.rel.mul(by),
@@ -215,14 +219,14 @@ impl UiScalar {
} }
} }
pub fn within_len(&self, len: UiScalar) -> Self { pub fn within_len(&self, len: Len) -> Self {
self.within(&UiSpan { self.within(&UiSpan {
start: UiScalar::ZERO, start: Len::ZERO,
end: len, end: len,
}) })
} }
pub fn select_len(&self, len: UiScalar) -> Self { pub fn select_len(&self, len: Len) -> Self {
len.within_len(*self) len.within_len(*self)
} }
@@ -245,24 +249,24 @@ impl UiScalar {
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
pub struct UiSpan { pub struct UiSpan {
pub start: UiScalar, pub start: Len,
pub end: UiScalar, pub end: Len,
} }
impl UiSpan { impl UiSpan {
pub const FULL: Self = Self { pub const FULL: Self = Self {
start: UiScalar::ZERO, start: Len::ZERO,
end: UiScalar::FULL, end: Len::FULL,
}; };
pub const fn rel(rel: f32) -> Self { pub const fn rel(rel: f32) -> Self {
Self { Self {
start: UiScalar::rel(rel), start: Len::rel(rel),
end: UiScalar::rel(rel), end: Len::rel(rel),
} }
} }
pub const fn new(start: UiScalar, end: UiScalar) -> Self { pub const fn new(start: Len, end: Len) -> Self {
Self { start, end } Self { start, end }
} }
@@ -273,7 +277,7 @@ impl UiSpan {
std::mem::swap(&mut self.start.px, &mut self.end.px); std::mem::swap(&mut self.start.px, &mut self.end.px);
} }
pub const fn shift(&mut self, offset: UiScalar) { pub const fn shift(&mut self, offset: Len) {
self.start += offset; self.start += offset;
self.end += offset; self.end += offset;
} }
@@ -285,7 +289,7 @@ impl UiSpan {
} }
} }
pub const fn len(&self) -> UiScalar { pub const fn len(&self) -> Len {
self.end - self.start self.end - self.start
} }
} }
+10 -10
View File
@@ -49,16 +49,16 @@ struct RawSpan {
end: RawScalar, end: RawScalar,
} }
fn scalar_of(raw: RawScalar) -> UiScalar { fn scalar_of(raw: RawScalar) -> Len {
return UiScalar(f32(raw.rel) * REL_STEP, f32(raw.px) * PX_STEP); return Len(f32(raw.rel) * REL_STEP, f32(raw.px) * PX_STEP);
} }
fn span_of(raw: RawSpan) -> UiSpan { fn span_of(raw: RawSpan) -> UiSpan {
return UiSpan(scalar_of(raw.start), scalar_of(raw.end)); return UiSpan(scalar_of(raw.start), scalar_of(raw.end));
} }
fn scalar_of_pair(raw: vec2<i32>) -> UiScalar { fn scalar_of_pair(raw: vec2<i32>) -> Len {
return UiScalar(f32(raw.x) * REL_STEP, f32(raw.y) * PX_STEP); return Len(f32(raw.x) * REL_STEP, f32(raw.y) * PX_STEP);
} }
struct Region { struct Region {
@@ -72,12 +72,12 @@ const MOVE_NONE: u32 = 4294967295u;
// resolve a deep one the same way. // resolve a deep one the same way.
const CHAIN_LIMIT: u32 = 64u; const CHAIN_LIMIT: u32 = 64u;
// The same expression `UiScalar::within` uses, in floats rather than on the // The same expression `Len::within` uses, in floats rather than on the
// CPU's grid: a move is resolved here so that scrolling a subtree writes one // CPU's grid: a move is resolved here so that scrolling a subtree writes one
// entry instead of walking it. What has to hold is that this agrees with // entry instead of walking it. What has to hold is that this agrees with
// itself frame to frame, not that it matches the CPU to the last bit. // itself frame to frame, not that it matches the CPU to the last bit.
fn scalar_within(s: UiScalar, p: UiSpan) -> UiScalar { fn scalar_within(s: Len, p: UiSpan) -> Len {
return UiScalar( return Len(
p.start.rel + (p.end.rel - p.start.rel) * s.rel, p.start.rel + (p.end.rel - p.start.rel) * s.rel,
s.px + (p.start.px + (p.end.px - p.start.px) * s.rel), s.px + (p.start.px + (p.end.px - p.start.px) * s.rel),
); );
@@ -102,11 +102,11 @@ fn resolve_move(idx: u32, local: Region) -> Region {
} }
struct UiSpan { struct UiSpan {
start: UiScalar, start: Len,
end: UiScalar, end: Len,
} }
struct UiScalar { struct Len {
rel: f32, rel: f32,
px: f32, px: f32,
} }
+2 -2
View File
@@ -1,5 +1,5 @@
use crate::{ use crate::{
Holds, LayerId, Len, MaskIdx, MoveIdx, PrimitiveHandle, RegionAlign, Size, TextureHandle, Holds, LayerId, LayoutLen, MaskIdx, MoveIdx, PrimitiveHandle, RegionAlign, Size, TextureHandle,
UiRegion, WidgetId, UiRegion, WidgetId,
}; };
@@ -40,7 +40,7 @@ pub struct ActiveData {
/// The declared lengths whoever drew this widget resolved into its box. /// The declared lengths whoever drew this widget resolved into its box.
/// A change to one moves a box this widget cannot fix by drawing again, /// A change to one moves a box this widget cannot fix by drawing again,
/// and comparing them is what says so. /// and comparing them is what says so.
pub declared: [Option<Len>; 2], pub declared: [Option<LayoutLen>; 2],
/// The alignment its parent asked it with. A local redraw repeats that /// The alignment its parent asked it with. A local redraw repeats that
/// question, including an override chosen by a container. /// question, including an override chosen by a container.
pub align: RegionAlign, pub align: RegionAlign,
+4 -4
View File
@@ -1,4 +1,4 @@
use crate::{Px, REL_SHIFT, UiScalar, fixed::div_toward, fixed::narrow}; use crate::{Len, Px, REL_SHIFT, fixed::div_toward, fixed::narrow};
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
/// The lengths of a box, in pixels, that one drawing of a widget holds for: /// The lengths of a box, in pixels, that one drawing of a widget holds for:
@@ -50,7 +50,7 @@ impl Holds {
/// the length alone instead gives a point that need not even contain the /// the length alone instead gives a point that need not even contain the
/// box the part was drawn in, which is a range excluding the drawing it /// box the part was drawn in, which is a range excluding the drawing it
/// was made for. /// was made for.
pub const fn through(self, len: UiScalar) -> Self { pub const fn through(self, len: Len) -> Self {
let rel = len.rel.raw() as i64; let rel = len.rel.raw() as i64;
if rel == 0 { if rel == 0 {
return Self::ANY; return Self::ANY;
@@ -98,7 +98,7 @@ mod tests {
#[test] #[test]
fn through_reverses_a_range_for_a_negative_fraction() { fn through_reverses_a_range_for_a_negative_fraction() {
// `10 - box / 2` is between 20 and 40 for boxes from -60 to -20. // `10 - box / 2` is between 20 and 40 for boxes from -60 to -20.
let part = UiScalar::from_parts(Rel::from_f32(-0.5), Px::from_int(10)); let part = Len::from_parts(Rel::from_f32(-0.5), Px::from_int(10));
let holds = Holds::from(Px::from_int(20)..=Px::from_int(40)).through(part); let holds = Holds::from(Px::from_int(20)..=Px::from_int(40)).through(part);
assert!(holds.contains(Px::from_int(-60)) && holds.contains(Px::from_int(-20))); assert!(holds.contains(Px::from_int(-60)) && holds.contains(Px::from_int(-20)));
assert!(!holds.contains(Px::from_int(-61)) && !holds.contains(Px::from_int(-19))); assert!(!holds.contains(Px::from_int(-61)) && !holds.contains(Px::from_int(-19)));
@@ -109,7 +109,7 @@ mod tests {
/// box is not a whole number of steps. /// box is not a whole number of steps.
#[test] #[test]
fn a_part_maps_back_onto_the_box_it_was_measured_in() { fn a_part_maps_back_onto_the_box_it_was_measured_in() {
let part = UiScalar::from_parts(Rel::from_f32(1.0 / 3.0), Px::from_int(-146)); let part = Len::from_parts(Rel::from_f32(1.0 / 3.0), Px::from_int(-146));
for box_len in (440..460).map(Px::from_int) { for box_len in (440..460).map(Px::from_int) {
let holds = Holds::at(part.to_px(box_len)).through(part); let holds = Holds::at(part.to_px(box_len)).through(part);
assert!(holds.contains(box_len), "{box_len:?} left out by {holds:?}"); assert!(holds.contains(box_len), "{box_len:?} left out by {holds:?}");
+11 -11
View File
@@ -1,8 +1,8 @@
#[cfg(feature = "layout-diagnostics")] #[cfg(feature = "layout-diagnostics")]
use crate::layout_diagnostics::{self as diag, Counter}; use crate::layout_diagnostics::{self as diag, Counter};
use crate::{ use crate::{
Axis, Holds, Len, Px, PxVec2, RegionAlign, Rel, RenderedText, Size, StrongWidget, TextAttrs, Axis, Holds, LayoutLen, Len, Px, PxVec2, RegionAlign, Rel, RenderedText, Size, StrongWidget,
TextBuffer, TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, Weight, TextAttrs, TextBuffer, TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiVec2, Weight,
WidgetId, Widgets, WidgetId, Widgets,
render::{ render::{
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst, GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst,
@@ -109,7 +109,7 @@ impl<'a> Painter<'a> {
/// it resolves into its box. Reading them depends on nothing -- the box /// it resolves into its box. Reading them depends on nothing -- the box
/// that comes of them is kept on the child, and `redraw` compares it /// that comes of them is kept on the child, and `redraw` compares it
/// there. /// there.
fn declared_lens<W: ?Sized>(&self, id: &StrongWidget<W>) -> [Option<Len>; 2] { fn declared_lens<W: ?Sized>(&self, id: &StrongWidget<W>) -> [Option<LayoutLen>; 2] {
declared_lens(self.rsc.widgets(), id.id()) declared_lens(self.rsc.widgets(), id.id())
} }
@@ -218,7 +218,7 @@ impl<'a> Painter<'a> {
/// What a child says its length is without being drawn, if it can say. /// What a child says its length is without being drawn, if it can say.
/// Asking counts as reading its size. /// Asking counts as reading its size.
pub fn size_hint<W: ?Sized>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Option<Len> { pub fn size_hint<W: ?Sized>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Option<LayoutLen> {
let widgets = self.rsc.widgets(); let widgets = self.rsc.widgets();
// A rule is the answer where there is one: it wins over whatever the // A rule is the answer where there is one: it wins over whatever the
// widget would draw, so it has to win over what the widget says too. // widget would draw, so it has to win over what the widget says too.
@@ -252,7 +252,7 @@ impl<'a> Painter<'a> {
child: &StrongWidget<W>, child: &StrongWidget<W>,
axis: Axis, axis: Axis,
region: UiRegion, region: UiRegion,
) -> Option<Len> { ) -> Option<LayoutLen> {
let declared = self.declared_lens(child); let declared = self.declared_lens(child);
let align = self.rsc.widgets().alignment(child.id()); let align = self.rsc.widgets().alignment(child.id());
let local = declared_box(region, declared, align); let local = declared_box(region, declared, align);
@@ -472,7 +472,7 @@ impl<W: ?Sized> DrawResult<'_, '_, W> {
self.size self.size
} }
pub fn len(self, axis: Axis) -> Len { pub fn len(self, axis: Axis) -> LayoutLen {
self.size().axis(axis) self.size().axis(axis)
} }
} }
@@ -505,7 +505,7 @@ impl PrimitiveLike for &TextureHandle {
/// What a widget declares a length of its box to be. `leftover` is not one: a /// What a widget declares a length of its box to be. `leftover` is not one: a
/// share of what is left over is only a length to the widget dividing one, /// share of what is left over is only a length to the widget dividing one,
/// so it passes up in the size instead. /// so it passes up in the size instead.
pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option<Len>; 2] { pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option<LayoutLen>; 2] {
let rules = widgets.size_rules(id); let rules = widgets.size_rules(id);
let widget = widgets.get_dyn(id); let widget = widgets.get_dyn(id);
AXES.map(|axis| { AXES.map(|axis| {
@@ -537,7 +537,7 @@ pub(crate) fn placed_box(
region: UiRegion, region: UiRegion,
size: Size, size: Size,
align: RegionAlign, align: RegionAlign,
declared: [Option<Len>; 2], declared: [Option<LayoutLen>; 2],
) -> UiRegion { ) -> UiRegion {
let mut placed = region; let mut placed = region;
for (axis, declared) in AXES.into_iter().zip(declared) { for (axis, declared) in AXES.into_iter().zip(declared) {
@@ -546,7 +546,7 @@ pub(crate) fn placed_box(
continue; continue;
} }
let span = placed.axis_mut(axis); let span = placed.axis_mut(axis);
let len = span.len().scale(reported.rel) + UiScalar::from_parts(Rel::ZERO, reported.px); let len = span.len().scale(reported.rel) + Len::from_parts(Rel::ZERO, reported.px);
span.start += (span.len() - len).scale(align.axis(axis).rel()); span.start += (span.len() - len).scale(align.axis(axis).rel());
span.end = span.start + len; span.end = span.start + len;
} }
@@ -559,13 +559,13 @@ pub(crate) fn placed_box(
/// space hands back the same length, so this is the identity for it. /// space hands back the same length, so this is the identity for it.
pub(crate) fn declared_box( pub(crate) fn declared_box(
mut region: UiRegion, mut region: UiRegion,
declared: [Option<Len>; 2], declared: [Option<LayoutLen>; 2],
align: RegionAlign, align: RegionAlign,
) -> UiRegion { ) -> UiRegion {
for (axis, len) in AXES.into_iter().zip(declared) { for (axis, len) in AXES.into_iter().zip(declared) {
let Some(len) = len else { continue }; let Some(len) = len else { continue };
let span = region.axis_mut(axis); let span = region.axis_mut(axis);
let len = UiScalar::from_parts(len.rel, len.px); let len = Len::from_parts(len.rel, len.px);
span.start += (span.len() - len).scale(align.axis(axis).rel()); span.start += (span.len() - len).scale(align.axis(axis).rel());
span.end = span.start + len; span.end = span.start + len;
} }
+11 -17
View File
@@ -2,9 +2,9 @@
use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind}; use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
use crate::ui::painter::{declared_box, declared_lens, placed_box}; use crate::ui::painter::{declared_box, declared_lens, placed_box};
use crate::{ use crate::{
ActiveData, Axis, DrawLayers, Holds, IdLike, Len, MaskIdx, MoveIdx, Moves, Painter, ActiveData, Axis, DrawLayers, Holds, IdLike, LayoutLen, Len, MaskIdx, MoveIdx, Moves, Painter,
PixelRegion, PxVec2, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, PixelRegion, PxVec2, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiSpan, Weight,
Weight, WidgetId, Widgets, WidgetId, Widgets,
util::{HashMap, Vec2}, util::{HashMap, Vec2},
}; };
@@ -75,14 +75,8 @@ impl UiRenderState {
/// downstream has to know the output's size to resolve a position. /// downstream has to know the output's size to resolve a position.
fn write_root(&mut self) { fn write_root(&mut self) {
let region = UiRegion::new( let region = UiRegion::new(
UiSpan::new( UiSpan::new(Len::ZERO, Len::from_parts(Rel::ZERO, self.output_size.x)),
UiScalar::ZERO, UiSpan::new(Len::ZERO, Len::from_parts(Rel::ZERO, self.output_size.y)),
UiScalar::from_parts(Rel::ZERO, self.output_size.x),
),
UiSpan::new(
UiScalar::ZERO,
UiScalar::from_parts(Rel::ZERO, self.output_size.y),
),
); );
match self.root_move == MoveIdx::NONE { match self.root_move == MoveIdx::NONE {
true => self.root_move = self.moves.push(MoveIdx::NONE, region), true => self.root_move = self.moves.push(MoveIdx::NONE, region),
@@ -255,8 +249,8 @@ impl UiRenderState {
let reported = answer.0.axis(axis); let reported = answer.0.axis(axis);
let placed_len = let placed_len =
match reported.leftover != Weight::ZERO || declared[axis as usize].is_some() { match reported.leftover != Weight::ZERO || declared[axis as usize].is_some() {
true => UiScalar::FULL, true => Len::FULL,
false => UiScalar::from_parts(reported.rel, reported.px), false => Len::from_parts(reported.rel, reported.px),
}; };
settled.1[axis as usize] = settled.1[axis as usize] =
settled.1[axis as usize].and(drawing_holds[axis as usize].through(placed_len)); settled.1[axis as usize].and(drawing_holds[axis as usize].through(placed_len));
@@ -765,10 +759,10 @@ impl UiRenderState {
let size = Size { let size = Size {
x: widget x: widget
.and_then(|w| w.size_hint(Axis::X)) .and_then(|w| w.size_hint(Axis::X))
.unwrap_or(Len::ZERO), .unwrap_or(LayoutLen::ZERO),
y: widget y: widget
.and_then(|w| w.size_hint(Axis::Y)) .and_then(|w| w.size_hint(Axis::Y))
.unwrap_or(Len::ZERO), .unwrap_or(LayoutLen::ZERO),
}; };
self.active.insert( self.active.insert(
id, id,
@@ -1079,7 +1073,7 @@ impl RegionRemap {
} }
} }
fn apply_scalar(self, scalar: UiScalar, from: UiSpan, to: UiSpan) -> UiScalar { fn apply_scalar(self, scalar: Len, from: UiSpan, to: UiSpan) -> Len {
let extent = from.end.rel - from.start.rel; let extent = from.end.rel - from.start.rel;
// A box that only moved, or that has no relative extent to divide, // A box that only moved, or that has no relative extent to divide,
// carries its parts by moving them, which is exact. Dividing to find // carries its parts by moving them, which is exact. Dividing to find
@@ -1100,7 +1094,7 @@ impl RegionRemap {
let from_px = fraction.lerp(from.start.px, from.end.px); let from_px = fraction.lerp(from.start.px, from.end.px);
let to_rel = fraction.lerp(to.start.rel, to.end.rel); let to_rel = fraction.lerp(to.start.rel, to.end.rel);
let to_px = fraction.lerp(to.start.px, to.end.px); let to_px = fraction.lerp(to.start.px, to.end.px);
UiScalar::from_parts(to_rel, scalar.px - from_px + to_px) Len::from_parts(to_rel, scalar.px - from_px + to_px)
} }
} }
+4 -4
View File
@@ -1,4 +1,4 @@
use crate::{Axis, Len, Painter, Size}; use crate::{Axis, LayoutLen, Painter, Size};
use std::any::Any; use std::any::Any;
mod data; mod data;
@@ -24,7 +24,7 @@ pub trait Widget: Any {
/// An exact length the widget can give without a painter or its children. /// An exact length the widget can give without a painter or its children.
/// Optional, and saves a draw rather than changing one: a hint that /// Optional, and saves a draw rather than changing one: a hint that
/// disagrees with the eventual draw fails a debug assertion. /// disagrees with the eventual draw fails a debug assertion.
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
None None
} }
} }
@@ -35,8 +35,8 @@ impl Widget for () {
Size::default() Size::default()
} }
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
Some(Len::default()) Some(LayoutLen::default())
} }
} }
+9 -9
View File
@@ -1,4 +1,4 @@
use crate::{Axis, Len, Weight}; use crate::{Axis, LayoutLen, Weight};
/// What a widget's length on one axis is, as a rule its parent applies where /// What a widget's length on one axis is, as a rule its parent applies where
/// it draws it rather than an answer the widget gives about itself. /// it draws it rather than an answer the widget gives about itself.
@@ -14,7 +14,7 @@ pub enum SizeRule {
#[default] #[default]
Free, Free,
/// This length, whatever the widget reports. /// This length, whatever the widget reports.
Exact(Len), Exact(LayoutLen),
} }
impl SizeRule { impl SizeRule {
@@ -22,7 +22,7 @@ impl SizeRule {
/// give one. `leftover` is never among them: a share is a length only to /// give one. `leftover` is never among them: a share is a length only to
/// whoever divides one, so it passes up in the reported size instead and /// whoever divides one, so it passes up in the reported size instead and
/// is resolved there. /// is resolved there.
pub fn declared(&self) -> Option<Len> { pub fn declared(&self) -> Option<LayoutLen> {
match self { match self {
Self::Exact(len) if len.leftover == Weight::ZERO => Some(*len), Self::Exact(len) if len.leftover == Weight::ZERO => Some(*len),
_ => None, _ => None,
@@ -33,7 +33,7 @@ impl SizeRule {
/// share is a length the widget's parent still has to divide, so it is /// share is a length the widget's parent still has to divide, so it is
/// known here and resolved there -- unlike `declared`, which is only the /// known here and resolved there -- unlike `declared`, which is only the
/// ones that give a box directly. /// ones that give a box directly.
pub fn known(&self) -> Option<Len> { pub fn known(&self) -> Option<LayoutLen> {
match self { match self {
Self::Free => None, Self::Free => None,
Self::Exact(len) => Some(*len), Self::Exact(len) => Some(*len),
@@ -41,7 +41,7 @@ impl SizeRule {
} }
/// The length a widget reporting `reported` ends up with. /// The length a widget reporting `reported` ends up with.
pub fn apply(&self, reported: Len) -> Len { pub fn apply(&self, reported: LayoutLen) -> LayoutLen {
match self { match self {
Self::Free => reported, Self::Free => reported,
Self::Exact(len) => *len, Self::Exact(len) => *len,
@@ -49,14 +49,14 @@ impl SizeRule {
} }
} }
impl From<Len> for SizeRule { impl From<LayoutLen> for SizeRule {
fn from(len: Len) -> Self { fn from(len: LayoutLen) -> Self {
Self::Exact(len) Self::Exact(len)
} }
} }
impl From<Option<Len>> for SizeRule { impl From<Option<LayoutLen>> for SizeRule {
fn from(len: Option<Len>) -> Self { fn from(len: Option<LayoutLen>) -> Self {
len.map_or(Self::Free, Self::Exact) len.map_or(Self::Free, Self::Exact)
} }
} }
+1 -1
View File
@@ -165,7 +165,7 @@ impl Harness {
} }
/// Changes a length rule after the fact, the way `.width()` sets one. /// Changes a length rule after the fact, the way `.width()` sets one.
pub fn set_len(&mut self, id: impl IdLike, axis: Axis, len: impl Into<Len>) { pub fn set_len(&mut self, id: impl IdLike, axis: Axis, len: impl Into<LayoutLen>) {
self.rsc self.rsc
.widgets_mut() .widgets_mut()
.set_size_rule(id, axis, SizeRule::Exact(len.into())); .set_size_rule(id, axis, SizeRule::Exact(len.into()));
+5 -5
View File
@@ -9,7 +9,7 @@ use crate::prelude::*;
use std::collections::HashMap; use std::collections::HashMap;
/// The declared lengths of one widget carrying a size rule, by axis. /// The declared lengths of one widget carrying a size rule, by axis.
pub type Lens = [Option<Len>; 2]; pub type Lens = [Option<LayoutLen>; 2];
/// Where one widget carrying an alignment sits, by axis. `None` uses the /// Where one widget carrying an alignment sits, by axis. `None` uses the
/// centered default. /// centered default.
@@ -181,10 +181,10 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
id id
} }
fn len(&mut self) -> Option<Len> { fn len(&mut self) -> Option<LayoutLen> {
match self.rng.below(4) { match self.rng.below(4) {
0 => Some(Len::px(20.0 + self.rng.below(180) as f32)), 0 => Some(LayoutLen::px(20.0 + self.rng.below(180) as f32)),
1 => Some(Len::LEFTOVER), 1 => Some(LayoutLen::LEFTOVER),
_ => None, _ => None,
} }
} }
@@ -367,7 +367,7 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
if dir.axis == Axis::X { if dir.axis == Axis::X {
self.rsc self.rsc
.widgets_mut() .widgets_mut()
.set_size_rules(id, None, Some(Len::rel(1.0))); .set_size_rules(id, None, Some(LayoutLen::rel(1.0)));
} }
self.tree.ids.push(id.id()); self.tree.ids.push(id.id());
self.tree.spans.push(Spanned { id, spares, grown }); self.tree.spans.push(Spanned { id, spares, grown });
+2 -2
View File
@@ -11,8 +11,8 @@ impl Widget for Image {
Size::px(self.handle.size()) Size::px(self.handle.size())
} }
fn size_hint(&self, axis: Axis) -> Option<Len> { fn size_hint(&self, axis: Axis) -> Option<LayoutLen> {
Some(Len::px(self.handle.size().axis(axis))) Some(LayoutLen::px(self.handle.size().axis(axis)))
} }
} }
+2 -2
View File
@@ -11,11 +11,11 @@ impl Widget for Pad {
.widget_aligned(&self.inner, self.padding.region(), RegionAlign::NEAR) .widget_aligned(&self.inner, self.padding.region(), RegionAlign::NEAR)
.size(); .size();
Size { Size {
x: Len { x: LayoutLen {
px: inner.x.px + self.padding.left + self.padding.right, px: inner.x.px + self.padding.left + self.padding.right,
..inner.x ..inner.x
}, },
y: Len { y: LayoutLen {
px: inner.y.px + self.padding.top + self.padding.bottom, px: inner.y.px + self.padding.top + self.padding.bottom,
..inner.y ..inner.y
}, },
+2 -2
View File
@@ -57,8 +57,8 @@ impl Widget for Scroll {
if moved || self.content_len != self.container_len { if moved || self.content_len != self.container_len {
let offset = UiVec2::from_axis( let offset = UiVec2::from_axis(
self.axis, self.axis,
UiScalar::from_parts(Rel::ZERO, anchor - self.amt), Len::from_parts(Rel::ZERO, anchor - self.amt),
UiScalar::ZERO, Len::ZERO,
); );
region = region.offset(offset); region = region.offset(offset);
region.axis_mut(self.axis).end = region.axis(self.axis).start.offset(self.content_len); region.axis_mut(self.axis).end = region.axis(self.axis).start.offset(self.content_len);
+11 -11
View File
@@ -12,10 +12,10 @@ impl Widget for Span {
let axis = self.dir.axis; let axis = self.dir.axis;
// A length for every child before their final boxes are chosen: from // A length for every child before their final boxes are chosen: from
// a hint where one exists, and from drawing otherwise. // a hint where one exists, and from drawing otherwise.
let mut cursor = UiScalar::rel_min(); let mut cursor = Len::rel_min();
let mut lens = Vec::with_capacity(self.children.len()); let mut lens = Vec::with_capacity(self.children.len());
for child in &self.children { for child in &self.children {
let mut span = UiSpan::new(cursor, UiScalar::rel_max()); let mut span = UiSpan::new(cursor, Len::rel_max());
if self.dir.sign == Sign::Neg { if self.dir.sign == Sign::Neg {
span.flip(); span.flip();
} }
@@ -33,9 +33,9 @@ impl Widget for Span {
.gap .gap
.mul_int(self.children.len().saturating_sub(1) as i32); .mul_int(self.children.len().saturating_sub(1) as i32);
let total = lens.iter().fold( let total = lens.iter().fold(
Len { LayoutLen {
px: gaps, px: gaps,
..Len::ZERO ..LayoutLen::ZERO
}, },
|sum, len| sum + *len, |sum, len| sum + *len,
); );
@@ -89,11 +89,11 @@ impl Widget for Span {
// from the last child: the share of the room is rounded, and taking // 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 // each from the one before it would carry every rounding along the
// row. // row.
let mut fixed = UiScalar::rel_min(); let mut fixed = Len::rel_min();
let mut taken = Weight::ZERO; let mut taken = Weight::ZERO;
let room = UiScalar::rel_max() - UiScalar::from_parts(total.rel, total.px); let room = Len::rel_max() - Len::from_parts(total.rel, total.px);
let mut start = UiScalar::rel_min(); let mut start = Len::rel_min();
let mut ortho = Len::ZERO; let mut ortho = LayoutLen::ZERO;
for (child, len) in self.children.iter().zip(&lens) { for (child, len) in self.children.iter().zip(&lens) {
// A child asking for nothing but a part of what is left over, // A child asking for nothing but a part of what is left over,
// when nothing is, is not drawn at all. One that also asked for // when nothing is, is not drawn at all. One that also asked for
@@ -125,7 +125,7 @@ impl Widget for Span {
// A scalable child therefore makes Children scalable too; // A scalable child therefore makes Children scalable too;
// only fixed children are compared with one another. // only fixed children are compared with one another.
if used.rel != Rel::ZERO || used.leftover != Weight::ZERO { if used.rel != Rel::ZERO || used.leftover != Weight::ZERO {
ortho = Len::LEFTOVER; ortho = LayoutLen::LEFTOVER;
} else if ortho.leftover == Weight::ZERO { } else if ortho.leftover == Weight::ZERO {
ortho.px = ortho.px.max(used.px); ortho.px = ortho.px.max(used.px);
} }
@@ -144,7 +144,7 @@ impl Widget for Span {
let along = total; let along = total;
let ortho = match shrinks { let ortho = match shrinks {
true => ortho, true => ortho,
false => Len::rel(1.0), false => LayoutLen::rel(1.0),
}; };
Size::from_axis(axis, along, ortho) Size::from_axis(axis, along, ortho)
} }
@@ -153,7 +153,7 @@ impl Widget for Span {
/// Where a row has reached: everything fixed before this point, which is a /// Where a row has reached: everything fixed before this point, which is a
/// sum and exact, plus the share of the room the weights so far are worth, /// sum and exact, plus the share of the room the weights so far are worth,
/// which is one rounding wherever it is asked for. /// which is one rounding wherever it is asked for.
fn shared(fixed: UiScalar, taken: Weight, weight: Weight, room: UiScalar) -> UiScalar { fn shared(fixed: Len, taken: Weight, weight: Weight, room: Len) -> Len {
if taken == Weight::ZERO { if taken == Weight::ZERO {
return fixed; return fixed;
} }
+2 -2
View File
@@ -38,8 +38,8 @@ impl Widget for Rect {
Size::LEFTOVER Size::LEFTOVER
} }
fn size_hint(&self, _: Axis) -> Option<Len> { fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
Some(Len::LEFTOVER) Some(LayoutLen::LEFTOVER)
} }
} }
+2 -2
View File
@@ -59,7 +59,7 @@ widget_trait! {
} }
} }
fn width(self, len: impl Into<Len>) -> impl WidgetIdFn<Rsc, WL::Widget> { fn width(self, len: impl Into<LayoutLen>) -> impl WidgetIdFn<Rsc, WL::Widget> {
let len = len.into(); let len = len.into();
move |state| { move |state| {
let id = self.add(state); let id = self.add(state);
@@ -71,7 +71,7 @@ widget_trait! {
} }
} }
fn height(self, len: impl Into<Len>) -> impl WidgetIdFn<Rsc, WL::Widget> { fn height(self, len: impl Into<LayoutLen>) -> impl WidgetIdFn<Rsc, WL::Widget> {
let len = len.into(); let len = len.into();
move |state| { move |state| {
let id = self.add(state); let id = self.add(state);
+5 -5
View File
@@ -27,7 +27,7 @@ fn a_span_ruled_across_itself_does_not_measure_its_children_there() {
let span = (child,).span(Dir::RIGHT).height(rel(1.0)).add(&mut h.rsc); let span = (child,).span(Dir::RIGHT).height(rel(1.0)).add(&mut h.rsc);
h.set_root(span); h.set_root(span);
assert_eq!(h.render.active[&span.id()].size.y, Len::rel(1.0)); assert_eq!(h.render.active[&span.id()].size.y, LayoutLen::rel(1.0));
} }
#[test] #[test]
@@ -38,7 +38,7 @@ fn a_span_reports_its_tallest_fixed_child() {
let span = (short, tall).span(Dir::RIGHT).add(&mut h.rsc); let span = (short, tall).span(Dir::RIGHT).add(&mut h.rsc);
h.set_root(span); h.set_root(span);
assert_eq!(h.render.active[&span.id()].size.y, Len::px(70.0)); assert_eq!(h.render.active[&span.id()].size.y, LayoutLen::px(70.0));
} }
#[test] #[test]
@@ -331,7 +331,7 @@ fn drawn_edges(h: &Harness, id: WidgetId, axis: Axis) -> (f32, f32) {
let region = h.render.moves.resolve(active.parent_move, active.region); let region = h.render.moves.resolve(active.parent_move, active.region);
let dim = h.size().axis(axis); let dim = h.size().axis(axis);
let snap = |v: f32| (v + Px::STEP.to_f32() * 0.5).floor(); let snap = |v: f32| (v + Px::STEP.to_f32() * 0.5).floor();
let edge = |s: UiScalar| snap(s.rel.to_f32() * dim) + snap(s.px.to_f32()); let edge = |s: Len| snap(s.rel.to_f32() * dim) + snap(s.px.to_f32());
let span = region.axis(axis); let span = region.axis(axis);
(edge(span.start), edge(span.end)) (edge(span.start), edge(span.end))
} }
@@ -343,7 +343,7 @@ fn hairline(h: &mut Harness, marks: &mut Vec<WidgetId>) -> StrongWidget {
} }
fn share(h: &mut Harness, inner: StrongWidget, ratio: f32) -> StrongWidget { fn share(h: &mut Harness, inner: StrongWidget, ratio: f32) -> StrongWidget {
h.set_len(&inner, Axis::X, Len::leftover(ratio)); h.set_len(&inner, Axis::X, LayoutLen::leftover(ratio));
inner inner
} }
@@ -454,7 +454,7 @@ fn only_a_pure_leftover_child_disappears_when_nothing_is_left() {
let mut h = Harness::new((100, 20)); let mut h = Harness::new((100, 20));
let fixed = rect(Color::RED).width(100).add(&mut h.rsc); let fixed = rect(Color::RED).width(100).add(&mut h.rsc);
let mixed = rect(Color::BLUE) let mixed = rect(Color::BLUE)
.width(Len::px(20) + Len::LEFTOVER) .width(LayoutLen::px(20) + LayoutLen::LEFTOVER)
.add(&mut h.rsc); .add(&mut h.rsc);
h.set_root((fixed, mixed).span(Dir::RIGHT)); h.set_root((fixed, mixed).span(Dir::RIGHT));
+4 -7
View File
@@ -125,10 +125,7 @@ fn moving_an_ordinary_subtree_remaps_its_mask() {
let active = &h.render.active[&masked.id()]; let active = &h.render.active[&masked.id()];
assert_eq!( assert_eq!(
h.rsc.ui().masks[active.mask.idx()].region, h.rsc.ui().masks[active.mask.idx()].region,
UiRegion::new( UiRegion::new(UiSpan::new(Len::px(150.0), Len::rel_max()), UiSpan::FULL,)
UiSpan::new(UiScalar::px(150.0), UiScalar::rel_max()),
UiSpan::FULL,
)
); );
assert_corners!(h, inner, (150, 0), (400, 200)); assert_corners!(h, inner, (150, 0), (400, 200));
} }
@@ -312,7 +309,7 @@ fn a_span_ruled_across_itself_moves_its_child_without_redrawing_it() {
assert_eq!(draws.get(), settled); assert_eq!(draws.get(), settled);
assert_corners!(h, leaf, (0, 0), (400, 100)); assert_corners!(h, leaf, (0, 0), (400, 100));
assert_eq!(h.render.active[&span.id()].size.y, Len::rel(1.0)); assert_eq!(h.render.active[&span.id()].size.y, LayoutLen::rel(1.0));
} }
/// The output is the root of the box chain, so a resize is a box that changed /// The output is the root of the box chain, so a resize is a box that changed
@@ -407,12 +404,12 @@ fn a_box_change_within_one_step_is_not_a_change() {
let step = Px::STEP.to_f32(); let step = Px::STEP.to_f32();
for part in [0.1, 0.2, 0.3] { for part in [0.1, 0.2, 0.3] {
h.rsc[first].size.x = Len::px(100.0 + step * part); h.rsc[first].size.x = LayoutLen::px(100.0 + step * part);
h.frame(); h.frame();
assert_eq!(draws.get(), settled); assert_eq!(draws.get(), settled);
} }
h.rsc[first].size.x = Len::px(100.0 + step); h.rsc[first].size.x = LayoutLen::px(100.0 + step);
h.frame(); h.frame();
assert_eq!(draws.get(), settled + 1); assert_eq!(draws.get(), settled + 1);
} }
+5 -5
View File
@@ -281,11 +281,11 @@ struct Wider {
impl Widget for Wider { impl Widget for Wider {
fn draw(&mut self, painter: &mut Painter) -> Size { fn draw(&mut self, painter: &mut Painter) -> Size {
Size { Size {
x: Len { x: LayoutLen {
px: painter.px_len(Axis::X) + Px::from_f32(self.extra), px: painter.px_len(Axis::X) + Px::from_f32(self.extra),
..Len::ZERO ..LayoutLen::ZERO
}, },
y: Len::LEFTOVER, y: LayoutLen::LEFTOVER,
} }
} }
} }
@@ -341,7 +341,7 @@ fn plant_boundary(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
let whole = rect(Color::RED).add(&mut h.rsc); let whole = rect(Color::RED).add(&mut h.rsc);
h.rsc h.rsc
.widgets_mut() .widgets_mut()
.set_size_rules(whole, None, Some(Len::rel(1.0))); .set_size_rules(whole, None, Some(LayoutLen::rel(1.0)));
let mut inner_children: Vec<StrongWidget> = vec![ let mut inner_children: Vec<StrongWidget> = vec![
measured.add_strong(&mut h.rsc), measured.add_strong(&mut h.rsc),
whole.add_strong(&mut h.rsc), whole.add_strong(&mut h.rsc),
@@ -357,7 +357,7 @@ fn plant_boundary(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
.add(&mut h.rsc); .add(&mut h.rsc);
h.rsc h.rsc
.widgets_mut() .widgets_mut()
.set_size_rules(inner, None, Some(Len::px(198.0))); .set_size_rules(inner, None, Some(LayoutLen::px(198.0)));
// One more span above it: without a box composed through it, both trees // One more span above it: without a box composed through it, both trees
// round the same way and the boundary is never crossed. // round the same way and the boundary is never crossed.
let outer = (inner,).span(Dir::DOWN).add(&mut h.rsc); let outer = (inner,).span(Dir::DOWN).add(&mut h.rsc);
+3 -3
View File
@@ -16,8 +16,8 @@
use iris::prelude::*; use iris::prelude::*;
use iris_core::{ use iris_core::{
MaskIdx, MoveIdx, PrimitiveInst, RectPrimitive, UiData, UiRegion, UiRenderNode, UiRenderState, Len, MaskIdx, MoveIdx, PrimitiveInst, RectPrimitive, UiData, UiRegion, UiRenderNode,
UiScalar, UiSpan, UiRenderState, UiSpan,
}; };
use wgpu::{Color as GpuColor, *}; use wgpu::{Color as GpuColor, *};
@@ -79,7 +79,7 @@ fn fill(ui: &mut UiData, render: &mut UiRenderState, depth: usize) {
slot = render.moves.push(slot, UiRegion::FULL); slot = render.moves.push(slot, UiRegion::FULL);
} }
let px = |v: f32| UiScalar::px(v); let px = |v: f32| Len::px(v);
for i in 0..INSTANCES { for i in 0..INSTANCES {
let x = (i % (SIZE as usize / 2)) as f32 * 2.0; let x = (i % (SIZE as usize / 2)) as f32 * 2.0;
let y = (i / (SIZE as usize / 2)) as f32; let y = (i / (SIZE as usize / 2)) as f32;
+2 -2
View File
@@ -61,8 +61,8 @@ fn plant(h: &mut Harness, seed: u64, edits: &Edits) -> Tree {
fn resize_one(h: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> Lens { fn resize_one(h: &mut Harness, tree: &Tree, idx: usize, rng: &mut Rng) -> Lens {
let lens = [ let lens = [
Some(Len::px(20.0 + rng.below(180) as f32)), Some(LayoutLen::px(20.0 + rng.below(180) as f32)),
Some(Len::px(20.0 + rng.below(180) as f32)), Some(LayoutLen::px(20.0 + rng.below(180) as f32)),
]; ];
h.rsc h.rsc
.widgets_mut() .widgets_mut()
+1 -1
View File
@@ -216,7 +216,7 @@ fn layout_cost() {
trace_selected(&tree); trace_selected(&tree);
let sized = tree.sized[0]; let sized = tree.sized[0];
run("size", frames, &mut harness, move |harness, frame| { run("size", frames, &mut harness, move |harness, frame| {
let len = Len::px(100.0 + (frame % 2) as f32 * 40.0); let len = LayoutLen::px(100.0 + (frame % 2) as f32 * 40.0);
harness harness
.rsc .rsc
.widgets_mut() .widgets_mut()
+5 -1
View File
@@ -94,7 +94,11 @@ fn build(h: &mut Harness, rows: usize) -> Vec<WidgetId> {
let mut col = Span::empty(Dir::DOWN); let mut col = Span::empty(Dir::DOWN);
for _ in 0..rows { for _ in 0..rows {
let mut row = Span::empty(Dir::RIGHT); let mut row = Span::empty(Dir::RIGHT);
row.push(rect(Color::RED).width(Len::px(40.0)).add_strong(&mut h.rsc)); row.push(
rect(Color::RED)
.width(LayoutLen::px(40.0))
.add_strong(&mut h.rsc),
);
let mut body = Span::empty(Dir::DOWN); let mut body = Span::empty(Dir::DOWN);
let para = wtext(words(&mut rng, 12, 52)) let para = wtext(words(&mut rng, 12, 52))
.size(16) .size(16)
+10 -10
View File
@@ -72,7 +72,7 @@ enum Node {
Stack(Vec<Node>), Stack(Vec<Node>),
Pad(f32, Box<Node>), Pad(f32, Box<Node>),
Aligned(u8, u8, Box<Node>), Aligned(u8, u8, Box<Node>),
Sized(Option<Len>, Option<Len>, Box<Node>), Sized(Option<LayoutLen>, Option<LayoutLen>, Box<Node>),
Scroll(bool, Box<Node>), Scroll(bool, Box<Node>),
Branch(Box<Node>, Box<Node>, Box<Node>, f32), Branch(Box<Node>, Box<Node>, Box<Node>, f32),
} }
@@ -131,7 +131,7 @@ impl Node {
if !*down { if !*down {
h.rsc h.rsc
.widgets_mut() .widgets_mut()
.set_size_rules(handle, None, Some(Len::rel(1.0))); .set_size_rules(handle, None, Some(LayoutLen::rel(1.0)));
} }
spans.push(handle); spans.push(handle);
handle.add_strong(&mut h.rsc) handle.add_strong(&mut h.rsc)
@@ -191,7 +191,7 @@ impl Node {
/// The lengths every `Sized` node would carry after `resized`, in the /// The lengths every `Sized` node would carry after `resized`, in the
/// order `build` pushes them. /// order `build` pushes them.
fn sized_lens(&self, out: &mut Vec<(Option<Len>, Option<Len>)>) { fn sized_lens(&self, out: &mut Vec<(Option<LayoutLen>, Option<LayoutLen>)>) {
match self { match self {
Node::Text(..) | Node::OneLine | Node::Rect => {} Node::Text(..) | Node::OneLine | Node::Rect => {}
Node::Span(_, _, kids, _) | Node::Stack(kids) => { Node::Span(_, _, kids, _) | Node::Stack(kids) => {
@@ -352,8 +352,8 @@ fn sized(rng: &mut Rng, inner: Node) -> Node {
return inner; return inner;
} }
let len = |rng: &mut Rng| match rng.below(4) { let len = |rng: &mut Rng| match rng.below(4) {
0 => Some(Len::px(20.0 + rng.below(180) as f32)), 0 => Some(LayoutLen::px(20.0 + rng.below(180) as f32)),
1 => Some(Len::LEFTOVER), 1 => Some(LayoutLen::LEFTOVER),
_ => None, _ => None,
}; };
Node::Sized(len(rng), len(rng), Box::new(inner)) Node::Sized(len(rng), len(rng), Box::new(inner))
@@ -368,9 +368,9 @@ fn grow(rng: &mut Rng, depth: usize) -> Node {
}; };
} }
let len = |rng: &mut Rng| match rng.below(4) { let len = |rng: &mut Rng| match rng.below(4) {
0 => Some(Len::px(20.0 + rng.below(180) as f32)), 0 => Some(LayoutLen::px(20.0 + rng.below(180) as f32)),
1 => Some(Len::LEFTOVER), 1 => Some(LayoutLen::LEFTOVER),
2 => Some(Len::rel(0.25 + rng.below(3) as f32 * 0.25)), 2 => Some(LayoutLen::rel(0.25 + rng.below(3) as f32 * 0.25)),
_ => None, _ => None,
}; };
let kid = |rng: &mut Rng| { let kid = |rng: &mut Rng| {
@@ -408,9 +408,9 @@ enum Case {
/// A different declared length, kept the same kind so the change is to the /// A different declared length, kept the same kind so the change is to the
/// value alone. /// value alone.
fn resized_len(len: Option<Len>) -> Option<Len> { fn resized_len(len: Option<LayoutLen>) -> Option<LayoutLen> {
let half = Rel::from_f32(0.5); let half = Rel::from_f32(0.5);
len.map(|len| Len { len.map(|len| LayoutLen {
px: len.px.mul(half) + Px::from_int(13), px: len.px.mul(half) + Px::from_int(13),
rel: len.rel.mul(half), rel: len.rel.mul(half),
leftover: len.leftover, leftover: len.leftover,