Make alignment a widget property

This commit is contained in:
iris-ai committed 2026-09-15 23:12:16 -04:00
1 parent 8220a78d4a
commit d3b0ebf90c
21 files changed
+823 -300

No files matched your search

+92 -21
View File
@@ -76,9 +76,9 @@ enum Node {
fn axis_align(v: u8) -> Option<AxisAlign> {
match v % 4 {
0 => None,
1 => Some(AxisAlign::Neg),
2 => Some(AxisAlign::Center),
_ => Some(AxisAlign::Pos),
1 => Some(AxisAlign::NEG),
2 => Some(AxisAlign::CENTER),
_ => Some(AxisAlign::POS),
}
}
@@ -94,6 +94,7 @@ impl Node {
h: &mut Harness,
out: &mut Vec<WidgetId>,
spans: &mut Vec<WeakWidget<Span>>,
sized: &mut Vec<WidgetId>,
) -> StrongWidget {
let id: StrongWidget = match self {
Node::Text(words, wrap) => {
@@ -106,7 +107,10 @@ impl Node {
Node::OneLine => wtext(ONE_LINE).size(16).wrap(false).add_strong(&mut h.rsc),
Node::Rect => rect(Color::RED).add_strong(&mut h.rsc),
Node::Span(down, gap, kids, order) => {
let mut built: Vec<_> = kids.iter().map(|k| Some(k.build(h, out, spans))).collect();
let mut built: Vec<_> = kids
.iter()
.map(|k| Some(k.build(h, out, spans, sized)))
.collect();
// `order` is a permutation, so each is taken exactly once.
let children = order
.iter()
@@ -126,7 +130,7 @@ impl Node {
handle.add_strong(&mut h.rsc)
}
Node::Stack(kids) => {
let children = kids.iter().map(|k| k.build(h, out, spans)).collect();
let children = kids.iter().map(|k| k.build(h, out, spans, sized)).collect();
Stack {
children,
size: StackSize::Child(0),
@@ -134,7 +138,7 @@ impl Node {
.add_strong(&mut h.rsc)
}
Node::Pad(p, kid) => {
let inner = kid.build(h, out, spans);
let inner = kid.build(h, out, spans, sized);
Pad {
padding: Padding {
left: *p,
@@ -147,30 +151,29 @@ impl Node {
.add_strong(&mut h.rsc)
}
Node::Aligned(x, y, kid) => {
let inner = kid.build(h, out, spans);
Aligned {
inner,
align: Align {
x: axis_align(*x),
y: axis_align(*y),
},
let inner = kid.build(h, out, spans, sized);
for (axis, align) in [(Axis::X, axis_align(*x)), (Axis::Y, axis_align(*y))] {
if let Some(align) = align {
h.rsc.widgets_mut().set_alignment(&inner, axis, align);
}
}
.add_strong(&mut h.rsc)
inner
}
Node::Sized(x, y, kid) => {
let inner = kid.build(h, out, spans);
let inner = kid.build(h, out, spans, sized);
h.rsc.widgets_mut().set_size_rules(&inner, *x, *y);
sized.push(inner.id());
inner
}
Node::Scroll(down, kid) => {
let inner = kid.build(h, out, spans);
let inner = kid.build(h, out, spans, sized);
let axis = if *down { Axis::Y } else { Axis::X };
Scroll::new(inner, axis).add_strong(&mut h.rsc)
}
Node::Branch(probe, a, b, at) => {
let probe = probe.build(h, out, spans);
let wide = a.build(h, out, spans);
let narrow = b.build(h, out, spans);
let probe = probe.build(h, out, spans, sized);
let wide = a.build(h, out, spans, sized);
let narrow = b.build(h, out, spans, sized);
Branch {
probe,
wide,
@@ -184,6 +187,27 @@ impl Node {
id
}
/// The lengths every `Sized` node would carry after `resized`, in the
/// order `build` pushes them.
fn sized_lens(&self, out: &mut Vec<(Option<Len>, Option<Len>)>) {
match self {
Node::Text(..) | Node::OneLine | Node::Rect => {}
Node::Span(_, _, kids, _) | Node::Stack(kids) => {
kids.iter().for_each(|k| k.sized_lens(out));
}
Node::Pad(_, k) | Node::Aligned(_, _, k) | Node::Scroll(_, k) => k.sized_lens(out),
Node::Sized(x, y, k) => {
k.sized_lens(out);
out.push((resized_len(*x), resized_len(*y)));
}
Node::Branch(p, a, b, _) => {
p.sized_lens(out);
a.sized_lens(out);
b.sized_lens(out);
}
}
}
fn size(&self) -> usize {
1 + match self {
Node::Text(..) | Node::OneLine | Node::Rect => 0,
@@ -377,6 +401,41 @@ enum Case {
Repaint,
ResizeRepaint,
Reorder,
SizeChange,
}
/// A different declared length, kept the same kind so the change is to the
/// value alone.
fn resized_len(len: Option<Len>) -> Option<Len> {
len.map(|len| Len {
px: len.px * 0.5 + 13.0,
rel: len.rel * 0.5,
leftover: len.leftover,
})
}
/// Every declared size changed, as a tree rather than as a change.
fn resized(node: &Node) -> Node {
match node {
Node::Span(down, gap, kids, order) => Node::Span(
*down,
*gap,
kids.iter().map(resized).collect(),
order.clone(),
),
Node::Stack(kids) => Node::Stack(kids.iter().map(resized).collect()),
Node::Pad(p, k) => Node::Pad(*p, Box::new(resized(k))),
Node::Aligned(x, y, k) => Node::Aligned(*x, *y, Box::new(resized(k))),
Node::Sized(x, y, k) => Node::Sized(resized_len(*x), resized_len(*y), Box::new(resized(k))),
Node::Scroll(d, k) => Node::Scroll(*d, Box::new(resized(k))),
Node::Branch(p, a, b, at) => Node::Branch(
Box::new(resized(p)),
Box::new(resized(a)),
Box::new(resized(b)),
*at,
),
leaf => leaf.clone(),
}
}
/// Every span's children rotated by one, as a tree rather than as a change:
@@ -413,7 +472,8 @@ fn diverges(node: &Node, case: Case) -> Option<String> {
let mut warm = Harness::new(start);
let mut warm_ids = Vec::new();
let mut warm_spans = Vec::new();
let root = node.build(&mut warm, &mut warm_ids, &mut warm_spans);
let mut warm_sized = Vec::new();
let root = node.build(&mut warm, &mut warm_ids, &mut warm_spans, &mut warm_sized);
warm.state.root = Some(root);
// The frame that makes it warm: without it there is nothing retained and
// the comparison is two cold starts agreeing with each other.
@@ -434,16 +494,26 @@ fn diverges(node: &Node, case: Case) -> Option<String> {
}
warm.frame();
}
if case == Case::SizeChange {
let mut lens = Vec::new();
node.sized_lens(&mut lens);
for (id, (x, y)) in warm_sized.iter().zip(lens) {
warm.rsc.widgets_mut().set_size_rules(*id, x, y);
}
warm.frame();
}
// What the warm tree was moved into, grown that way from the start.
let want = match case {
Case::Reorder => reordered(node),
Case::SizeChange => resized(node),
_ => node.clone(),
};
let mut cold = Harness::new(INNER);
let mut cold_ids = Vec::new();
let mut cold_spans = Vec::new();
let root = want.build(&mut cold, &mut cold_ids, &mut cold_spans);
let mut cold_sized = Vec::new();
let root = want.build(&mut cold, &mut cold_ids, &mut cold_spans, &mut cold_sized);
cold.state.root = Some(root);
cold.frame();
@@ -497,6 +567,7 @@ fn no_grown_tree_lays_out_differently_warm_than_cold() {
"repaint" => Case::Repaint,
"resize-repaint" => Case::ResizeRepaint,
"reorder" => Case::Reorder,
"size-change" => Case::SizeChange,
_ => Case::Resize,
};