iris: separate layout allocation from lengths

This commit is contained in:
iris committed 2026-09-12 19:35:18 -04:00
1 parent 2b9d8c49a0
commit 1f15125992
17 files changed
+395 -168

No files matched your search

+4 -4
View File
@@ -140,7 +140,7 @@ fn bench_input_grows(n: usize, lines: usize) {
let input_area = rsc.ui.widgets.add_strong(Sized { let input_area = rsc.ui.widgets.add_strong(Sized {
inner: input_rect.any(), inner: input_rect.any(),
x: None, x: None,
y: Some(abs(line_height)), y: Some(abs(line_height).into()),
}); });
let input_area_weak = input_area.weak(); let input_area_weak = input_area.weak();
@@ -162,7 +162,7 @@ fn bench_input_grows(n: usize, lines: usize) {
let mut total_moves = 0u64; let mut total_moves = 0u64;
for line in 1..=lines { for line in 1..=lines {
rsc.ui.widgets.get_mut(&input_area_weak).unwrap().y = rsc.ui.widgets.get_mut(&input_area_weak).unwrap().y =
Some(abs(line_height * (line + 1) as f32)); Some(abs(line_height * (line + 1) as f32).into());
let start = Instant::now(); let start = Instant::now();
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
total += start.elapsed(); total += start.elapsed();
@@ -253,7 +253,7 @@ fn bench_expand_holds_edge(n: usize, growths: usize) {
let sized = rsc.ui.widgets.add_strong(Sized { let sized = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(abs(40.0)), y: Some(abs(40.0).into()),
}); });
growable = Some(sized.weak()); growable = Some(sized.weak());
list.push_back(LazyItem::new(i as u64, sized.any())); list.push_back(LazyItem::new(i as u64, sized.any()));
@@ -287,7 +287,7 @@ fn bench_expand_holds_edge(n: usize, growths: usize) {
.unwrap() .unwrap()
.note_tap(top + 1.0); .note_tap(top + 1.0);
} }
rsc.ui.widgets.get_mut(&growable).unwrap().y = Some(abs(height)); rsc.ui.widgets.get_mut(&growable).unwrap().y = Some(abs(height).into());
let start = Instant::now(); let start = Instant::now();
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
total += start.elapsed(); total += start.elapsed();
+189 -45
View File
@@ -3,10 +3,13 @@ use crate::{UiNum, 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,
} }
/// A length resolved from physical pixels, density-independent pixels, and a
/// fraction of a reference length. Unlike [`LayoutLen`], it carries no claim
/// on space left over by a layout container.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Len { pub struct Len {
/// Physical pixels -- a raw device pixel, unaffected by the display's /// Physical pixels -- a raw device pixel, unaffected by the display's
@@ -17,6 +20,15 @@ pub struct Len {
pub abs: f32, pub abs: f32,
pub dp: f32, pub dp: f32,
pub rel: f32, pub rel: f32,
}
/// A widget length plus its proportional claim on the space left after fixed
/// and relative lengths have been allocated.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutLen {
pub abs: f32,
pub dp: f32,
pub rel: f32,
pub rest: f32, pub rest: f32,
} }
@@ -26,8 +38,25 @@ impl<N: UiNum> From<N> for Len {
} }
} }
impl<Nx: UiNum, Ny: UiNum> From<(Nx, Ny)> for Size { impl<N: UiNum> From<N> for LayoutLen {
fn from((x, y): (Nx, Ny)) -> Self { fn from(value: N) -> Self {
Self::abs(value.to_f32())
}
}
impl From<Len> for LayoutLen {
fn from(value: Len) -> Self {
Self {
abs: value.abs,
dp: value.dp,
rel: value.rel,
rest: 0.0,
}
}
}
impl<X: Into<LayoutLen>, Y: Into<LayoutLen>> From<(X, Y)> for Size {
fn from((x, y): (X, Y)) -> Self {
Self { Self {
x: x.into(), x: x.into(),
y: y.into(), y: y.into(),
@@ -35,41 +64,47 @@ impl<Nx: UiNum, Ny: UiNum> From<(Nx, Ny)> for Size {
} }
} }
impl From<LayoutLen> for Size {
fn from(value: LayoutLen) -> Self {
Self { x: value, y: value }
}
}
impl From<Len> for Size { impl From<Len> for Size {
fn from(value: Len) -> Self { fn from(value: Len) -> Self {
Self { x: value, y: value } Self::from(LayoutLen::from(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 REST: Self = Self { pub const REST: Self = Self {
x: Len::REST, x: LayoutLen::REST,
y: Len::REST, y: LayoutLen::REST,
}; };
pub fn abs(v: Vec2) -> Self { pub fn abs(v: Vec2) -> Self {
Self { Self {
x: Len::abs(v.x), x: LayoutLen::abs(v.x),
y: Len::abs(v.y), y: LayoutLen::abs(v.y),
} }
} }
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 rest(v: Vec2) -> Self { pub fn rest(v: Vec2) -> Self {
Self { Self {
x: Len::rest(v.x), x: LayoutLen::rest(v.x),
y: Len::rest(v.y), y: LayoutLen::rest(v.y),
} }
} }
@@ -80,7 +115,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,
@@ -93,7 +128,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,
@@ -101,7 +136,7 @@ impl Size {
} }
} }
impl Len { impl LayoutLen {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
abs: 0.0, abs: 0.0,
dp: 0.0, dp: 0.0,
@@ -119,7 +154,7 @@ impl Len {
/// Resolves to a `UiScalar`, folding `dp` into `abs` pixels against /// Resolves to a `UiScalar`, folding `dp` into `abs` pixels against
/// `density` (physical pixels per dp -- 1.0 on a desktop or an /// `density` (physical pixels per dp -- 1.0 on a desktop or an
/// unscaled display, `content_scale` on Android; see `dp`'s field /// unscaled display, `content_scale` on Android; see `dp`'s field
/// doc). Every other component of `Len` is already resolution- /// doc). Every other component of `LayoutLen` is already resolution-
/// independent (`rel` is a fraction of the parent; `rest` becomes a /// independent (`rel` is a fraction of the parent; `rest` becomes a
/// fraction too, below), so `density` only ever touches this one term. /// fraction too, below), so `density` only ever touches this one term.
pub fn apply_rest(&self, density: f32) -> UiScalar { pub fn apply_rest(&self, density: f32) -> UiScalar {
@@ -129,11 +164,11 @@ impl Len {
} }
} }
/// The same fold as [`Self::apply_rest`] but staying a `Len`, so /// The same fold as [`Self::apply_rest`] but staying a `LayoutLen`, so
/// `rest` survives: `dp` becomes physical pixels and every other /// `rest` survives: `dp` becomes physical pixels and every other
/// component is left alone. /// component is left alone.
/// ///
/// **A `Len` a widget *reports* must have been through this.** `dp` is /// **A `LayoutLen` a widget *reports* must have been through this.** `dp` is
/// an input unit -- a number the widget author wrote -- and the /// an input unit -- a number the widget author wrote -- and the
/// containers that consume a reported length read `abs`/`rel`/`rest` /// containers that consume a reported length read `abs`/`rel`/`rest`
/// directly (`Span::draw`'s placement arithmetic, `Pad`'s addition), /// directly (`Span::draw`'s placement arithmetic, `Pad`'s addition),
@@ -186,35 +221,68 @@ impl Len {
} }
} }
impl Len {
pub const ZERO: Self = Self {
abs: 0.0,
dp: 0.0,
rel: 0.0,
};
pub fn abs(abs: impl UiNum) -> Self {
Self {
abs: abs.to_f32(),
dp: 0.0,
rel: 0.0,
}
}
pub fn dp(dp: impl UiNum) -> Self {
Self {
abs: 0.0,
dp: dp.to_f32(),
rel: 0.0,
}
}
pub fn rel(rel: impl UiNum) -> Self {
Self {
abs: 0.0,
dp: 0.0,
rel: rel.to_f32(),
}
}
pub const fn fold_dp(self, density: f32) -> Self {
Self {
abs: self.abs + self.dp * density,
dp: 0.0,
rel: self.rel,
}
}
pub const fn resolve(self, density: f32) -> UiScalar {
let folded = self.fold_dp(density);
UiScalar {
rel: folded.rel,
abs: folded.abs,
}
}
}
pub mod len_fns { pub mod len_fns {
use super::*; use super::*;
pub fn abs(abs: impl UiNum) -> Len { pub fn abs(abs: impl UiNum) -> Len {
Len { Len::abs(abs)
abs: abs.to_f32(),
dp: 0.0,
rel: 0.0,
rest: 0.0,
}
} }
pub fn dp(dp: impl UiNum) -> Len { pub fn dp(dp: impl UiNum) -> Len {
Len { Len::dp(dp)
abs: 0.0,
dp: dp.to_f32(),
rel: 0.0,
rest: 0.0,
}
} }
pub fn rel(rel: impl UiNum) -> Len { pub fn rel(rel: impl UiNum) -> Len {
Len { Len::rel(rel)
abs: 0.0,
dp: 0.0,
rel: rel.to_f32(),
rest: 0.0,
}
} }
pub fn rest(ratio: impl UiNum) -> Len { pub fn rest(ratio: impl UiNum) -> LayoutLen {
Len { LayoutLen {
abs: 0.0, abs: 0.0,
dp: 0.0, dp: 0.0,
rel: 0.0, rel: 0.0,
@@ -223,25 +291,49 @@ pub mod len_fns {
} }
} }
impl_op!(Len Add add; abs dp rel rest); impl_op!(LayoutLen Add add; abs dp rel rest);
impl_op!(Len Sub sub; abs dp rel rest); impl_op!(LayoutLen Sub sub; abs dp rel rest);
impl_op!(Len Add add; abs dp rel);
impl_op!(Len Sub sub; abs dp rel);
impl std::ops::Add<Len> for LayoutLen {
type Output = Self;
fn add(self, rhs: Len) -> Self::Output {
self + Self::from(rhs)
}
}
impl std::ops::Sub<Len> for LayoutLen {
type Output = Self;
fn sub(self, rhs: Len) -> Self::Output {
self - Self::from(rhs)
}
}
impl_op!(Size Add add; x y); impl_op!(Size Add add; x y);
impl_op!(Size Sub sub; x y); impl_op!(Size Sub sub; x y);
impl Default for Len { impl Default for LayoutLen {
fn default() -> Self { fn default() -> Self {
Self::rest(1.0) Self::rest(1.0)
} }
} }
impl Default for Len {
fn default() -> Self {
Self::ZERO
}
}
impl std::fmt::Display for Size { impl std::fmt::Display for Size {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y) write!(f, "({}, {})", self.x, self.y)
} }
} }
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.abs != 0.0 { if self.abs != 0.0 {
write!(f, "{} abs;", self.abs)?; write!(f, "{} abs;", self.abs)?;
@@ -258,3 +350,55 @@ impl std::fmt::Display for Len {
Ok(()) Ok(())
} }
} }
impl std::fmt::Display for Len {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.abs != 0.0 {
write!(f, "{} abs;", self.abs)?;
}
if self.dp != 0.0 {
write!(f, "{} dp;", self.dp)?;
}
if self.rel != 0.0 {
write!(f, "{} rel;", self.rel)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_ordinary_length_enters_layout_without_claiming_rest() {
let layout = LayoutLen::from(Len {
abs: 3.0,
dp: 4.0,
rel: 0.5,
});
assert_eq!(layout.abs, 3.0);
assert_eq!(layout.dp, 4.0);
assert_eq!(layout.rel, 0.5);
assert_eq!(layout.rest, 0.0);
}
#[test]
fn ordinary_and_layout_lengths_keep_their_own_defaults() {
assert_eq!(Len::default(), Len::ZERO);
assert_eq!(LayoutLen::default(), LayoutLen::REST);
}
#[test]
fn adding_an_ordinary_length_preserves_a_layout_claim() {
assert_eq!(
LayoutLen::rest(2) + Len::dp(8),
LayoutLen {
abs: 0.0,
dp: 8.0,
rel: 0.0,
rest: 2.0,
}
);
}
}
+5 -5
View File
@@ -1,7 +1,7 @@
use crate::{ use crate::{
Axis, Len, MoveOffset, PaintId, RegionAlign, RenderedText, Size, StrongWidget, TextHandle, Axis, LayoutLen, MoveOffset, PaintId, RegionAlign, RenderedText, Size, StrongWidget,
TextResources, TextureHandle, UiData, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, TextHandle, TextResources, TextureHandle, UiData, UiRegion, UiRenderState, UiRsc, UiScalar,
WidgetId, UiVec2, WidgetId,
render::{ render::{
Drawn, GlyphPrimitive, IMAGE_BINDING, Mask, MaskIdx, MoveIdx, NOT_DRAWN, Primitive, Drawn, GlyphPrimitive, IMAGE_BINDING, Mask, MaskIdx, MoveIdx, NOT_DRAWN, Primitive,
PrimitiveHandle, PrimitiveInst, RectPrimitive, PrimitiveHandle, PrimitiveInst, RectPrimitive,
@@ -253,7 +253,7 @@ impl<'a> Painter<'a> {
} }
} }
pub fn known_len<W: ?Sized>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Option<Len> { pub fn known_len<W: ?Sized>(&mut self, id: &StrongWidget<W>, axis: Axis) -> Option<LayoutLen> {
let len = if let Some(len) = self.rsc.widgets().get_dyn(id.id())?.size_hint(axis) { let len = if let Some(len) = self.rsc.widgets().get_dyn(id.id())?.size_hint(axis) {
Some(len.fold_dp(self.density())) Some(len.fold_dp(self.density()))
} else if !self.reuse_child_sizes[match axis { } else if !self.reuse_child_sizes[match axis {
@@ -480,7 +480,7 @@ impl<'a> Painter<'a> {
} }
/// Physical pixels per `dp` -- see `UiRenderState::density`'s field /// Physical pixels per `dp` -- see `UiRenderState::density`'s field
/// doc. What `Len::dp`'s `apply_rest` call resolves against. /// doc. What `LayoutLen::dp`'s `apply_rest` call resolves against.
pub fn density(&self) -> f32 { pub fn density(&self) -> f32 {
self.render_state.density self.render_state.density
} }
+3 -3
View File
@@ -31,7 +31,7 @@ pub struct UiRenderState {
pub primitives: Primitives, pub primitives: Primitives,
pub layers: PrimitiveLayers, pub layers: PrimitiveLayers,
pub(super) output_size: Vec2, pub(super) output_size: Vec2,
/// Physical pixels per `dp` -- see `Len::dp`'s field doc. `1.0` (an /// Physical pixels per `dp` -- see `LayoutLen::dp`'s field doc. `1.0` (an
/// unscaled display) until a backend that knows its own density calls /// unscaled display) until a backend that knows its own density calls
/// `set_density` (Android's `content_scale`, read at `surface_changed` /// `set_density` (Android's `content_scale`, read at `surface_changed`
/// time); the winit backend has no analogous per-monitor value wired up /// time); the winit backend has no analogous per-monitor value wired up
@@ -207,7 +207,7 @@ impl UiRenderState {
self.resized = true; self.resized = true;
} }
/// Sets the physical-pixels-per-dp ratio every `Len::dp` in the tree /// Sets the physical-pixels-per-dp ratio every `LayoutLen::dp` in the tree
/// resolves against from the next layout pass on -- see `density`'s /// resolves against from the next layout pass on -- see `density`'s
/// field doc. Not folded into `resize` because the two change on /// field doc. Not folded into `resize` because the two change on
/// different triggers (a surface resize on every rotation or keyboard /// different triggers (a surface resize on every rotation or keyboard
@@ -485,7 +485,7 @@ impl UiRenderState {
debug_assert!( debug_assert!(
size.x.dp == 0.0 && size.y.dp == 0.0, size.x.dp == 0.0 && size.y.dp == 0.0,
"widget {id:?} reported an unresolved `dp` size ({size:?}); \ "widget {id:?} reported an unresolved `dp` size ({size:?}); \
report `Len::fold_dp(painter.density())` instead" report `LayoutLen::fold_dp(painter.density())` instead"
); );
for (axis, hint) in [Axis::X, Axis::Y] for (axis, hint) in [Axis::X, Axis::Y]
.into_iter() .into_iter()
+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;
@@ -28,7 +28,7 @@ pub enum ChildOrder {
pub trait Widget: Any { pub trait Widget: Any {
fn draw(&mut self, painter: &mut Painter); fn draw(&mut self, painter: &mut Painter);
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
None None
} }
@@ -63,8 +63,8 @@ impl Widget for () {
true true
} }
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
Some(Len::ZERO) Some(LayoutLen::ZERO)
} }
} }
+72 -27
View File
@@ -18,7 +18,7 @@ struct FixedRect(f32);
impl Widget for FixedRect { impl Widget for FixedRect {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let size = Size::from_axis(Axis::Y, Len::abs(self.0), Len::REST); let size = Size::from_axis(Axis::Y, LayoutLen::abs(self.0), LayoutLen::REST);
let paint = painter.paint(&PaintId::WHITE); let paint = painter.paint(&PaintId::WHITE);
painter.primitive_within( painter.primitive_within(
RectPrimitive::color(paint), RectPrimitive::color(paint),
@@ -50,7 +50,11 @@ struct TracedLeaf {
impl Widget for TracedLeaf { impl Widget for TracedLeaf {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
self.trace.borrow_mut().push("child"); self.trace.borrow_mut().push("child");
painter.set_size(Size::from_axis(Axis::Y, Len::abs(self.height), Len::REST)); painter.set_size(Size::from_axis(
Axis::Y,
LayoutLen::abs(self.height),
LayoutLen::REST,
));
} }
} }
@@ -68,7 +72,11 @@ struct CountedLeaf {
impl Widget for CountedLeaf { impl Widget for CountedLeaf {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
self.draws.set(self.draws.get() + 1); self.draws.set(self.draws.get() + 1);
painter.set_size(Size::from_axis(Axis::Y, Len::abs(self.height), Len::REST)); painter.set_size(Size::from_axis(
Axis::Y,
LayoutLen::abs(self.height),
LayoutLen::REST,
));
} }
} }
@@ -146,7 +154,10 @@ fn a_size_dependent_parent_is_invalidated_before_layout_runs_downward() {
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
assert_eq!(&*trace.borrow(), &["parent", "child"]); assert_eq!(&*trace.borrow(), &["parent", "child"]);
assert_eq!(render.active[&parent_weak.id()].size.y, Len::abs(40.0)); assert_eq!(
render.active[&parent_weak.id()].size.y,
LayoutLen::abs(40.0)
);
} }
#[test] #[test]
@@ -192,7 +203,7 @@ fn a_span_reuses_unchanged_sibling_sizes_when_only_its_along_extent_changes() {
let span = rsc.ui.widgets.add_strong(Span { let span = rsc.ui.widgets.add_strong(Span {
children: vec![changed.any(), sibling.any()], children: vec![changed.any(), sibling.any()],
dir: Dir::DOWN, dir: Dir::DOWN,
gap: Len::ZERO, gap: LayoutLen::ZERO,
}); });
let root = rsc let root = rsc
.ui .ui
@@ -275,7 +286,7 @@ fn a_hinted_rest_draws_once_and_only_moves_the_fixed_child_after_it() {
let fill = rsc.ui.widgets.add_strong(Sized { let fill = rsc.ui.widgets.add_strong(Sized {
inner: fill.any(), inner: fill.any(),
x: None, x: None,
y: Some(Len::REST), y: Some(LayoutLen::REST),
}); });
let last = rsc.ui.widgets.add_strong(FixedRect(40.0)); let last = rsc.ui.widgets.add_strong(FixedRect(40.0));
let last_w = last.weak(); let last_w = last.weak();
@@ -308,7 +319,7 @@ fn scrolled_rects(
let row = rsc.ui.widgets.add_strong(Sized { let row = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(10.0)), y: Some(LayoutLen::abs(10.0)),
}); });
span.push(row.any()); span.push(row.any());
} }
@@ -531,7 +542,7 @@ fn a_scroll_measures_the_box_it_was_offered_not_the_window() {
let tall = rsc.ui.widgets.add_strong(Sized { let tall = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(1000.0)), y: Some(LayoutLen::abs(1000.0)),
}); });
let scroll = rsc let scroll = rsc
.ui .ui
@@ -542,7 +553,7 @@ fn a_scroll_measures_the_box_it_was_offered_not_the_window() {
let capped = rsc.ui.widgets.add_strong(MaxSize { let capped = rsc.ui.widgets.add_strong(MaxSize {
inner: scroll.any(), inner: scroll.any(),
x: None, x: None,
y: Some(Len::abs(100.0)), y: Some(LayoutLen::abs(100.0)),
}); });
let capped_id = capped.id(); let capped_id = capped.id();
let root = capped.any(); let root = capped.any();
@@ -555,11 +566,11 @@ fn a_scroll_measures_the_box_it_was_offered_not_the_window() {
assert_eq!( assert_eq!(
render.active.get(&scroll_id).unwrap().size.y, render.active.get(&scroll_id).unwrap().size.y,
Len::abs(1000.0) LayoutLen::abs(1000.0)
); );
assert_eq!( assert_eq!(
render.active.get(&capped_id).unwrap().size.y, render.active.get(&capped_id).unwrap().size.y,
Len::abs(100.0), LayoutLen::abs(100.0),
"the cap, not the content and not the window" "the cap, not the content and not the window"
); );
@@ -579,7 +590,7 @@ fn a_panned_widgets_own_hit_box_moves_exactly_once() {
let tall = rsc.ui.widgets.add_strong(Sized { let tall = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(1000.0)), y: Some(LayoutLen::abs(1000.0)),
}); });
let tall_w = tall.weak(); let tall_w = tall.weak();
let scroll = rsc let scroll = rsc
@@ -624,7 +635,7 @@ fn a_masked_widget_keeps_one_mask_slot_that_is_always_its_own_region() {
let capped = rsc.ui.widgets.add_strong(MaxSize { let capped = rsc.ui.widgets.add_strong(MaxSize {
inner: masked.any(), inner: masked.any(),
x: None, x: None,
y: Some(Len::abs(60.0)), y: Some(LayoutLen::abs(60.0)),
}); });
let mut span = Span::empty(Dir::DOWN); let mut span = Span::empty(Dir::DOWN);
span.push(filler.any()); span.push(filler.any());
@@ -658,12 +669,12 @@ fn a_dp_cap_is_reported_in_pixels_so_a_span_can_place_it() {
let tall = rsc.ui.widgets.add_strong(Sized { let tall = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(1000.0)), y: Some(LayoutLen::abs(1000.0)),
}); });
let capped = rsc.ui.widgets.add_strong(MaxSize { let capped = rsc.ui.widgets.add_strong(MaxSize {
inner: tall.any(), inner: tall.any(),
x: None, x: None,
y: Some(Len::dp(100.0)), y: Some(LayoutLen::dp(100.0)),
}); });
let capped_w = capped.weak(); let capped_w = capped.weak();
let filler = rsc.ui.widgets.add_strong(Rect::new(PaintId::BLACK)); let filler = rsc.ui.widgets.add_strong(Rect::new(PaintId::BLACK));
@@ -698,7 +709,7 @@ fn a_size_independent_widget_moved_by_its_parent_has_the_hit_box_it_is_drawn_at(
let spacer = rsc.ui.widgets.add_strong(Sized { let spacer = rsc.ui.widgets.add_strong(Sized {
inner: top.any(), inner: top.any(),
x: None, x: None,
y: Some(Len::abs(100.0)), y: Some(LayoutLen::abs(100.0)),
}); });
let spacer_w = spacer.weak(); let spacer_w = spacer.weak();
let below = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)); let below = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE));
@@ -718,7 +729,7 @@ fn a_size_independent_widget_moved_by_its_parent_has_the_hit_box_it_is_drawn_at(
first.top_left first.top_left
); );
rsc.ui.widgets.get_mut(&spacer_w).unwrap().y = Some(Len::abs(250.0)); rsc.ui.widgets.get_mut(&spacer_w).unwrap().y = Some(LayoutLen::abs(250.0));
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
let after = render.window_region(&below_w, &rsc).unwrap(); let after = render.window_region(&below_w, &rsc).unwrap();
assert!( assert!(
@@ -763,7 +774,7 @@ fn a_widget_moved_by_its_parent_and_then_placed_inside_it_lands_at_the_placement
let child = rsc.ui.widgets.add_strong(Sized { let child = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(40.0)), y: Some(LayoutLen::abs(40.0)),
}); });
let child_w = child.weak(); let child_w = child.weak();
let parent = rsc.ui.widgets.add_strong(MoveThenPlace { let parent = rsc.ui.widgets.add_strong(MoveThenPlace {
@@ -998,7 +1009,7 @@ fn a_scroll_area_opens_at_the_start_of_content_it_has_not_measured_yet() {
let tall = rsc.ui.widgets.add_strong(Sized { let tall = rsc.ui.widgets.add_strong(Sized {
inner: fill, inner: fill,
x: None, x: None,
y: Some(Len::abs(5000.0)), y: Some(LayoutLen::abs(5000.0)),
}); });
let scroll = rsc let scroll = rsc
.ui .ui
@@ -1032,7 +1043,7 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() {
let header = rsc.ui.widgets.add_strong(Sized { let header = rsc.ui.widgets.add_strong(Sized {
inner: header_fill, inner: header_fill,
x: None, x: None,
y: Some(Len::abs(HEADER)), y: Some(LayoutLen::abs(HEADER)),
}); });
let mut inner = Span::empty(Dir::DOWN); let mut inner = Span::empty(Dir::DOWN);
let mut rects = Vec::new(); let mut rects = Vec::new();
@@ -1042,7 +1053,7 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() {
let sized = rsc.ui.widgets.add_strong(Sized { let sized = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(ROW)), y: Some(LayoutLen::abs(ROW)),
}); });
let padded = rsc.ui.widgets.add_strong(Pad { let padded = rsc.ui.widgets.add_strong(Pad {
padding: Padding::uniform(PAD), padding: Padding::uniform(PAD),
@@ -1056,7 +1067,7 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() {
}); });
let wide = rsc.ui.widgets.add_strong(Sized { let wide = rsc.ui.widgets.add_strong(Sized {
inner: card.any(), inner: card.any(),
x: Some(Len::rest(1.0)), x: Some(LayoutLen::rest(1.0)),
y: None, y: None,
}); });
inner.push(wide.any()); inner.push(wide.any());
@@ -1065,7 +1076,7 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() {
let outer = rsc.ui.widgets.add_strong(Span { let outer = rsc.ui.widgets.add_strong(Span {
children: vec![header.any(), inner.any()], children: vec![header.any(), inner.any()],
dir: Dir::DOWN, dir: Dir::DOWN,
gap: Len::ZERO, gap: LayoutLen::ZERO,
}); });
let mut list = LazySpan::new(Dir::DOWN, Pin::End); let mut list = LazySpan::new(Dir::DOWN, Pin::End);
list.push_back(LazyItem::new(0, outer.any())); list.push_back(LazyItem::new(0, outer.any()));
@@ -1101,6 +1112,40 @@ fn a_span_of_padded_children_inside_a_span_draws_each_where_its_box_is() {
} }
} }
#[test]
fn rest_padding_claims_leftover_space_around_fixed_content() {
let mut rsc = TestRsc { ui: Ui::default() };
let content = rect(PaintId::WHITE).sized((20, 10)).add_strong(&mut rsc);
let content_id = content.weak();
let padded = rsc.ui.widgets.add_strong(Pad {
inner: content.any(),
padding: Padding {
left: LayoutLen::rest(0.5),
right: LayoutLen::rest(0.5),
..Padding::ZERO
},
exact_region: false,
});
let root = rsc
.ui
.widgets
.add_strong(Sized {
inner: padded.any(),
x: Some(LayoutLen::abs(100)),
y: None,
})
.any();
let mut render = UiRenderState::new();
render.resize((100.0, 40.0));
render.update(&root, &mut rsc);
render.update(&root, &mut rsc);
let region = render.window_region(&content_id, &rsc).unwrap();
assert_eq!(region.top_left.x, 40.0);
assert_eq!(region.bot_right.x, 60.0);
}
#[test] #[test]
fn a_new_child_in_a_growing_lazy_row_uses_its_final_box_immediately() { fn a_new_child_in_a_growing_lazy_row_uses_its_final_box_immediately() {
const FIRST: f32 = 30.0; const FIRST: f32 = 30.0;
@@ -1113,15 +1158,15 @@ fn a_new_child_in_a_growing_lazy_row_uses_its_final_box_immediately() {
let first = rsc.ui.widgets.add_strong(Sized { let first = rsc.ui.widgets.add_strong(Sized {
inner: first.any(), inner: first.any(),
x: None, x: None,
y: Some(Len::abs(FIRST)), y: Some(LayoutLen::abs(FIRST)),
}); });
let mut contents = Span::empty(Dir::DOWN).gap(Len::abs(GAP)); let mut contents = Span::empty(Dir::DOWN).gap(LayoutLen::abs(GAP));
contents.push(first.any()); contents.push(first.any());
let contents = rsc.ui.widgets.add_strong(contents); let contents = rsc.ui.widgets.add_strong(contents);
let contents_w = contents.weak(); let contents_w = contents.weak();
let row = rsc.ui.widgets.add_strong(Sized { let row = rsc.ui.widgets.add_strong(Sized {
inner: contents.any(), inner: contents.any(),
x: Some(Len::rest(1.0)), x: Some(LayoutLen::rest(1.0)),
y: None, y: None,
}); });
let mut list = LazySpan::new(Dir::DOWN, Pin::End); let mut list = LazySpan::new(Dir::DOWN, Pin::End);
@@ -1137,7 +1182,7 @@ fn a_new_child_in_a_growing_lazy_row_uses_its_final_box_immediately() {
let second = rsc.ui.widgets.add_strong(Sized { let second = rsc.ui.widgets.add_strong(Sized {
inner: second.any(), inner: second.any(),
x: None, x: None,
y: Some(Len::abs(SECOND)), y: Some(LayoutLen::abs(SECOND)),
}); });
rsc.ui rsc.ui
.widgets .widgets
+6 -6
View File
@@ -277,7 +277,7 @@ fn a_finger_drag_over_a_scroll_area_pans_it() {
}; };
let scroll_strong = rect(PaintId::WHITE) let scroll_strong = rect(PaintId::WHITE)
.height(Len::abs(1000.0)) .height(LayoutLen::abs(1000.0))
.scrollable(Axis::Y, Pin::Start) .scrollable(Axis::Y, Pin::Start)
.add_strong(&mut rsc); .add_strong(&mut rsc);
let scroll = scroll_strong.weak(); let scroll = scroll_strong.weak();
@@ -367,7 +367,7 @@ fn a_scroll_area_that_captured_the_pointer_learns_its_gesture_ended() {
events: EventManager::default(), events: EventManager::default(),
}; };
let scroll_strong = rect(PaintId::WHITE) let scroll_strong = rect(PaintId::WHITE)
.height(Len::abs(1000.0)) .height(LayoutLen::abs(1000.0))
.scrollable(Axis::Y, Pin::Start) .scrollable(Axis::Y, Pin::Start)
.add_strong(&mut rsc); .add_strong(&mut rsc);
let scroll = scroll_strong.weak(); let scroll = scroll_strong.weak();
@@ -522,8 +522,8 @@ fn a_drag_pans_whichever_nested_scroll_area_owns_its_axis() {
let seen = Rc::new(Cell::new(None)); let seen = Rc::new(Cell::new(None));
let record = seen.clone(); let record = seen.clone();
let outer_strong = rect(PaintId::WHITE) let outer_strong = rect(PaintId::WHITE)
.width(Len::abs(1000.0)) .width(LayoutLen::abs(1000.0))
.height(Len::abs(1000.0)) .height(LayoutLen::abs(1000.0))
.scrollable(Axis::X, Pin::Start) .scrollable(Axis::X, Pin::Start)
.with_id(move |_rsc, id| { .with_id(move |_rsc, id| {
record.set(Some(id)); record.set(Some(id));
@@ -581,13 +581,13 @@ fn a_press_does_not_reach_a_widget_the_pointer_has_just_left() {
let half = |slot: &Rc<Cell<Option<WeakWidget<ScrollArea>>>>| { let half = |slot: &Rc<Cell<Option<WeakWidget<ScrollArea>>>>| {
let record = slot.clone(); let record = slot.clone();
rect(PaintId::WHITE) rect(PaintId::WHITE)
.height(Len::abs(1000.0)) .height(LayoutLen::abs(1000.0))
.scrollable(Axis::Y, Pin::Start) .scrollable(Axis::Y, Pin::Start)
.with_id(move |_rsc, id| { .with_id(move |_rsc, id| {
record.set(Some(id)); record.set(Some(id));
id id
}) })
.height(Len::rel(0.5)) .height(LayoutLen::rel(0.5))
}; };
let root = (half(&seen[0]), half(&seen[1])) let root = (half(&seen[0]), half(&seen[1]))
.span(Dir::DOWN) .span(Dir::DOWN)
+2 -2
View File
@@ -12,8 +12,8 @@ impl Widget for Image {
painter.set_size(Size::abs(size)); painter.set_size(Size::abs(size));
} }
fn size_hint(&self, axis: Axis) -> Option<Len> { fn size_hint(&self, axis: Axis) -> Option<LayoutLen> {
Some(Len::abs(self.handle.size().axis(axis))) Some(LayoutLen::abs(self.handle.size().axis(axis)))
} }
fn is_size_independent(&self) -> bool { fn is_size_independent(&self) -> bool {
+7 -7
View File
@@ -811,8 +811,8 @@ impl Widget for LazySpan {
painter.set_size(Size::REST); painter.set_size(Size::REST);
} }
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
Some(Len::REST) Some(LayoutLen::REST)
} }
} }
@@ -850,7 +850,7 @@ mod tests {
let sized = rsc.ui.widgets.add_strong(Sized { let sized = rsc.ui.widgets.add_strong(Sized {
inner: rect.any(), inner: rect.any(),
x: None, x: None,
y: Some(Len::abs(height)), y: Some(LayoutLen::abs(height)),
}); });
(sized.weak(), sized.any()) (sized.weak(), sized.any())
} }
@@ -1054,7 +1054,7 @@ mod tests {
let fg = rsc.ui.widgets.add_strong(Sized { let fg = rsc.ui.widgets.add_strong(Sized {
inner: fg_rect.any(), inner: fg_rect.any(),
x: None, x: None,
y: Some(Len::abs(height)), y: Some(LayoutLen::abs(height)),
}); });
let fg_weak = fg.weak(); let fg_weak = fg.weak();
let stack = Stack { let stack = Stack {
@@ -1084,7 +1084,7 @@ mod tests {
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
let (bg_id, fg) = rows[key_to_change as usize]; let (bg_id, fg) = rows[key_to_change as usize];
rsc.ui.widgets.get_mut(&fg).unwrap().y = Some(Len::abs(new_height)); rsc.ui.widgets.get_mut(&fg).unwrap().y = Some(LayoutLen::abs(new_height));
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
let px = render let px = render
@@ -1190,7 +1190,7 @@ mod tests {
} }
rsc.ui.widgets.get_mut(&list_weak).unwrap().note_tap(41.0); rsc.ui.widgets.get_mut(&list_weak).unwrap().note_tap(41.0);
rsc.ui.widgets.get_mut(&row2).unwrap().y = Some(Len::abs(50.0)); rsc.ui.widgets.get_mut(&row2).unwrap().y = Some(LayoutLen::abs(50.0));
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
@@ -1227,7 +1227,7 @@ mod tests {
let row2 = rows[2]; let row2 = rows[2];
rsc.ui.widgets.get_mut(&list_weak).unwrap().note_tap(59.0); rsc.ui.widgets.get_mut(&list_weak).unwrap().note_tap(59.0);
rsc.ui.widgets.get_mut(&row2).unwrap().y = Some(Len::abs(50.0)); rsc.ui.widgets.get_mut(&row2).unwrap().y = Some(LayoutLen::abs(50.0));
render.update(&root, &mut rsc); render.update(&root, &mut rsc);
let list_ref = rsc.ui.widgets.get(&list_weak).unwrap(); let list_ref = rsc.ui.widgets.get(&list_weak).unwrap();
+4 -4
View File
@@ -2,12 +2,12 @@ use crate::prelude::*;
pub struct MaxSize { pub struct MaxSize {
pub inner: StrongWidget, pub inner: StrongWidget,
pub x: Option<Len>, pub x: Option<LayoutLen>,
pub y: Option<Len>, pub y: Option<LayoutLen>,
} }
impl MaxSize { impl MaxSize {
fn clamp(len: Len, max: Option<Len>, output: f32, density: f32) -> Len { fn clamp(len: LayoutLen, max: Option<LayoutLen>, output: f32, density: f32) -> LayoutLen {
let Some(max) = max else { let Some(max) = max else {
return len; return len;
}; };
@@ -20,7 +20,7 @@ impl MaxSize {
} }
} }
fn clamp_region(offered_px: f32, max: Option<Len>, output: f32, density: f32) -> UiSpan { fn clamp_region(offered_px: f32, max: Option<LayoutLen>, output: f32, density: f32) -> UiSpan {
let Some(max) = max else { let Some(max) = max else {
return UiSpan::FULL; return UiSpan::FULL;
}; };
+75 -37
View File
@@ -10,16 +10,14 @@ impl Widget for Pad {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let density = painter.density(); let density = painter.density();
let offered = painter.px_size(); let offered = painter.px_size();
let region = self.padding.region(density); let used = painter
let used = painter.widget_within(&self.inner, region).size(); .widget_within(&self.inner, self.padding.fixed_region(density))
.size();
let region = self.padding.region_for(used, density);
painter.place_used(&self.inner, used, region); painter.place_used(&self.inner, used, region);
let width =
self.padding.left.apply_rest(density).abs + self.padding.right.apply_rest(density).abs;
let height =
self.padding.top.apply_rest(density).abs + self.padding.bottom.apply_rest(density).abs;
let size = Size { let size = Size {
x: used.x + Len::abs(width), x: self.padding.left.fold_dp(density) + used.x + self.padding.right.fold_dp(density),
y: used.y + Len::abs(height), y: self.padding.top.fold_dp(density) + used.y + self.padding.bottom.fold_dp(density),
}; };
let needed = size.to_uivec2(density).to_abs(painter.output_size()); let needed = size.to_uivec2(density).to_abs(painter.output_size());
if needed.x <= offered.x + 0.01 && needed.y <= offered.y + 0.01 { if needed.x <= offered.x + 0.01 && needed.y <= offered.y + 0.01 {
@@ -34,21 +32,21 @@ impl Widget for Pad {
} }
pub struct Padding { pub struct Padding {
pub left: Len, pub left: LayoutLen,
pub right: Len, pub right: LayoutLen,
pub top: Len, pub top: LayoutLen,
pub bottom: Len, pub bottom: LayoutLen,
} }
impl Padding { impl Padding {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
left: Len::ZERO, left: LayoutLen::ZERO,
right: Len::ZERO, right: LayoutLen::ZERO,
top: Len::ZERO, top: LayoutLen::ZERO,
bottom: Len::ZERO, bottom: LayoutLen::ZERO,
}; };
pub fn uniform(amt: impl Into<Len>) -> Self { pub fn uniform(amt: impl Into<LayoutLen>) -> Self {
let amt = amt.into(); let amt = amt.into();
Self { Self {
left: amt, left: amt,
@@ -57,79 +55,119 @@ impl Padding {
bottom: amt, bottom: amt,
} }
} }
pub fn region(&self, density: f32) -> UiRegion { fn fixed_region(&self, density: f32) -> UiRegion {
let mut region = UiRegion::FULL; let mut region = UiRegion::FULL;
region.x.start.abs += self.left.apply_rest(density).abs; let left = self.left.fold_dp(density);
region.y.start.abs += self.top.apply_rest(density).abs; let right = self.right.fold_dp(density);
region.x.end.abs -= self.right.apply_rest(density).abs; let top = self.top.fold_dp(density);
region.y.end.abs -= self.bottom.apply_rest(density).abs; let bottom = self.bottom.fold_dp(density);
region.x.start += UiScalar::new(left.rel, left.abs);
region.x.end -= UiScalar::new(right.rel, right.abs);
region.y.start += UiScalar::new(top.rel, top.abs);
region.y.end -= UiScalar::new(bottom.rel, bottom.abs);
region region
} }
pub fn x(amt: impl Into<Len>) -> Self {
fn region_for(&self, inner: Size, density: f32) -> UiRegion {
UiRegion {
x: padded_axis(self.left, inner.x, self.right, density),
y: padded_axis(self.top, inner.y, self.bottom, density),
}
}
pub fn x(amt: impl Into<LayoutLen>) -> Self {
let amt = amt.into(); let amt = amt.into();
Self { Self {
left: amt, left: amt,
right: amt, right: amt,
top: Len::ZERO, top: LayoutLen::ZERO,
bottom: Len::ZERO, bottom: LayoutLen::ZERO,
} }
} }
pub fn y(amt: impl Into<Len>) -> Self { pub fn y(amt: impl Into<LayoutLen>) -> Self {
let amt = amt.into(); let amt = amt.into();
Self { Self {
left: Len::ZERO, left: LayoutLen::ZERO,
right: Len::ZERO, right: LayoutLen::ZERO,
top: amt, top: amt,
bottom: amt, bottom: amt,
} }
} }
pub fn top(amt: impl Into<Len>) -> Self { pub fn top(amt: impl Into<LayoutLen>) -> Self {
let mut s = Self::ZERO; let mut s = Self::ZERO;
s.top = amt.into(); s.top = amt.into();
s s
} }
pub fn bottom(amt: impl Into<Len>) -> Self { pub fn bottom(amt: impl Into<LayoutLen>) -> Self {
let mut s = Self::ZERO; let mut s = Self::ZERO;
s.bottom = amt.into(); s.bottom = amt.into();
s s
} }
pub fn left(amt: impl Into<Len>) -> Self { pub fn left(amt: impl Into<LayoutLen>) -> Self {
let mut s = Self::ZERO; let mut s = Self::ZERO;
s.left = amt.into(); s.left = amt.into();
s s
} }
pub fn right(amt: impl Into<Len>) -> Self { pub fn right(amt: impl Into<LayoutLen>) -> Self {
let mut s = Self::ZERO; let mut s = Self::ZERO;
s.right = amt.into(); s.right = amt.into();
s s
} }
pub fn with_top(mut self, amt: impl Into<Len>) -> Self { pub fn with_top(mut self, amt: impl Into<LayoutLen>) -> Self {
self.top = amt.into(); self.top = amt.into();
self self
} }
pub fn with_bottom(mut self, amt: impl Into<Len>) -> Self { pub fn with_bottom(mut self, amt: impl Into<LayoutLen>) -> Self {
self.bottom = amt.into(); self.bottom = amt.into();
self self
} }
pub fn with_left(mut self, amt: impl Into<Len>) -> Self { pub fn with_left(mut self, amt: impl Into<LayoutLen>) -> Self {
self.left = amt.into(); self.left = amt.into();
self self
} }
pub fn with_right(mut self, amt: impl Into<Len>) -> Self { pub fn with_right(mut self, amt: impl Into<LayoutLen>) -> Self {
self.right = amt.into(); self.right = amt.into();
self self
} }
} }
impl<T: Into<Len>> From<T> for Padding { fn padded_axis(before: LayoutLen, inner: LayoutLen, after: LayoutLen, density: f32) -> UiSpan {
let lengths = [
before.fold_dp(density),
inner.fold_dp(density),
after.fold_dp(density),
];
let total = lengths
.iter()
.copied()
.fold(LayoutLen::ZERO, |sum, len| sum + len);
let fixed = UiScalar::new(total.rel, total.abs);
let mut cursor = UiScalar::ZERO;
let mut inner_span = UiSpan::FULL;
for (index, len) in lengths.into_iter().enumerate() {
let start = cursor;
if len.rest > 0.0 {
let rest_end = UiScalar::rel(len.rest / total.rest);
let available_end = (UiScalar::FULL + cursor) - fixed;
cursor = rest_end.within(&cursor.to(available_end));
}
cursor.rel += len.rel;
cursor.abs += len.abs;
if index == 1 {
inner_span = start.to(cursor);
}
}
inner_span
}
impl<T: Into<LayoutLen>> From<T> for Padding {
fn from(amt: T) -> Self { fn from(amt: T) -> Self {
Self::uniform(amt.into()) Self::uniform(amt.into())
} }
+1 -1
View File
@@ -97,7 +97,7 @@ mod tests {
let mut rsc = TestRsc { ui: Ui::default() }; let mut rsc = TestRsc { ui: Ui::default() };
let fill = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)).any(); let fill = rsc.ui.widgets.add_strong(Rect::new(PaintId::WHITE)).any();
let id = fill.id(); let id = fill.id();
let long = Some(Len::abs(1000.0)); let long = Some(LayoutLen::abs(1000.0));
let tall = rsc.ui.widgets.add_strong(Sized { let tall = rsc.ui.widgets.add_strong(Sized {
inner: fill, inner: fill,
x: (axis == Axis::X).then_some(long).flatten(), x: (axis == Axis::X).then_some(long).flatten(),
+3 -3
View File
@@ -2,8 +2,8 @@ use crate::prelude::*;
pub struct Sized { pub struct Sized {
pub inner: StrongWidget, pub inner: StrongWidget,
pub x: Option<Len>, pub x: Option<LayoutLen>,
pub y: Option<Len>, pub y: Option<LayoutLen>,
} }
impl Widget for Sized { impl Widget for Sized {
@@ -25,7 +25,7 @@ impl Widget for Sized {
painter.set_size(size); painter.set_size(size);
} }
fn size_hint(&self, axis: Axis) -> Option<Len> { fn size_hint(&self, axis: Axis) -> Option<LayoutLen> {
match axis { match axis {
Axis::X => self.x, Axis::X => self.x,
Axis::Y => self.y, Axis::Y => self.y,
+12 -12
View File
@@ -4,7 +4,7 @@ use std::marker::PhantomData;
pub struct Span { pub struct Span {
pub children: Vec<StrongWidget>, pub children: Vec<StrongWidget>,
pub dir: Dir, pub dir: Dir,
pub gap: Len, pub gap: LayoutLen,
} }
impl Widget for Span { impl Widget for Span {
@@ -16,7 +16,7 @@ impl Widget for Span {
let axis = self.dir.axis; let axis = self.dir.axis;
let gap = self.gap.apply_rest(painter.density()).abs; let gap = self.gap.apply_rest(painter.density()).abs;
let mut lens: Vec<Option<Len>> = self let mut lens: Vec<Option<LayoutLen>> = self
.children .children
.iter() .iter()
.map(|child| painter.known_len(child, axis)) .map(|child| painter.known_len(child, axis))
@@ -43,13 +43,13 @@ impl Widget for Span {
cursor.rel += len.rel; cursor.rel += len.rel;
} }
let lens: Vec<Len> = lens.into_iter().map(Option::unwrap).collect(); let lens: Vec<LayoutLen> = lens.into_iter().map(Option::unwrap).collect();
let gap_total = gap * self.children.len().saturating_sub(1) as f32; let gap_total = gap * self.children.len().saturating_sub(1) as f32;
let total = lens.iter().fold(Len::abs(gap_total), |s, &l| s + l); let total = lens.iter().fold(LayoutLen::abs(gap_total), |s, &l| s + l);
let mut start = UiScalar::rel_min(); let mut start = UiScalar::rel_min();
let mut ortho_len = Len::ZERO; let mut ortho_len = LayoutLen::ZERO;
let mut ortho_mixed = false; let mut ortho_mixed = false;
let mut placed = Vec::with_capacity(self.children.len()); let mut placed = Vec::with_capacity(self.children.len());
for (i, (child, &len)) in self.children.iter().zip(&lens).enumerate() { for (i, (child, &len)) in self.children.iter().zip(&lens).enumerate() {
@@ -84,7 +84,7 @@ impl Widget for Span {
} }
} }
if ortho_mixed { if ortho_mixed {
ortho_len = Len::default(); ortho_len = LayoutLen::default();
} else { } else {
let ortho = ortho_len let ortho = ortho_len
.apply_rest(painter.density()) .apply_rest(painter.density())
@@ -98,7 +98,7 @@ impl Widget for Span {
let along = if total.rest == 0.0 && total.rel == 0.0 { let along = if total.rest == 0.0 && total.rel == 0.0 {
total total
} else { } else {
Len::default() LayoutLen::default()
}; };
painter.set_size(Size::from_axis(axis, along, ortho_len)); painter.set_size(Size::from_axis(axis, along, ortho_len));
@@ -110,11 +110,11 @@ impl Span {
Self { Self {
children: Vec::new(), children: Vec::new(),
dir, dir,
gap: Len::ZERO, gap: LayoutLen::ZERO,
} }
} }
pub fn gap(mut self, gap: impl Into<Len>) -> Self { pub fn gap(mut self, gap: impl Into<LayoutLen>) -> Self {
self.gap = gap.into(); self.gap = gap.into();
self self
} }
@@ -131,7 +131,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: Len, pub gap: LayoutLen,
_pd: PhantomData<(State, Tag)>, _pd: PhantomData<(State, Tag)>,
} }
@@ -157,12 +157,12 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
Self { Self {
children, children,
dir, dir,
gap: Len::ZERO, gap: LayoutLen::ZERO,
_pd: PhantomData, _pd: PhantomData,
} }
} }
pub fn gap(mut self, gap: impl Into<Len>) -> Self { pub fn gap(mut self, gap: impl Into<LayoutLen>) -> Self {
self.gap = gap.into(); self.gap = gap.into();
self self
} }
+2 -2
View File
@@ -53,9 +53,9 @@ impl Widget for Stack {
painter.set_size(size); painter.set_size(size);
} }
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
match self.size { match self.size {
StackSize::Default => Some(Len::REST), StackSize::Default => Some(LayoutLen::REST),
StackSize::Child(_) => None, StackSize::Child(_) => None,
} }
} }
+2 -2
View File
@@ -49,8 +49,8 @@ impl Widget for Rect {
painter.set_size(Size::REST); painter.set_size(Size::REST);
} }
fn size_hint(&self, _axis: Axis) -> Option<Len> { fn size_hint(&self, _axis: Axis) -> Option<LayoutLen> {
Some(Len::REST) Some(LayoutLen::REST)
} }
} }
+4 -4
View File
@@ -41,7 +41,7 @@ widget_trait! {
} }
} }
fn max_width(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, MaxSize> { fn max_width(self, len: impl Into<LayoutLen>) -> impl WidgetFn<Rsc, MaxSize> {
let len = len.into(); let len = len.into();
move |state| MaxSize { move |state| MaxSize {
inner: self.add_strong(state), inner: self.add_strong(state),
@@ -50,7 +50,7 @@ widget_trait! {
} }
} }
fn max_height(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, MaxSize> { fn max_height(self, len: impl Into<LayoutLen>) -> impl WidgetFn<Rsc, MaxSize> {
let len = len.into(); let len = len.into();
move |state| MaxSize { move |state| MaxSize {
inner: self.add_strong(state), inner: self.add_strong(state),
@@ -59,7 +59,7 @@ widget_trait! {
} }
} }
fn width(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, Sized> { fn width(self, len: impl Into<LayoutLen>) -> impl WidgetFn<Rsc, Sized> {
let len = len.into(); let len = len.into();
move |state| Sized { move |state| Sized {
inner: self.add_strong(state), inner: self.add_strong(state),
@@ -68,7 +68,7 @@ widget_trait! {
} }
} }
fn height(self, len: impl Into<Len>) -> impl WidgetFn<Rsc, Sized> { fn height(self, len: impl Into<LayoutLen>) -> impl WidgetFn<Rsc, Sized> {
let len = len.into(); let len = len.into();
move |state| Sized { move |state| Sized {
inner: self.add_strong(state), inner: self.add_strong(state),