Add explicit orthogonal span sizing
This commit is contained in:
1 parent
29c7881c8a
commit
f437495309
7 files changed
+108
-8
No files matched your search
@@ -308,6 +308,12 @@ impl<Rsc: UiRsc + 'static> Grow<'_, Rsc> {
|
|||||||
children,
|
children,
|
||||||
dir,
|
dir,
|
||||||
gap: self.rng.below(3) as f32 * 4.0,
|
gap: self.rng.below(3) as f32 * 4.0,
|
||||||
|
// Derive this from an existing choice: a seed must keep growing
|
||||||
|
// the same tree when the generator gains another configuration.
|
||||||
|
ortho: match dir.axis {
|
||||||
|
Axis::X => OrthoSize::Full,
|
||||||
|
Axis::Y => OrthoSize::Children,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
.add(self.rsc);
|
.add(self.rsc);
|
||||||
self.tree.ids.push(id.id());
|
self.tree.ids.push(id.id());
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum OrthoSize {
|
||||||
|
/// Reports one full relative length across the span's orthogonal axis,
|
||||||
|
/// whether or not its siblings also take space.
|
||||||
|
Full,
|
||||||
|
/// Reports its longest fixed child, or leftover space if any child scales.
|
||||||
|
#[default]
|
||||||
|
Children,
|
||||||
|
}
|
||||||
|
|
||||||
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: f32,
|
||||||
|
pub ortho: OrthoSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Span {
|
impl Widget for Span {
|
||||||
@@ -82,12 +93,18 @@ impl Widget for Span {
|
|||||||
if self.dir.sign == Sign::Neg {
|
if self.dir.sign == Sign::Neg {
|
||||||
region.flip(axis);
|
region.flip(axis);
|
||||||
}
|
}
|
||||||
let used = painter.place(child, region).size().axis(!axis);
|
let placed = painter.place(child, region);
|
||||||
// TODO: rel shouldn't do this, but no easy way before actually calculating pixels
|
if self.ortho == OrthoSize::Children {
|
||||||
if used.rel > 0.0 || used.leftover > 0.0 {
|
let used = placed.len(!axis);
|
||||||
ortho = Len::LEFTOVER;
|
// Choosing between a fixed and a relative length from the
|
||||||
} else if ortho.leftover == 0.0 {
|
// span's own eventual width admits multiple fixed points.
|
||||||
ortho.px = ortho.px.max(used.px);
|
// 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 {
|
||||||
|
ortho = Len::LEFTOVER;
|
||||||
|
} else if ortho.leftover == 0.0 {
|
||||||
|
ortho.px = ortho.px.max(used.px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
start.px += self.gap;
|
start.px += self.gap;
|
||||||
}
|
}
|
||||||
@@ -100,6 +117,10 @@ impl Widget for Span {
|
|||||||
// not give. Resolution happens at the nearest ancestor with a length,
|
// not give. Resolution happens at the nearest ancestor with a length,
|
||||||
// and the root always has one.
|
// and the root always has one.
|
||||||
let along = total;
|
let along = total;
|
||||||
|
let ortho = match self.ortho {
|
||||||
|
OrthoSize::Full => Len::rel(1.0),
|
||||||
|
OrthoSize::Children => ortho,
|
||||||
|
};
|
||||||
Size::from_axis(axis, along, ortho)
|
Size::from_axis(axis, along, ortho)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,6 +131,7 @@ impl Span {
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
dir,
|
dir,
|
||||||
gap: 0.0,
|
gap: 0.0,
|
||||||
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +140,11 @@ impl Span {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ortho(mut self, ortho: OrthoSize) -> Self {
|
||||||
|
self.ortho = ortho;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, w: StrongWidget) {
|
pub fn push(&mut self, w: StrongWidget) {
|
||||||
self.children.push(w);
|
self.children.push(w);
|
||||||
}
|
}
|
||||||
@@ -131,6 +158,7 @@ pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Ta
|
|||||||
pub children: Wa,
|
pub children: Wa,
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
pub gap: f32,
|
pub gap: f32,
|
||||||
|
pub ortho: OrthoSize,
|
||||||
_pd: PhantomData<(State, Tag)>,
|
_pd: PhantomData<(State, Tag)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +173,7 @@ impl<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> WidgetFnTrait
|
|||||||
children: self.children.add(rsc).arr.into_iter().collect(),
|
children: self.children.add(rsc).arr.into_iter().collect(),
|
||||||
dir: self.dir,
|
dir: self.dir,
|
||||||
gap: self.gap,
|
gap: self.gap,
|
||||||
|
ortho: self.ortho,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,6 +186,7 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
|||||||
children,
|
children,
|
||||||
dir,
|
dir,
|
||||||
gap: 0.0,
|
gap: 0.0,
|
||||||
|
ortho: OrthoSize::Children,
|
||||||
_pd: PhantomData,
|
_pd: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +195,11 @@ impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
|||||||
self.gap = gap.to_f32();
|
self.gap = gap.to_f32();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ortho(mut self, ortho: OrthoSize) -> Self {
|
||||||
|
self.ortho = ortho;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for Span {
|
impl std::ops::Deref for Span {
|
||||||
|
|||||||
+2
-1
@@ -190,9 +190,10 @@ fn describe(id: WidgetId, h: &Harness) -> String {
|
|||||||
if let Some(w) = any.downcast_ref::<Span>() {
|
if let Some(w) = any.downcast_ref::<Span>() {
|
||||||
let sign = if w.dir.sign == Sign::Neg { "-" } else { "+" };
|
let sign = if w.dir.sign == Sign::Neg { "-" } else { "+" };
|
||||||
return format!(
|
return format!(
|
||||||
"Span{{dir:{:?}{sign},gap:{},n:{}}}",
|
"Span{{dir:{:?}{sign},gap:{},ortho:{:?},n:{}}}",
|
||||||
w.dir.axis,
|
w.dir.axis,
|
||||||
w.gap,
|
w.gap,
|
||||||
|
w.ortho,
|
||||||
w.children.len()
|
w.children.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-1
@@ -20,6 +20,33 @@ fn a_span_gives_each_child_the_width_it_asked_for() {
|
|||||||
assert_corners!(h, right, (100, 0), (400, 200));
|
assert_corners!(h, right, (100, 0), (400, 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_full_ortho_span_reports_relative_full() {
|
||||||
|
let mut h = Harness::new((400, 200));
|
||||||
|
let child = rect(Color::RED).height(40).add(&mut h.rsc);
|
||||||
|
let span = (child,)
|
||||||
|
.span(Dir::RIGHT)
|
||||||
|
.ortho(OrthoSize::Full)
|
||||||
|
.add(&mut h.rsc);
|
||||||
|
h.set_root(span);
|
||||||
|
|
||||||
|
assert_eq!(h.render.active[&span.id()].size.y, Len::rel(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_children_ortho_span_reports_its_tallest_fixed_child() {
|
||||||
|
let mut h = Harness::new((400, 200));
|
||||||
|
let short = rect(Color::RED).height(40).add(&mut h.rsc);
|
||||||
|
let tall = rect(Color::BLUE).height(70).add(&mut h.rsc);
|
||||||
|
let span = (short, tall)
|
||||||
|
.span(Dir::RIGHT)
|
||||||
|
.ortho(OrthoSize::Children)
|
||||||
|
.add(&mut h.rsc);
|
||||||
|
h.set_root(span);
|
||||||
|
|
||||||
|
assert_eq!(h.render.active[&span.id()].size.y, Len::px(70.0));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resizing_relays_out_against_the_new_output() {
|
fn resizing_relays_out_against_the_new_output() {
|
||||||
let mut h = Harness::new((400, 200));
|
let mut h = Harness::new((400, 200));
|
||||||
@@ -158,7 +185,12 @@ fn a_box_with_a_fixed_length_can_be_stretched_on_its_other_axis() {
|
|||||||
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
||||||
let row = inner.pad(10).height(40).add(&mut h.rsc);
|
let row = inner.pad(10).height(40).add(&mut h.rsc);
|
||||||
let filler = rect(Color::GREEN).add(&mut h.rsc);
|
let filler = rect(Color::GREEN).add(&mut h.rsc);
|
||||||
let column = (row, filler).span(Dir::DOWN).add(&mut h.rsc);
|
// This column is an item in a row, so it takes the width left for it
|
||||||
|
// rather than asking for a full row-width in addition to the bar.
|
||||||
|
let column = (row, filler)
|
||||||
|
.span(Dir::DOWN)
|
||||||
|
.ortho(OrthoSize::Children)
|
||||||
|
.add(&mut h.rsc);
|
||||||
let bar = rect(Color::RED).width(100).add(&mut h.rsc);
|
let bar = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||||
h.set_root((bar, column).span(Dir::RIGHT));
|
h.set_root((bar, column).span(Dir::RIGHT));
|
||||||
assert_corners!(h, inner, (110, 10), (390, 30));
|
assert_corners!(h, inner, (110, 10), (390, 30));
|
||||||
|
|||||||
@@ -264,6 +264,25 @@ fn a_resize_does_not_redraw_what_the_shader_can_move() {
|
|||||||
assert_corners!(h, leaf, (0, 0), (800, 100));
|
assert_corners!(h, leaf, (0, 0), (800, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_full_ortho_span_moves_its_child_without_redrawing_it() {
|
||||||
|
let mut h = Harness::new((400, 200));
|
||||||
|
let (leaf, draws) = counted(&mut h, Size::LEFTOVER, false);
|
||||||
|
let span = (leaf,)
|
||||||
|
.span(Dir::RIGHT)
|
||||||
|
.ortho(OrthoSize::Full)
|
||||||
|
.add(&mut h.rsc);
|
||||||
|
h.set_root(span);
|
||||||
|
let settled = draws.get();
|
||||||
|
|
||||||
|
h.resize((400, 100));
|
||||||
|
h.frame();
|
||||||
|
|
||||||
|
assert_eq!(draws.get(), settled);
|
||||||
|
assert_corners!(h, leaf, (0, 0), (400, 100));
|
||||||
|
assert_eq!(h.render.active[&span.id()].size.y, Len::rel(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
/// The output is the root of the box chain, so a resize is a box that changed
|
/// The output is the root of the box chain, so a resize is a box that changed
|
||||||
/// length like any other -- there is not a second rule for the window. A
|
/// length like any other -- there is not a second rule for the window. A
|
||||||
/// drawing that holds for one length is drawn again whichever box moved.
|
/// drawing that holds for one length is drawn again whichever box moved.
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ impl Node {
|
|||||||
children,
|
children,
|
||||||
dir: dir(*down),
|
dir: dir(*down),
|
||||||
gap: *gap,
|
gap: *gap,
|
||||||
|
ortho: match down {
|
||||||
|
true => OrthoSize::Children,
|
||||||
|
false => OrthoSize::Full,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
spans.push(handle);
|
spans.push(handle);
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ fn plant_pair(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, WeakWidget<Span
|
|||||||
children,
|
children,
|
||||||
dir: Dir::RIGHT,
|
dir: Dir::RIGHT,
|
||||||
gap: 0.0,
|
gap: 0.0,
|
||||||
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
let span_handle = span;
|
let span_handle = span;
|
||||||
@@ -230,6 +231,7 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
|
|||||||
children: inner_children,
|
children: inner_children,
|
||||||
dir: Dir::RIGHT,
|
dir: Dir::RIGHT,
|
||||||
gap: 0.0,
|
gap: 0.0,
|
||||||
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
let block = rect(Color::RED).add(&mut h.rsc);
|
let block = rect(Color::RED).add(&mut h.rsc);
|
||||||
@@ -248,6 +250,7 @@ fn plant_scrolled(h: &mut Harness, swapped: bool) -> (Vec<WidgetId>, [WeakWidget
|
|||||||
children: outer_children,
|
children: outer_children,
|
||||||
dir: Dir::RIGHT,
|
dir: Dir::RIGHT,
|
||||||
gap: 0.0,
|
gap: 0.0,
|
||||||
|
ortho: OrthoSize::Children,
|
||||||
}
|
}
|
||||||
.add(&mut h.rsc);
|
.add(&mut h.rsc);
|
||||||
let through = SetSize {
|
let through = SetSize {
|
||||||
|
|||||||
Reference in new issue
Block a user