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:
1 parent
4e28f1047e
commit
bd6de71a55
15 files changed
+199
-158
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::UiNum;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign},
|
ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign},
|
||||||
@@ -28,6 +29,12 @@ pub struct Fixed<const SHIFT: u32>(i32);
|
|||||||
/// the same number reach the GPU.
|
/// the same number reach the GPU.
|
||||||
pub type Px = Fixed<6>;
|
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
|
/// 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
|
/// 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
|
/// +/-128 of range, enough to sum a hundred children each asking for a whole
|
||||||
@@ -86,6 +93,12 @@ impl<const SHIFT: u32> Fixed<SHIFT> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
pub const fn to_f32(self) -> f32 {
|
||||||
self.0 as f32 / Self::one().0 as f32
|
self.0 as f32 / Self::one().0 as f32
|
||||||
}
|
}
|
||||||
@@ -117,6 +130,11 @@ impl<const SHIFT: u32> Fixed<SHIFT> {
|
|||||||
Self(narrow(shift_round(self.0 as i64 * by.0 as i64, BY)))
|
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
|
/// 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
|
/// box of no length has no fraction of itself -- and saturates so that a
|
||||||
/// release build lays out something absurd rather than dying.
|
/// release build lays out something absurd rather than dying.
|
||||||
@@ -131,6 +149,16 @@ impl<const SHIFT: u32> Fixed<SHIFT> {
|
|||||||
Self(narrow(div_round((self.0 as i64) << BY, by.0 as i64)))
|
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<const OF: u32>(num: Fixed<OF>, den: Fixed<OF>) -> 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
|
/// `from` and `to` a fraction of the way apart, the fraction being the
|
||||||
/// receiver -- the argument order [`crate::util::LerpUtil`] already uses.
|
/// receiver -- the argument order [`crate::util::LerpUtil`] already uses.
|
||||||
pub const fn lerp<const OF: u32>(self, from: Fixed<OF>, to: Fixed<OF>) -> Fixed<OF> {
|
pub const fn lerp<const OF: u32>(self, from: Fixed<OF>, to: Fixed<OF>) -> Fixed<OF> {
|
||||||
@@ -347,6 +375,15 @@ mod tests {
|
|||||||
assert_eq!(Rel::from_f32(0.5).lerp(to, from), Px::from_int(15));
|
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]
|
#[test]
|
||||||
fn nothing_sits_between_a_value_and_the_next_one() {
|
fn nothing_sits_between_a_value_and_the_next_one() {
|
||||||
let at = Px::from_int(3);
|
let at = Px::from_int(3);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Px, Rel, vec2};
|
use crate::{Px, Rel};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ impl Align {
|
|||||||
/// is the near one depends on the writing system and on which way a container
|
/// is the near one depends on the writing system and on which way a container
|
||||||
/// runs, and the middle is the same either way.
|
/// runs, and the middle is the same either way.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct AxisAlign(f32);
|
pub struct AxisAlign(Rel);
|
||||||
|
|
||||||
impl AxisAlign {
|
impl AxisAlign {
|
||||||
pub const NEG: Self = Self::new(0.0);
|
pub const NEG: Self = Self::new(0.0);
|
||||||
@@ -43,10 +43,12 @@ impl AxisAlign {
|
|||||||
pub const POS: Self = Self::new(1.0);
|
pub const POS: Self = Self::new(1.0);
|
||||||
|
|
||||||
pub const fn new(rel: f32) -> Self {
|
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
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,9 +120,6 @@ impl RegionAlign {
|
|||||||
pub const fn new(x: AxisAlign, y: AxisAlign) -> Self {
|
pub const fn new(x: AxisAlign, y: AxisAlign) -> Self {
|
||||||
Self { x, y }
|
Self { x, y }
|
||||||
}
|
}
|
||||||
pub const fn rel(&self) -> Vec2 {
|
|
||||||
vec2(self.x.rel(), self.y.rel())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiVec2 {
|
impl UiVec2 {
|
||||||
@@ -175,7 +174,7 @@ impl Vec2 {
|
|||||||
|
|
||||||
impl UiScalar {
|
impl UiScalar {
|
||||||
pub const fn align(&self, align: AxisAlign) -> UiSpan {
|
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 rest = Rel::ONE.sub(rel);
|
||||||
let at = UiScalar::from_parts(rel, Px::ZERO);
|
let at = UiScalar::from_parts(rel, Px::ZERO);
|
||||||
UiSpan {
|
UiSpan {
|
||||||
@@ -221,7 +220,10 @@ 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::rel(align.rel())
|
Self::new(
|
||||||
|
UiScalar::from_parts(align.x.rel(), Px::ZERO),
|
||||||
|
UiScalar::from_parts(align.y.rel(), Px::ZERO),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+38
-44
@@ -1,5 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{UiNum, util::impl_op};
|
use crate::{Px, Rel, UiNum, Weight, util::impl_op};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
@@ -7,11 +7,14 @@ pub struct Size {
|
|||||||
pub y: Len,
|
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 struct Len {
|
||||||
pub px: f32,
|
pub px: Px,
|
||||||
pub rel: f32,
|
pub rel: Rel,
|
||||||
pub leftover: f32,
|
pub leftover: Weight,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: UiNum> From<N> for Len {
|
impl<N: UiNum> From<N> for Len {
|
||||||
@@ -97,41 +100,44 @@ impl Size {
|
|||||||
|
|
||||||
impl Len {
|
impl Len {
|
||||||
pub const ZERO: Self = Self {
|
pub const ZERO: Self = Self {
|
||||||
px: 0.0,
|
px: Px::ZERO,
|
||||||
rel: 0.0,
|
rel: Rel::ZERO,
|
||||||
leftover: 0.0,
|
leftover: Weight::ZERO,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const LEFTOVER: Self = Self {
|
pub const LEFTOVER: Self = Self {
|
||||||
px: 0.0,
|
px: Px::ZERO,
|
||||||
rel: 0.0,
|
rel: Rel::ZERO,
|
||||||
leftover: 1.0,
|
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 {
|
pub fn apply_leftover(&self) -> UiScalar {
|
||||||
let share = if self.leftover > 0.0 { 1.0 } else { 0.0 };
|
let share = match self.leftover > Weight::ZERO {
|
||||||
UiScalar::new(self.rel + share, self.px)
|
true => Rel::ONE,
|
||||||
|
false => Rel::ZERO,
|
||||||
|
};
|
||||||
|
UiScalar::from_parts(self.rel.add(share), self.px)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn px(px: impl UiNum) -> Self {
|
pub fn px(px: impl UiNum) -> Self {
|
||||||
Self {
|
Self {
|
||||||
px: px.to_f32(),
|
px: Px::from_num(px),
|
||||||
rel: 0.0,
|
..Self::ZERO
|
||||||
leftover: 0.0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn rel(rel: impl UiNum) -> Self {
|
pub fn rel(rel: impl UiNum) -> Self {
|
||||||
Self {
|
Self {
|
||||||
px: 0.0,
|
rel: Rel::from_num(rel),
|
||||||
rel: rel.to_f32(),
|
..Self::ZERO
|
||||||
leftover: 0.0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn leftover(ratio: impl UiNum) -> Self {
|
pub fn leftover(ratio: impl UiNum) -> Self {
|
||||||
Self {
|
Self {
|
||||||
px: 0.0,
|
leftover: Weight::from_num(ratio),
|
||||||
rel: 0.0,
|
..Self::ZERO
|
||||||
leftover: ratio.to_f32(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,33 +146,21 @@ pub mod len_fns {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn px(px: impl UiNum) -> Len {
|
pub fn px(px: impl UiNum) -> Len {
|
||||||
Len {
|
Len::px(px)
|
||||||
px: px.to_f32(),
|
|
||||||
rel: 0.0,
|
|
||||||
leftover: 0.0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn rel(rel: impl UiNum) -> Len {
|
pub fn rel(rel: impl UiNum) -> Len {
|
||||||
Len {
|
Len::rel(rel)
|
||||||
px: 0.0,
|
|
||||||
rel: rel.to_f32(),
|
|
||||||
leftover: 0.0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn leftover(ratio: impl UiNum) -> Len {
|
pub fn leftover(ratio: impl UiNum) -> Len {
|
||||||
Len {
|
Len::leftover(ratio)
|
||||||
px: 0.0,
|
|
||||||
rel: 0.0,
|
|
||||||
leftover: ratio.to_f32(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_op!(Len Add add; px rel leftover);
|
impl_op!(same Len Add add; px rel leftover);
|
||||||
impl_op!(Len Sub sub; px rel leftover);
|
impl_op!(same Len Sub sub; px rel leftover);
|
||||||
|
|
||||||
impl_op!(Size Add add; x y);
|
impl_op!(same Size Add add; x y);
|
||||||
impl_op!(Size Sub sub; x y);
|
impl_op!(same Size Sub sub; x y);
|
||||||
|
|
||||||
impl Default for Len {
|
impl Default for Len {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@@ -182,13 +176,13 @@ impl std::fmt::Display for Size {
|
|||||||
|
|
||||||
impl std::fmt::Display for Len {
|
impl std::fmt::Display for Len {
|
||||||
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 != 0.0 {
|
if self.px != Px::ZERO {
|
||||||
write!(f, "{} px;", self.px)?;
|
write!(f, "{} px;", self.px)?;
|
||||||
}
|
}
|
||||||
if self.rel != 0.0 {
|
if self.rel != Rel::ZERO {
|
||||||
write!(f, "{} rel;", self.rel)?;
|
write!(f, "{} rel;", self.rel)?;
|
||||||
}
|
}
|
||||||
if self.leftover != 0.0 {
|
if self.leftover != Weight::ZERO {
|
||||||
write!(f, "{} leftover;", self.leftover)?;
|
write!(f, "{} leftover;", self.leftover)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -189,16 +189,15 @@ impl UiScalar {
|
|||||||
|
|
||||||
/// Both channels by the same factor, which is what a fraction of a
|
/// 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.
|
/// length means when the length is part pixels and part a share.
|
||||||
pub const fn scale(&self, by: f32) -> Self {
|
pub const fn scale(&self, by: Rel) -> Self {
|
||||||
let by = Rel::from_f32(by);
|
|
||||||
Self {
|
Self {
|
||||||
rel: self.rel.mul(by),
|
rel: self.rel.mul(by),
|
||||||
px: self.px.mul(by),
|
px: self.px.mul(by),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn offset(mut self, amt: f32) -> Self {
|
pub const fn offset(mut self, amt: Px) -> Self {
|
||||||
self.px = self.px.add(Px::from_f32(amt));
|
self.px = self.px.add(amt);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#[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, RegionAlign, RenderedText, Size, StrongWidget, TextAttrs, TextBuffer,
|
Axis, Holds, Len, Px, RegionAlign, Rel, RenderedText, Size, StrongWidget, TextAttrs,
|
||||||
TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId, Widgets,
|
TextBuffer, TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, Weight,
|
||||||
|
WidgetId, Widgets,
|
||||||
render::{
|
render::{
|
||||||
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst,
|
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst,
|
||||||
PrimitiveKind, TexturePrimitive,
|
PrimitiveKind, TexturePrimitive,
|
||||||
@@ -484,7 +485,7 @@ pub(crate) fn declared_lens(widgets: &Widgets, id: WidgetId) -> [Option<Len>; 2]
|
|||||||
// occupies its reported size inside the box it was offered.
|
// occupies its reported size inside the box it was offered.
|
||||||
widget
|
widget
|
||||||
.and_then(|widget| widget.size_hint(axis))
|
.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;
|
let mut placed = region;
|
||||||
for (axis, declared) in AXES.into_iter().zip(declared) {
|
for (axis, declared) in AXES.into_iter().zip(declared) {
|
||||||
let reported = size.axis(axis);
|
let reported = size.axis(axis);
|
||||||
if reported.leftover != 0.0 || declared.is_some() {
|
if reported.leftover != Weight::ZERO || declared.is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let span = placed.axis_mut(axis);
|
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.start += (span.len() - len).scale(align.axis(axis).rel());
|
||||||
span.end = span.start + len;
|
span.end = span.start + len;
|
||||||
}
|
}
|
||||||
@@ -532,7 +533,7 @@ pub(crate) fn declared_box(
|
|||||||
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::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.start += (span.len() - len).scale(align.axis(axis).rel());
|
||||||
span.end = span.start + len;
|
span.end = span.start + len;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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::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, Len, MaskIdx, MoveIdx, Moves, Painter,
|
||||||
PixelRegion, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, WidgetId,
|
PixelRegion, RegionAlign, Rel, Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, Weight,
|
||||||
Widgets,
|
WidgetId, Widgets,
|
||||||
util::{HashMap, Vec2},
|
util::{HashMap, Vec2},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,10 +245,11 @@ impl UiRenderState {
|
|||||||
let mut settled = answer;
|
let mut settled = answer;
|
||||||
for axis in AXES {
|
for axis in AXES {
|
||||||
let reported = answer.0.axis(axis);
|
let reported = answer.0.axis(axis);
|
||||||
let placed_len = match reported.leftover != 0.0 || declared[axis as usize].is_some() {
|
let placed_len =
|
||||||
true => UiScalar::FULL,
|
match reported.leftover != Weight::ZERO || declared[axis as usize].is_some() {
|
||||||
false => UiScalar::new(reported.rel, reported.px),
|
true => UiScalar::FULL,
|
||||||
};
|
false => UiScalar::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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
/// 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.
|
||||||
@@ -24,7 +24,7 @@ impl SizeRule {
|
|||||||
/// is resolved there.
|
/// is resolved there.
|
||||||
pub fn declared(&self) -> Option<Len> {
|
pub fn declared(&self) -> Option<Len> {
|
||||||
match self {
|
match self {
|
||||||
Self::Exact(len) if len.leftover == 0.0 => Some(*len),
|
Self::Exact(len) if len.leftover == Weight::ZERO => Some(*len),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -111,7 +111,7 @@ pub struct Branch {
|
|||||||
impl Widget for Branch {
|
impl Widget for Branch {
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||||
let mut top = UiRegion::FULL;
|
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 measured = painter.widget_within(&self.probe, top).len(Axis::X);
|
||||||
let px = measured
|
let px = measured
|
||||||
.apply_leftover()
|
.apply_leftover()
|
||||||
@@ -119,7 +119,7 @@ impl Widget for Branch {
|
|||||||
.to_f32();
|
.to_f32();
|
||||||
|
|
||||||
let mut below = UiRegion::FULL;
|
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 {
|
match px > self.threshold {
|
||||||
true => painter.widget_within(&self.wide, below),
|
true => painter.widget_within(&self.wide, below),
|
||||||
false => painter.widget_within(&self.narrow, below),
|
false => painter.widget_within(&self.narrow, below),
|
||||||
@@ -312,7 +312,7 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
|
|||||||
let inner = self.noded(inner);
|
let inner = self.noded(inner);
|
||||||
// Each side its own, since a padding that is the same all round
|
// Each side its own, since a padding that is the same all round
|
||||||
// hides anything that treats one edge differently from another.
|
// 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 {
|
let padding = Padding {
|
||||||
left: side(),
|
left: side(),
|
||||||
right: side(),
|
right: side(),
|
||||||
@@ -360,7 +360,7 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
|
|||||||
let id = Span {
|
let id = Span {
|
||||||
children,
|
children,
|
||||||
dir,
|
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
|
// Derive this from an existing choice: a seed must keep growing
|
||||||
// the same tree when the generator gains another configuration.
|
// the same tree when the generator gains another configuration.
|
||||||
ortho: match dir.axis {
|
ortho: match dir.axis {
|
||||||
|
|||||||
+26
-28
@@ -24,22 +24,22 @@ impl Widget for Pad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Padding {
|
pub struct Padding {
|
||||||
pub left: f32,
|
pub left: Px,
|
||||||
pub right: f32,
|
pub right: Px,
|
||||||
pub top: f32,
|
pub top: Px,
|
||||||
pub bottom: f32,
|
pub bottom: Px,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Padding {
|
impl Padding {
|
||||||
pub const ZERO: Self = Self {
|
pub const ZERO: Self = Self {
|
||||||
left: 0.0,
|
left: Px::ZERO,
|
||||||
right: 0.0,
|
right: Px::ZERO,
|
||||||
top: 0.0,
|
top: Px::ZERO,
|
||||||
bottom: 0.0,
|
bottom: Px::ZERO,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn uniform(amt: impl UiNum) -> Self {
|
pub fn uniform(amt: impl UiNum) -> Self {
|
||||||
let amt = amt.to_f32();
|
let amt = Px::from_num(amt);
|
||||||
Self {
|
Self {
|
||||||
left: amt,
|
left: amt,
|
||||||
right: amt,
|
right: amt,
|
||||||
@@ -49,78 +49,76 @@ impl Padding {
|
|||||||
}
|
}
|
||||||
pub fn region(&self) -> UiRegion {
|
pub fn region(&self) -> UiRegion {
|
||||||
let mut region = UiRegion::FULL;
|
let mut region = UiRegion::FULL;
|
||||||
region.x.start.px += Px::from_f32(self.left);
|
region.x.start.px += self.left;
|
||||||
region.y.start.px += Px::from_f32(self.top);
|
region.y.start.px += self.top;
|
||||||
region.x.end.px -= Px::from_f32(self.right);
|
region.x.end.px -= self.right;
|
||||||
region.y.end.px -= Px::from_f32(self.bottom);
|
region.y.end.px -= self.bottom;
|
||||||
region
|
region
|
||||||
}
|
}
|
||||||
pub fn x(amt: impl UiNum) -> Self {
|
pub fn x(amt: impl UiNum) -> Self {
|
||||||
let amt = amt.to_f32();
|
let amt = Px::from_num(amt);
|
||||||
Self {
|
Self {
|
||||||
left: amt,
|
left: amt,
|
||||||
right: amt,
|
right: amt,
|
||||||
top: 0.0,
|
..Self::ZERO
|
||||||
bottom: 0.0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn y(amt: impl UiNum) -> Self {
|
pub fn y(amt: impl UiNum) -> Self {
|
||||||
let amt = amt.to_f32();
|
let amt = Px::from_num(amt);
|
||||||
Self {
|
Self {
|
||||||
left: 0.0,
|
|
||||||
right: 0.0,
|
|
||||||
top: amt,
|
top: amt,
|
||||||
bottom: amt,
|
bottom: amt,
|
||||||
|
..Self::ZERO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn top(amt: impl UiNum) -> Self {
|
pub fn top(amt: impl UiNum) -> Self {
|
||||||
let mut s = Self::ZERO;
|
let mut s = Self::ZERO;
|
||||||
s.top = amt.to_f32();
|
s.top = Px::from_num(amt);
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bottom(amt: impl UiNum) -> Self {
|
pub fn bottom(amt: impl UiNum) -> Self {
|
||||||
let mut s = Self::ZERO;
|
let mut s = Self::ZERO;
|
||||||
s.bottom = amt.to_f32();
|
s.bottom = Px::from_num(amt);
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left(amt: impl UiNum) -> Self {
|
pub fn left(amt: impl UiNum) -> Self {
|
||||||
let mut s = Self::ZERO;
|
let mut s = Self::ZERO;
|
||||||
s.left = amt.to_f32();
|
s.left = Px::from_num(amt);
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn right(amt: impl UiNum) -> Self {
|
pub fn right(amt: impl UiNum) -> Self {
|
||||||
let mut s = Self::ZERO;
|
let mut s = Self::ZERO;
|
||||||
s.right = amt.to_f32();
|
s.right = Px::from_num(amt);
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_top(mut self, amt: impl UiNum) -> Self {
|
pub fn with_top(mut self, amt: impl UiNum) -> Self {
|
||||||
self.top = amt.to_f32();
|
self.top = Px::from_num(amt);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_bottom(mut self, amt: impl UiNum) -> Self {
|
pub fn with_bottom(mut self, amt: impl UiNum) -> Self {
|
||||||
self.bottom = amt.to_f32();
|
self.bottom = Px::from_num(amt);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_left(mut self, amt: impl UiNum) -> Self {
|
pub fn with_left(mut self, amt: impl UiNum) -> Self {
|
||||||
self.left = amt.to_f32();
|
self.left = Px::from_num(amt);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_right(mut self, amt: impl UiNum) -> Self {
|
pub fn with_right(mut self, amt: impl UiNum) -> Self {
|
||||||
self.right = amt.to_f32();
|
self.right = Px::from_num(amt);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: UiNum> From<T> for Padding {
|
impl<T: UiNum> From<T> for Padding {
|
||||||
fn from(amt: T) -> Self {
|
fn from(amt: T) -> Self {
|
||||||
Self::uniform(amt.to_f32())
|
Self::uniform(amt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,9 +45,12 @@ impl Widget for Scroll {
|
|||||||
// sits is this widget's own alignment -- the same property that would
|
// sits is this widget's own alignment -- the same property that would
|
||||||
// have placed the whole scroll in a box longer than it.
|
// have placed the whole scroll in a box longer than it.
|
||||||
let slack = (self.container_len - self.content_len).max(0.0);
|
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));
|
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);
|
painter.widget_aligned(&self.inner, region, RegionAlign::NEAR);
|
||||||
// What it occupies is its box, on both axes: it clips its content to
|
// 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
|
// that box, so it can neither take less of one nor honestly ask for
|
||||||
|
|||||||
+43
-33
@@ -14,7 +14,7 @@ pub enum OrthoSize {
|
|||||||
pub struct Span {
|
pub struct Span {
|
||||||
pub children: Vec<StrongWidget>,
|
pub children: Vec<StrongWidget>,
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
pub gap: f32,
|
pub gap: Px,
|
||||||
pub ortho: OrthoSize,
|
pub ortho: OrthoSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,14 +35,21 @@ impl Widget for Span {
|
|||||||
Some(len) => len,
|
Some(len) => len,
|
||||||
None => painter.widget_within(child, region).len(axis),
|
None => painter.widget_within(child, region).len(axis),
|
||||||
};
|
};
|
||||||
// Onto the grid at the seam: `Len` is still in floats.
|
cursor.px += len.px + self.gap;
|
||||||
cursor.px += Px::from_f32(len.px + self.gap);
|
cursor.rel += len.rel;
|
||||||
cursor.rel += Rel::from_f32(len.rel);
|
|
||||||
lens.push(len);
|
lens.push(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
let gap = self.gap * self.children.len().saturating_sub(1) as f32;
|
let gaps = self
|
||||||
let total = lens.iter().fold(Len::px(gap), |sum, len| sum + *len);
|
.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)`
|
// 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
|
// 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
|
// 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
|
// drawn at all. The validity range is split at the moved boundary, and
|
||||||
// exact there, since no box lands on it any more.
|
// 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;
|
let mut shares = false;
|
||||||
if total.leftover > 0.0 {
|
if total.leftover > Weight::ZERO {
|
||||||
let current = painter.px_len(axis);
|
let current = Px::from_f32(painter.px_len(axis));
|
||||||
let holds = if fixed > 0.0 {
|
let holds = if fixed > Rel::ZERO {
|
||||||
// The box length at which the room reaches the margin.
|
// 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;
|
shares = current > enough;
|
||||||
match shares {
|
match shares {
|
||||||
true => Holds::exact(enough.next_up()..=f32::INFINITY),
|
true => Holds::exact(enough.to_f32().next_up()..=f32::INFINITY),
|
||||||
false => Holds::exact(f32::NEG_INFINITY..=enough),
|
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
|
// The relative parts grow faster than the box does, so here
|
||||||
// a shorter box is the one that leaves room.
|
// 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;
|
shares = current < enough;
|
||||||
match shares {
|
match shares {
|
||||||
true => Holds::exact(f32::NEG_INFINITY..=enough.next_down()),
|
true => Holds::exact(f32::NEG_INFINITY..=enough.to_f32().next_down()),
|
||||||
false => Holds::exact(enough..=f32::INFINITY),
|
false => Holds::exact(enough.to_f32()..=f32::INFINITY),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The relative parts take exactly the box, whatever it is, so
|
// The relative parts take exactly the box, whatever it is, so
|
||||||
// the only room is what negative pixels leave.
|
// the only room is what negative pixels leave.
|
||||||
shares = total.px < -HOLDS_EPSILON_PX;
|
shares = total.px < margin.neg();
|
||||||
Holds::ANY
|
Holds::ANY
|
||||||
};
|
};
|
||||||
painter.holds(axis, holds);
|
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,
|
// 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
|
||||||
// pixels or a fraction keeps those and overflows.
|
// 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);
|
painter.undraw(child);
|
||||||
start.px += Px::from_f32(self.gap);
|
start.px += self.gap;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut span = UiSpan::FULL;
|
let mut span = UiSpan::FULL;
|
||||||
span.start = start;
|
span.start = start;
|
||||||
if len.leftover > 0.0 && shares {
|
if len.leftover > Weight::ZERO && shares {
|
||||||
let offset = UiScalar::new(total.rel, total.px);
|
let offset = UiScalar::from_parts(total.rel, total.px);
|
||||||
let rel_end = UiScalar::rel(len.leftover / total.leftover);
|
let share = Rel::ratio(len.leftover, total.leftover);
|
||||||
|
let rel_end = UiScalar::from_parts(share, Px::ZERO);
|
||||||
let end = (UiScalar::rel_max() + start) - offset;
|
let end = (UiScalar::rel_max() + start) - offset;
|
||||||
start = rel_end.within(&start.to(end));
|
start = rel_end.within(&start.to(end));
|
||||||
}
|
}
|
||||||
start.px += Px::from_f32(len.px);
|
start.px += len.px;
|
||||||
start.rel += Rel::from_f32(len.rel);
|
start.rel += len.rel;
|
||||||
span.end = start;
|
span.end = start;
|
||||||
let mut region = UiRegion::from_axis(axis, span, UiSpan::FULL);
|
let mut region = UiRegion::from_axis(axis, span, UiSpan::FULL);
|
||||||
if self.dir.sign == Sign::Neg {
|
if self.dir.sign == Sign::Neg {
|
||||||
@@ -116,13 +126,13 @@ impl Widget for Span {
|
|||||||
// span's own eventual width admits multiple fixed points.
|
// span's own eventual width admits multiple fixed points.
|
||||||
// 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 != 0.0 || used.leftover != 0.0 {
|
if used.rel != Rel::ZERO || used.leftover != Weight::ZERO {
|
||||||
ortho = Len::LEFTOVER;
|
ortho = Len::LEFTOVER;
|
||||||
} else if ortho.leftover == 0.0 {
|
} else if ortho.leftover == Weight::ZERO {
|
||||||
ortho.px = ortho.px.max(used.px);
|
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
|
// Carried whole rather than collapsed to one share: a span that sizes
|
||||||
@@ -146,13 +156,13 @@ impl Span {
|
|||||||
Self {
|
Self {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
dir,
|
dir,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gap(mut self, gap: impl UiNum) -> Self {
|
pub fn gap(mut self, gap: impl UiNum) -> Self {
|
||||||
self.gap = gap.to_f32();
|
self.gap = Px::from_num(gap);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +183,7 @@ impl Span {
|
|||||||
pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
||||||
pub children: Wa,
|
pub children: Wa,
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
pub gap: f32,
|
pub gap: Px,
|
||||||
pub ortho: OrthoSize,
|
pub ortho: OrthoSize,
|
||||||
_pd: PhantomData<(State, Tag)>,
|
_pd: PhantomData<(State, Tag)>,
|
||||||
}
|
}
|
||||||
@@ -201,14 +211,14 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
|||||||
Self {
|
Self {
|
||||||
children,
|
children,
|
||||||
dir,
|
dir,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
_pd: PhantomData,
|
_pd: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gap(mut self, gap: impl UiNum) -> Self {
|
pub fn gap(mut self, gap: impl UiNum) -> Self {
|
||||||
self.gap = gap.to_f32();
|
self.gap = Px::from_num(gap);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ struct BranchesOnMeasurement {
|
|||||||
impl Widget for BranchesOnMeasurement {
|
impl Widget for BranchesOnMeasurement {
|
||||||
fn draw(&mut self, painter: &mut Painter) -> Size {
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
||||||
let mut top = UiRegion::FULL;
|
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 measured = painter.widget_within(&self.probe, top).len(Axis::X);
|
||||||
let px = measured
|
let px = measured
|
||||||
.apply_leftover()
|
.apply_leftover()
|
||||||
@@ -30,7 +30,7 @@ impl Widget for BranchesOnMeasurement {
|
|||||||
.to_f32();
|
.to_f32();
|
||||||
|
|
||||||
let mut below = UiRegion::FULL;
|
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 {
|
match px > self.threshold {
|
||||||
true => painter.widget_within(&self.wide, below),
|
true => painter.widget_within(&self.wide, below),
|
||||||
false => painter.widget_within(&self.narrow, below),
|
false => painter.widget_within(&self.narrow, below),
|
||||||
|
|||||||
+4
-4
@@ -340,10 +340,10 @@ fn hairlines(h: &mut Harness, depth: usize, marks: &mut Vec<WidgetId>) -> Strong
|
|||||||
let second = hairlines(h, depth - 1, marks);
|
let second = hairlines(h, depth - 1, marks);
|
||||||
let second = Pad {
|
let second = Pad {
|
||||||
padding: Padding {
|
padding: Padding {
|
||||||
left: 3.0,
|
left: Px::from_int(3),
|
||||||
right: 7.0,
|
right: Px::from_int(7),
|
||||||
top: 0.0,
|
top: Px::ZERO,
|
||||||
bottom: 0.0,
|
bottom: Px::ZERO,
|
||||||
},
|
},
|
||||||
inner: second,
|
inner: second,
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-9
@@ -119,7 +119,7 @@ impl Node {
|
|||||||
let handle = Span {
|
let handle = Span {
|
||||||
children,
|
children,
|
||||||
dir: dir(*down),
|
dir: dir(*down),
|
||||||
gap: *gap,
|
gap: Px::from_f32(*gap),
|
||||||
ortho: match down {
|
ortho: match down {
|
||||||
true => OrthoSize::Children,
|
true => OrthoSize::Children,
|
||||||
false => OrthoSize::Full,
|
false => OrthoSize::Full,
|
||||||
@@ -140,12 +140,7 @@ impl Node {
|
|||||||
Node::Pad(p, kid) => {
|
Node::Pad(p, kid) => {
|
||||||
let inner = kid.build(h, out, spans, sized);
|
let inner = kid.build(h, out, spans, sized);
|
||||||
Pad {
|
Pad {
|
||||||
padding: Padding {
|
padding: Padding::uniform(*p),
|
||||||
left: *p,
|
|
||||||
right: *p,
|
|
||||||
top: *p,
|
|
||||||
bottom: *p,
|
|
||||||
},
|
|
||||||
inner,
|
inner,
|
||||||
}
|
}
|
||||||
.add_strong(&mut h.rsc)
|
.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
|
/// 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<Len>) -> Option<Len> {
|
||||||
|
let half = Rel::from_f32(0.5);
|
||||||
len.map(|len| Len {
|
len.map(|len| Len {
|
||||||
px: len.px * 0.5 + 13.0,
|
px: len.px.mul(half) + Px::from_int(13),
|
||||||
rel: len.rel * 0.5,
|
rel: len.rel.mul(half),
|
||||||
leftover: len.leftover,
|
leftover: len.leftover,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -159,7 +159,7 @@ fn plant_pair(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, WeakWidget<Span
|
|||||||
let span = Span {
|
let span = Span {
|
||||||
children,
|
children,
|
||||||
dir: Dir::RIGHT,
|
dir: Dir::RIGHT,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
@@ -213,7 +213,7 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
|
|||||||
let inner = Span {
|
let inner = Span {
|
||||||
children: inner_children,
|
children: inner_children,
|
||||||
dir: Dir::RIGHT,
|
dir: Dir::RIGHT,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
@@ -227,7 +227,7 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
|
|||||||
let outer = Span {
|
let outer = Span {
|
||||||
children: outer_children,
|
children: outer_children,
|
||||||
dir: Dir::RIGHT,
|
dir: Dir::RIGHT,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
@@ -333,7 +333,7 @@ fn plant_boundary(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
|
|||||||
let measured = Span {
|
let measured = Span {
|
||||||
children: pair,
|
children: pair,
|
||||||
dir: Dir::DOWN,
|
dir: Dir::DOWN,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
@@ -353,7 +353,7 @@ fn plant_boundary(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
|
|||||||
let inner = Span {
|
let inner = Span {
|
||||||
children: inner_children,
|
children: inner_children,
|
||||||
dir: Dir::DOWN,
|
dir: Dir::DOWN,
|
||||||
gap: 0.0,
|
gap: Px::ZERO,
|
||||||
ortho: OrthoSize::Children,
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
|
|||||||
Reference in new issue
Block a user