diff --git a/core/src/fixed.rs b/core/src/fixed.rs index 637fd61..15e3a9e 100644 --- a/core/src/fixed.rs +++ b/core/src/fixed.rs @@ -1,3 +1,4 @@ +use crate::UiNum; use std::{ fmt::{Debug, Display, Formatter}, ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}, @@ -28,6 +29,12 @@ pub struct Fixed(i32); /// the same number reach the GPU. pub type Px = Fixed<6>; +/// A share of what a box has left over, which is a weight beside its +/// siblings rather than a fraction of anything: a list divides its room by +/// the total of these, so the range has to hold a whole list's worth and the +/// precision only has to tell two weights apart. +pub type Weight = Fixed<16>; + /// A fraction of a box. Twenty-four bits of it, which matches `f32` around a /// half and beats it above one -- where anchors actually sit -- and leaves /// +/-128 of range, enough to sum a hundred children each asking for a whole @@ -86,6 +93,12 @@ impl Fixed { }) } + /// From a number as it is written in source -- `16`, `1.5` -- which is + /// the other place a value enters the grid. + pub fn from_num(v: impl UiNum) -> Self { + Self::from_f32(v.to_f32()) + } + pub const fn to_f32(self) -> f32 { self.0 as f32 / Self::one().0 as f32 } @@ -117,6 +130,11 @@ impl Fixed { Self(narrow(shift_round(self.0 as i64 * by.0 as i64, BY))) } + /// Repeated a whole number of times, which no grid rounds. + pub const fn mul_int(self, by: i32) -> Self { + Self(narrow(self.0 as i64 * by as i64)) + } + /// Divided by a number on any grid. A zero divisor is a caller bug -- a /// box of no length has no fraction of itself -- and saturates so that a /// release build lays out something absurd rather than dying. @@ -131,6 +149,16 @@ impl Fixed { Self(narrow(div_round((self.0 as i64) << BY, by.0 as i64))) } + /// `num / den` on *this* grid rather than on theirs, for weights coarser + /// than the share they divide. + pub const fn ratio(num: Fixed, den: Fixed) -> Self { + debug_assert!(den.0 != 0, "no part of a whole of nothing"); + if den.0 == 0 { + return Self::ZERO; + } + Self(narrow(div_round((num.0 as i64) << SHIFT, den.0 as i64))) + } + /// `from` and `to` a fraction of the way apart, the fraction being the /// receiver -- the argument order [`crate::util::LerpUtil`] already uses. pub const fn lerp(self, from: Fixed, to: Fixed) -> Fixed { @@ -347,6 +375,15 @@ mod tests { assert_eq!(Rel::from_f32(0.5).lerp(to, from), Px::from_int(15)); } + #[test] + fn a_ratio_is_finer_than_the_weights_it_divides() { + let (one, three) = (Weight::ONE, Weight::from_int(3)); + // A third, which the weights' own grid could only hold to 1/65536. + assert_eq!(Rel::ratio(one, three), Rel::from_raw(5592405)); + assert_eq!(Rel::ratio(three, three), Rel::ONE); + assert_eq!(Rel::ratio(Weight::ZERO, three), Rel::ZERO); + } + #[test] fn nothing_sits_between_a_value_and_the_next_one() { let at = Px::from_int(3); diff --git a/core/src/orientation/align.rs b/core/src/orientation/align.rs index e47d372..d2e873b 100644 --- a/core/src/orientation/align.rs +++ b/core/src/orientation/align.rs @@ -1,4 +1,4 @@ -use crate::{Px, Rel, vec2}; +use crate::{Px, Rel}; use super::*; @@ -35,7 +35,7 @@ impl Align { /// is the near one depends on the writing system and on which way a container /// runs, and the middle is the same either way. #[derive(Debug, Clone, Copy, PartialEq)] -pub struct AxisAlign(f32); +pub struct AxisAlign(Rel); impl AxisAlign { pub const NEG: Self = Self::new(0.0); @@ -43,10 +43,12 @@ impl AxisAlign { pub const POS: Self = Self::new(1.0); pub const fn new(rel: f32) -> Self { - Self(rel) + Self(Rel::from_f32(rel)) } - pub const fn rel(&self) -> f32 { + /// A fraction of the room left over, which is what the layout reads: the + /// three constants are the familiar places along it, not the only ones. + pub const fn rel(&self) -> Rel { self.0 } } @@ -118,9 +120,6 @@ impl RegionAlign { pub const fn new(x: AxisAlign, y: AxisAlign) -> Self { Self { x, y } } - pub const fn rel(&self) -> Vec2 { - vec2(self.x.rel(), self.y.rel()) - } } impl UiVec2 { @@ -175,7 +174,7 @@ impl Vec2 { impl UiScalar { pub const fn align(&self, align: AxisAlign) -> UiSpan { - let rel = Rel::from_f32(align.rel()); + let rel = align.rel(); let rest = Rel::ONE.sub(rel); let at = UiScalar::from_parts(rel, Px::ZERO); UiSpan { @@ -221,7 +220,10 @@ impl From for Align { const impl From for UiVec2 { fn from(align: RegionAlign) -> Self { - Self::rel(align.rel()) + Self::new( + UiScalar::from_parts(align.x.rel(), Px::ZERO), + UiScalar::from_parts(align.y.rel(), Px::ZERO), + ) } } diff --git a/core/src/orientation/len.rs b/core/src/orientation/len.rs index e77bbb0..36a68a8 100644 --- a/core/src/orientation/len.rs +++ b/core/src/orientation/len.rs @@ -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 From 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(()) diff --git a/core/src/orientation/pos.rs b/core/src/orientation/pos.rs index 8a97ad6..8a4a7ae 100644 --- a/core/src/orientation/pos.rs +++ b/core/src/orientation/pos.rs @@ -189,16 +189,15 @@ impl UiScalar { /// Both channels by the same factor, which is what a fraction of a /// length means when the length is part pixels and part a share. - pub const fn scale(&self, by: f32) -> Self { - let by = Rel::from_f32(by); + pub const fn scale(&self, by: Rel) -> Self { Self { rel: self.rel.mul(by), px: self.px.mul(by), } } - pub const fn offset(mut self, amt: f32) -> Self { - self.px = self.px.add(Px::from_f32(amt)); + pub const fn offset(mut self, amt: Px) -> Self { + self.px = self.px.add(amt); self } diff --git a/core/src/ui/painter.rs b/core/src/ui/painter.rs index fa7428b..69fd38e 100644 --- a/core/src/ui/painter.rs +++ b/core/src/ui/painter.rs @@ -1,8 +1,9 @@ #[cfg(feature = "layout-diagnostics")] use crate::layout_diagnostics::{self as diag, Counter}; use crate::{ - Axis, Holds, Len, Px, RegionAlign, RenderedText, Size, StrongWidget, TextAttrs, TextBuffer, - TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId, Widgets, + Axis, Holds, Len, Px, RegionAlign, Rel, RenderedText, Size, StrongWidget, TextAttrs, + TextBuffer, TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, Weight, + WidgetId, Widgets, render::{ GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst, PrimitiveKind, TexturePrimitive, @@ -484,7 +485,7 @@ pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option; 2] // occupies its reported size inside the box it was offered. widget .and_then(|widget| widget.size_hint(axis)) - .filter(|len| len.leftover == 0.0) + .filter(|len| len.leftover == Weight::ZERO) }) }) } @@ -509,11 +510,11 @@ pub(crate) fn placed_box( let mut placed = region; for (axis, declared) in AXES.into_iter().zip(declared) { let reported = size.axis(axis); - if reported.leftover != 0.0 || declared.is_some() { + if reported.leftover != Weight::ZERO || declared.is_some() { continue; } let span = placed.axis_mut(axis); - let len = span.len().scale(reported.rel) + UiScalar::px(reported.px); + let len = span.len().scale(reported.rel) + UiScalar::from_parts(Rel::ZERO, reported.px); span.start += (span.len() - len).scale(align.axis(axis).rel()); span.end = span.start + len; } @@ -532,7 +533,7 @@ pub(crate) fn declared_box( for (axis, len) in AXES.into_iter().zip(declared) { let Some(len) = len else { continue }; let span = region.axis_mut(axis); - let len = UiScalar::new(len.rel, len.px); + let len = UiScalar::from_parts(len.rel, len.px); span.start += (span.len() - len).scale(align.axis(axis).rel()); span.end = span.start + len; } diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 7432951..afde7a5 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -3,8 +3,8 @@ use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind}; use crate::ui::painter::{declared_box, declared_lens, placed_box}; use crate::{ ActiveData, Axis, DrawLayers, Holds, IdLike, Len, MaskIdx, MoveIdx, Moves, Painter, - PixelRegion, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, WidgetId, - Widgets, + PixelRegion, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, Weight, + WidgetId, Widgets, util::{HashMap, Vec2}, }; @@ -245,10 +245,11 @@ impl UiRenderState { let mut settled = answer; for axis in AXES { let reported = answer.0.axis(axis); - let placed_len = match reported.leftover != 0.0 || declared[axis as usize].is_some() { - true => UiScalar::FULL, - false => UiScalar::new(reported.rel, reported.px), - }; + let placed_len = + match reported.leftover != Weight::ZERO || declared[axis as usize].is_some() { + true => UiScalar::FULL, + false => UiScalar::from_parts(reported.rel, reported.px), + }; settled.1[axis as usize] = settled.1[axis as usize].and(drawing_holds[axis as usize].through(placed_len)); } diff --git a/core/src/widget/size_rule.rs b/core/src/widget/size_rule.rs index 5550913..4cae504 100644 --- a/core/src/widget/size_rule.rs +++ b/core/src/widget/size_rule.rs @@ -1,4 +1,4 @@ -use crate::{Axis, Len}; +use crate::{Axis, Len, Weight}; /// 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. @@ -24,7 +24,7 @@ impl SizeRule { /// is resolved there. pub fn declared(&self) -> Option { match self { - Self::Exact(len) if len.leftover == 0.0 => Some(*len), + Self::Exact(len) if len.leftover == Weight::ZERO => Some(*len), _ => None, } } diff --git a/src/random.rs b/src/random.rs index e12a5b5..a1895dc 100644 --- a/src/random.rs +++ b/src/random.rs @@ -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 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 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 { diff --git a/src/widget/position/pad.rs b/src/widget/position/pad.rs index 36eb3f6..1a62d57 100644 --- a/src/widget/position/pad.rs +++ b/src/widget/position/pad.rs @@ -24,22 +24,22 @@ impl Widget for Pad { } pub struct Padding { - pub left: f32, - pub right: f32, - pub top: f32, - pub bottom: f32, + pub left: Px, + pub right: Px, + pub top: Px, + pub bottom: Px, } impl Padding { pub const ZERO: Self = Self { - left: 0.0, - right: 0.0, - top: 0.0, - bottom: 0.0, + left: Px::ZERO, + right: Px::ZERO, + top: Px::ZERO, + bottom: Px::ZERO, }; pub fn uniform(amt: impl UiNum) -> Self { - let amt = amt.to_f32(); + let amt = Px::from_num(amt); Self { left: amt, right: amt, @@ -49,78 +49,76 @@ impl Padding { } pub fn region(&self) -> UiRegion { let mut region = UiRegion::FULL; - region.x.start.px += Px::from_f32(self.left); - region.y.start.px += Px::from_f32(self.top); - region.x.end.px -= Px::from_f32(self.right); - region.y.end.px -= Px::from_f32(self.bottom); + region.x.start.px += self.left; + region.y.start.px += self.top; + region.x.end.px -= self.right; + region.y.end.px -= self.bottom; region } pub fn x(amt: impl UiNum) -> Self { - let amt = amt.to_f32(); + let amt = Px::from_num(amt); Self { left: amt, right: amt, - top: 0.0, - bottom: 0.0, + ..Self::ZERO } } pub fn y(amt: impl UiNum) -> Self { - let amt = amt.to_f32(); + let amt = Px::from_num(amt); Self { - left: 0.0, - right: 0.0, top: amt, bottom: amt, + ..Self::ZERO } } pub fn top(amt: impl UiNum) -> Self { let mut s = Self::ZERO; - s.top = amt.to_f32(); + s.top = Px::from_num(amt); s } pub fn bottom(amt: impl UiNum) -> Self { let mut s = Self::ZERO; - s.bottom = amt.to_f32(); + s.bottom = Px::from_num(amt); s } pub fn left(amt: impl UiNum) -> Self { let mut s = Self::ZERO; - s.left = amt.to_f32(); + s.left = Px::from_num(amt); s } pub fn right(amt: impl UiNum) -> Self { let mut s = Self::ZERO; - s.right = amt.to_f32(); + s.right = Px::from_num(amt); s } pub fn with_top(mut self, amt: impl UiNum) -> Self { - self.top = amt.to_f32(); + self.top = Px::from_num(amt); self } pub fn with_bottom(mut self, amt: impl UiNum) -> Self { - self.bottom = amt.to_f32(); + self.bottom = Px::from_num(amt); self } pub fn with_left(mut self, amt: impl UiNum) -> Self { - self.left = amt.to_f32(); + self.left = Px::from_num(amt); self } pub fn with_right(mut self, amt: impl UiNum) -> Self { - self.right = amt.to_f32(); + self.right = Px::from_num(amt); self } } impl From for Padding { fn from(amt: T) -> Self { - Self::uniform(amt.to_f32()) + Self::uniform(amt) } } diff --git a/src/widget/position/scroll.rs b/src/widget/position/scroll.rs index dd57e0e..9f06180 100644 --- a/src/widget/position/scroll.rs +++ b/src/widget/position/scroll.rs @@ -45,9 +45,12 @@ impl Widget for Scroll { // sits is this widget's own alignment -- the same property that would // have placed the whole scroll in a box longer than it. let slack = (self.container_len - self.content_len).max(0.0); - let anchor = slack * align.rel(); + let anchor = slack * align.rel().to_f32(); let mut region = UiRegion::FULL.offset(Vec2::from_axis(self.axis, anchor - self.amt, 0.0)); - 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(Px::from_f32(self.content_len)); painter.widget_aligned(&self.inner, region, RegionAlign::NEAR); // What it occupies is its box, on both axes: it clips its content to // that box, so it can neither take less of one nor honestly ask for diff --git a/src/widget/position/span.rs b/src/widget/position/span.rs index b5ffddc..8e51fcf 100644 --- a/src/widget/position/span.rs +++ b/src/widget/position/span.rs @@ -14,7 +14,7 @@ pub enum OrthoSize { pub struct Span { pub children: Vec, pub dir: Dir, - pub gap: f32, + pub gap: Px, pub ortho: OrthoSize, } @@ -35,14 +35,21 @@ impl Widget for Span { Some(len) => len, None => painter.widget_within(child, region).len(axis), }; - // Onto the grid at the seam: `Len` is still in floats. - cursor.px += Px::from_f32(len.px + self.gap); - cursor.rel += Rel::from_f32(len.rel); + cursor.px += len.px + self.gap; + cursor.rel += len.rel; lens.push(len); } - let gap = self.gap * self.children.len().saturating_sub(1) as f32; - let total = lens.iter().fold(Len::px(gap), |sum, len| sum + *len); + let gaps = self + .gap + .mul_int(self.children.len().saturating_sub(1) as i32); + let total = lens.iter().fold( + Len { + px: gaps, + ..Len::ZERO + }, + |sum, len| sum + *len, + ); // Whether anything is left over is a question in pixels: `rel(0.5)` // beside 300 px is full at 600 and overfull at 400. The room to divide @@ -53,31 +60,32 @@ impl Widget for Span { // 0.00003 px of rounding decides whether a leftover-only child is // drawn at all. The validity range is split at the moved boundary, and // exact there, since no box lands on it any more. - let fixed = 1.0 - total.rel; + let fixed = Rel::ONE - total.rel; + let margin = Px::from_f32(HOLDS_EPSILON_PX); let mut shares = false; - if total.leftover > 0.0 { - let current = painter.px_len(axis); - let holds = if fixed > 0.0 { + if total.leftover > Weight::ZERO { + let current = Px::from_f32(painter.px_len(axis)); + let holds = if fixed > Rel::ZERO { // The box length at which the room reaches the margin. - let enough = (total.px + HOLDS_EPSILON_PX) / fixed; + let enough = (total.px + margin).div(fixed); shares = current > enough; match shares { - true => Holds::exact(enough.next_up()..=f32::INFINITY), - false => Holds::exact(f32::NEG_INFINITY..=enough), + true => Holds::exact(enough.to_f32().next_up()..=f32::INFINITY), + false => Holds::exact(f32::NEG_INFINITY..=enough.to_f32()), } - } else if fixed < 0.0 { + } else if fixed < Rel::ZERO { // The relative parts grow faster than the box does, so here // a shorter box is the one that leaves room. - let enough = (total.px + HOLDS_EPSILON_PX) / fixed; + let enough = (total.px + margin).div(fixed); shares = current < enough; match shares { - true => Holds::exact(f32::NEG_INFINITY..=enough.next_down()), - false => Holds::exact(enough..=f32::INFINITY), + true => Holds::exact(f32::NEG_INFINITY..=enough.to_f32().next_down()), + false => Holds::exact(enough.to_f32()..=f32::INFINITY), } } else { // The relative parts take exactly the box, whatever it is, so // the only room is what negative pixels leave. - shares = total.px < -HOLDS_EPSILON_PX; + shares = total.px < margin.neg(); Holds::ANY }; painter.holds(axis, holds); @@ -89,21 +97,23 @@ impl Widget for Span { // 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 // pixels or a fraction keeps those and overflows. - if len.leftover > 0.0 && len.px == 0.0 && len.rel == 0.0 && !shares { + if len.leftover > Weight::ZERO && len.px == Px::ZERO && len.rel == Rel::ZERO && !shares + { painter.undraw(child); - start.px += Px::from_f32(self.gap); + start.px += self.gap; continue; } let mut span = UiSpan::FULL; span.start = start; - if len.leftover > 0.0 && shares { - let offset = UiScalar::new(total.rel, total.px); - let rel_end = UiScalar::rel(len.leftover / total.leftover); + if len.leftover > Weight::ZERO && shares { + let offset = UiScalar::from_parts(total.rel, total.px); + let share = Rel::ratio(len.leftover, total.leftover); + let rel_end = UiScalar::from_parts(share, Px::ZERO); let end = (UiScalar::rel_max() + start) - offset; start = rel_end.within(&start.to(end)); } - start.px += Px::from_f32(len.px); - start.rel += Rel::from_f32(len.rel); + start.px += len.px; + start.rel += len.rel; span.end = start; let mut region = UiRegion::from_axis(axis, span, UiSpan::FULL); if self.dir.sign == Sign::Neg { @@ -116,13 +126,13 @@ impl Widget for Span { // span's own eventual width admits multiple fixed points. // A scalable child therefore makes Children scalable too; // only fixed children are compared with one another. - if used.rel != 0.0 || used.leftover != 0.0 { + if used.rel != Rel::ZERO || used.leftover != Weight::ZERO { ortho = Len::LEFTOVER; - } else if ortho.leftover == 0.0 { + } else if ortho.leftover == Weight::ZERO { ortho.px = ortho.px.max(used.px); } } - start.px += Px::from_f32(self.gap); + start.px += self.gap; } // Carried whole rather than collapsed to one share: a span that sizes @@ -146,13 +156,13 @@ impl Span { Self { children: Vec::new(), dir, - gap: 0.0, + gap: Px::ZERO, ortho: OrthoSize::Children, } } pub fn gap(mut self, gap: impl UiNum) -> Self { - self.gap = gap.to_f32(); + self.gap = Px::from_num(gap); self } @@ -173,7 +183,7 @@ impl Span { pub struct SpanBuilder, Tag> { pub children: Wa, pub dir: Dir, - pub gap: f32, + pub gap: Px, pub ortho: OrthoSize, _pd: PhantomData<(State, Tag)>, } @@ -201,14 +211,14 @@ impl, Tag> Self { children, dir, - gap: 0.0, + gap: Px::ZERO, ortho: OrthoSize::Children, _pd: PhantomData, } } pub fn gap(mut self, gap: impl UiNum) -> Self { - self.gap = gap.to_f32(); + self.gap = Px::from_num(gap); self } diff --git a/tests/determinism.rs b/tests/determinism.rs index b18e96c..b2d6b9a 100644 --- a/tests/determinism.rs +++ b/tests/determinism.rs @@ -22,7 +22,7 @@ struct BranchesOnMeasurement { impl Widget for BranchesOnMeasurement { 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() @@ -30,7 +30,7 @@ impl Widget for BranchesOnMeasurement { .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), diff --git a/tests/layout.rs b/tests/layout.rs index 2640625..2c616e6 100644 --- a/tests/layout.rs +++ b/tests/layout.rs @@ -340,10 +340,10 @@ fn hairlines(h: &mut Harness, depth: usize, marks: &mut Vec) -> Strong let second = hairlines(h, depth - 1, marks); let second = Pad { padding: Padding { - left: 3.0, - right: 7.0, - top: 0.0, - bottom: 0.0, + left: Px::from_int(3), + right: Px::from_int(7), + top: Px::ZERO, + bottom: Px::ZERO, }, inner: second, } diff --git a/tests/shrink.rs b/tests/shrink.rs index a16e901..5eb6612 100644 --- a/tests/shrink.rs +++ b/tests/shrink.rs @@ -119,7 +119,7 @@ impl Node { let handle = Span { children, dir: dir(*down), - gap: *gap, + gap: Px::from_f32(*gap), ortho: match down { true => OrthoSize::Children, false => OrthoSize::Full, @@ -140,12 +140,7 @@ impl Node { Node::Pad(p, kid) => { let inner = kid.build(h, out, spans, sized); Pad { - padding: Padding { - left: *p, - right: *p, - top: *p, - bottom: *p, - }, + padding: Padding::uniform(*p), inner, } .add_strong(&mut h.rsc) @@ -407,9 +402,10 @@ enum Case { /// A different declared length, kept the same kind so the change is to the /// value alone. fn resized_len(len: Option) -> Option { + let half = Rel::from_f32(0.5); len.map(|len| Len { - px: len.px * 0.5 + 13.0, - rel: len.rel * 0.5, + px: len.px.mul(half) + Px::from_int(13), + rel: len.rel.mul(half), leftover: len.leftover, }) } diff --git a/tests/unsettled.rs b/tests/unsettled.rs index bc28049..98536ae 100644 --- a/tests/unsettled.rs +++ b/tests/unsettled.rs @@ -159,7 +159,7 @@ fn plant_pair(h: &mut Harness, swapped: bool) -> (Vec, WeakWidget (Vec, [WeakWidget let inner = Span { children: inner_children, dir: Dir::RIGHT, - gap: 0.0, + gap: Px::ZERO, ortho: OrthoSize::Children, } .add(&mut h.rsc); @@ -227,7 +227,7 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec, [WeakWidget let outer = Span { children: outer_children, dir: Dir::RIGHT, - gap: 0.0, + gap: Px::ZERO, ortho: OrthoSize::Children, } .add(&mut h.rsc); @@ -333,7 +333,7 @@ fn plant_boundary(h: &mut Harness, swapped: bool) -> (Vec, [WeakWidget let measured = Span { children: pair, dir: Dir::DOWN, - gap: 0.0, + gap: Px::ZERO, ortho: OrthoSize::Children, } .add(&mut h.rsc); @@ -353,7 +353,7 @@ fn plant_boundary(h: &mut Harness, swapped: bool) -> (Vec, [WeakWidget let inner = Span { children: inner_children, dir: Dir::DOWN, - gap: 0.0, + gap: Px::ZERO, ortho: OrthoSize::Children, } .add(&mut h.rsc);