382 lines
12 KiB
Rust
382 lines
12 KiB
Rust
use std::{cell::Cell, rc::Rc};
|
|
|
|
use iris::harness::{Harness, assert_corners};
|
|
use iris::prelude::*;
|
|
|
|
struct Counted {
|
|
draws: Rc<Cell<usize>>,
|
|
}
|
|
|
|
impl Widget for Counted {
|
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
|
self.draws.set(self.draws.get() + 1);
|
|
painter.px_size();
|
|
painter.primitive(RectPrimitive::color(Color::RED));
|
|
Size::LEFTOVER
|
|
}
|
|
|
|
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
|
Some(LayoutLen::LEFTOVER)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_capped_share_returns_room_to_its_sibling() {
|
|
let mut h = Harness::new((300, 100));
|
|
let first = rect(Color::RED).max_width(80).add(&mut h.rsc);
|
|
let second = rect(Color::BLUE).add(&mut h.rsc);
|
|
h.set_root((first, second).span(Dir::RIGHT));
|
|
assert_corners!(h, first, (0, 0), (80, 100));
|
|
assert_corners!(h, second, (80, 0), (300, 100));
|
|
h.resize((100, 100));
|
|
h.frame();
|
|
assert_corners!(h, first, (0, 0), (50, 100));
|
|
assert_corners!(h, second, (50, 0), (100, 100));
|
|
h.resize((300, 100));
|
|
h.frame();
|
|
assert_corners!(h, first, (0, 0), (80, 100));
|
|
assert_corners!(h, second, (80, 0), (300, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn nested_shares_are_discovered_without_provisional_paint() {
|
|
let mut h = Harness::new((400, 100));
|
|
let draws = Rc::new(Cell::new(0));
|
|
let leaf = h.rsc.ui_mut().widgets.add_strong(Counted {
|
|
draws: draws.clone(),
|
|
});
|
|
let leaf_id = leaf.id();
|
|
let mut inner: StrongWidget = leaf;
|
|
for _ in 0..8 {
|
|
let sibling = rect(Color::BLUE).add_strong(&mut h.rsc);
|
|
inner = h.rsc.ui_mut().widgets.add_strong(Span {
|
|
children: vec![inner, sibling],
|
|
dir: Dir::RIGHT,
|
|
gap: Px::ZERO,
|
|
});
|
|
}
|
|
h.state.root = Some(inner);
|
|
h.frame();
|
|
assert_eq!(draws.get(), 1);
|
|
assert!(h.region(&leaf_id).is_some());
|
|
h.resize((800, 100));
|
|
h.frame();
|
|
assert_eq!(draws.get(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn nested_bounds_are_resolved_in_the_outer_allocation() {
|
|
let mut h = Harness::new((300, 100));
|
|
let a = rect(Color::RED).max_width(40).add(&mut h.rsc);
|
|
let b = rect(Color::GREEN).max_width(60).add(&mut h.rsc);
|
|
let inner = (a, b).span(Dir::RIGHT).add(&mut h.rsc);
|
|
let tail = rect(Color::BLUE).add(&mut h.rsc);
|
|
h.set_root((inner, tail).span(Dir::RIGHT));
|
|
assert_corners!(h, a, (0, 0), (40, 100));
|
|
assert_corners!(h, b, (40, 0), (100, 100));
|
|
assert_corners!(h, tail, (100, 0), (300, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn request_edits_in_a_nested_child_reach_the_allocator() {
|
|
let mut h = Harness::new((300, 100));
|
|
let a = rect(Color::RED).max_width(80).add(&mut h.rsc);
|
|
let inner = (a,).span(Dir::RIGHT).add(&mut h.rsc);
|
|
let tail = rect(Color::BLUE).add(&mut h.rsc);
|
|
h.set_root((inner, tail).span(Dir::RIGHT));
|
|
h.rsc.ui_mut().widgets.get_mut(&a).unwrap().x = Some(Len::px(40.0));
|
|
h.frame();
|
|
assert_corners!(h, a, (0, 0), (40, 100));
|
|
assert_corners!(h, tail, (40, 0), (300, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn adding_a_bound_to_a_previously_unbounded_share_reallocates_the_row() {
|
|
let mut h = Harness::new((300, 100));
|
|
let a = rect(Color::RED).add(&mut h.rsc);
|
|
let b = rect(Color::BLUE).add(&mut h.rsc);
|
|
h.set_root((a, b).span(Dir::RIGHT));
|
|
h.resize((400, 100));
|
|
h.frame();
|
|
h.rsc
|
|
.widgets_mut()
|
|
.set_size_rule(a, Axis::X, SizeRule::Max(Len::px(80.0)));
|
|
h.frame();
|
|
assert_corners!(h, a, (0, 0), (80, 100));
|
|
assert_corners!(h, b, (80, 0), (400, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn a_deferred_comparison_can_compare_two_different_weights() {
|
|
let a = SizeRequest::from(leftover(1.0) + px(30.0)).min(leftover(2.0));
|
|
let b = SizeRequest::from(leftover(1.0)).clamp(px(20.0), px(100.0));
|
|
let mut h = Harness::new((60, 100));
|
|
let a = rect(Color::RED).width(a).add(&mut h.rsc);
|
|
let b = rect(Color::BLUE).width(b).add(&mut h.rsc);
|
|
h.set_root((a, b).span(Dir::RIGHT));
|
|
assert_corners!(h, a, (0, 0), (40, 100));
|
|
assert_corners!(h, b, (40, 0), (60, 100));
|
|
h.resize((300, 100));
|
|
h.frame();
|
|
assert_corners!(h, a, (0, 0), (200, 100));
|
|
assert_corners!(h, b, (200, 0), (300, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn a_length_expression_is_resolved_before_wrapping_text() {
|
|
let mut h = Harness::new((300, 500));
|
|
let text = wtext("one two three four five six seven eight nine ten")
|
|
.size(16)
|
|
.wrap(true)
|
|
.width(leftover(1).clamp(40, 80))
|
|
.add(&mut h.rsc);
|
|
let other = rect(Color::BLUE).add(&mut h.rsc);
|
|
h.set_root((text, other).span(Dir::RIGHT));
|
|
let box_ = h.region(&text).unwrap();
|
|
assert_eq!(box_.top_left.x, Px::ZERO);
|
|
assert_eq!(box_.bot_right.x, Px::from_int(80));
|
|
assert!(box_.bot_right.y - box_.top_left.y > Px::from_int(30));
|
|
assert_corners!(h, other, (80, 0), (300, 500));
|
|
}
|
|
|
|
#[test]
|
|
fn relative_bounds_keep_the_allocators_base() {
|
|
let mut h = Harness::new((300, 100));
|
|
let head = rect(Color::BLUE).width(30).add(&mut h.rsc);
|
|
let bounded = rect(Color::RED)
|
|
.width(leftover(1).min(rel(0.25)))
|
|
.add(&mut h.rsc);
|
|
let tail = rect(Color::GREEN).add(&mut h.rsc);
|
|
h.set_root((head, bounded, tail).span(Dir::RIGHT));
|
|
assert_corners!(h, bounded, (30, 0), (105, 100));
|
|
assert_corners!(h, tail, (105, 0), (300, 100));
|
|
h.resize((400, 100));
|
|
h.frame();
|
|
assert_corners!(h, bounded, (30, 0), (130, 100));
|
|
assert_corners!(h, tail, (130, 0), (400, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn the_root_resolves_a_deferred_request_again_after_resize() {
|
|
let mut h = Harness::new((300, 100));
|
|
let bounded = rect(Color::RED)
|
|
.width(leftover(1).min(rel(0.25)))
|
|
.add(&mut h.rsc);
|
|
h.set_root(bounded);
|
|
assert_corners!(h, bounded, (112.5, 0), (187.5, 100));
|
|
h.resize((400, 100));
|
|
h.frame();
|
|
assert_corners!(h, bounded, (150, 0), (250, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn filling_a_stack_does_not_mean_its_sizing_child_was_already_allocated() {
|
|
let mut h = Harness::new((300, 100));
|
|
let child = rect(Color::RED).width(leftover(1).min(80)).add(&mut h.rsc);
|
|
let overlay = rect(Color::BLUE).add(&mut h.rsc);
|
|
let children: Vec<StrongWidget> =
|
|
vec![child.add_strong(&mut h.rsc), overlay.add_strong(&mut h.rsc)];
|
|
h.set_root(Stack {
|
|
children,
|
|
size: StackSize::Child(0),
|
|
});
|
|
assert_corners!(h, child, (110, 0), (190, 100));
|
|
assert_corners!(h, overlay, (110, 0), (190, 100));
|
|
}
|
|
|
|
struct Unhinted;
|
|
impl Widget for Unhinted {
|
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
|
painter.px_size();
|
|
painter.primitive(RectPrimitive::color(Color::RED));
|
|
Size::LEFTOVER
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn bounds_also_apply_to_shares_discovered_by_drawing() {
|
|
let mut h = Harness::new((300, 100));
|
|
let a = h.rsc.widgets_mut().add_strong(Unhinted);
|
|
h.rsc
|
|
.widgets_mut()
|
|
.set_size_rule(a.id(), Axis::X, SizeRule::Max(Len::px(80.0)));
|
|
let id = a.id();
|
|
let b = rect(Color::BLUE).add_strong(&mut h.rsc);
|
|
let b_id = b.id();
|
|
h.state.root = Some(h.rsc.widgets_mut().add_strong(Span {
|
|
children: vec![a, b],
|
|
dir: Dir::RIGHT,
|
|
gap: Px::ZERO,
|
|
}));
|
|
h.frame();
|
|
assert_corners!(h, id, (0, 0), (80, 100));
|
|
assert_corners!(h, b_id, (80, 0), (300, 100));
|
|
h.resize((100, 100));
|
|
h.frame();
|
|
assert_corners!(h, id, (0, 0), (50, 100));
|
|
assert_corners!(h, b_id, (50, 0), (100, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn comparisons_with_a_known_order_remain_plain_lengths() {
|
|
assert_eq!(leftover(2).max(leftover(5)), SizeRequest::from(leftover(5)));
|
|
assert_eq!(leftover(2).min(leftover(5)), SizeRequest::from(leftover(2)));
|
|
assert_eq!(
|
|
(px(10) + rel(0.5)).max(px(30) + rel(0.5)),
|
|
SizeRequest::from(px(30) + rel(0.5))
|
|
);
|
|
assert_eq!(LayoutLen::px(10).clamp(20, 80), SizeRequest::from(20));
|
|
}
|
|
|
|
#[test]
|
|
fn a_measured_nested_share_keeps_its_comparison_for_the_outer_span() {
|
|
let mut h = Harness::new((300, 100));
|
|
let a = h.rsc.widgets_mut().add_strong(Unhinted);
|
|
let a_id = a.id();
|
|
h.rsc
|
|
.widgets_mut()
|
|
.set_size_rule(a_id, Axis::X, SizeRule::Max(Len::px(80.0)));
|
|
let b = rect(Color::BLUE).add_strong(&mut h.rsc);
|
|
let b_id = b.id();
|
|
let inner = h.rsc.widgets_mut().add_strong(Span {
|
|
children: vec![a, b],
|
|
dir: Dir::RIGHT,
|
|
gap: Px::ZERO,
|
|
});
|
|
let c = rect(Color::GREEN).add_strong(&mut h.rsc);
|
|
let c_id = c.id();
|
|
h.state.root = Some(h.rsc.widgets_mut().add_strong(Span {
|
|
children: vec![inner, c],
|
|
dir: Dir::RIGHT,
|
|
gap: Px::ZERO,
|
|
}));
|
|
h.frame();
|
|
assert_corners!(h, a_id, (0, 0), (80, 100));
|
|
assert_corners!(h, b_id, (80, 0), (190, 100));
|
|
assert_corners!(h, c_id, (190, 0), (300, 100));
|
|
h.rsc
|
|
.widgets_mut()
|
|
.mark_for_redraw(h.state.root.as_ref().unwrap().id());
|
|
h.frame();
|
|
assert_corners!(h, c_id, (190, 0), (300, 100));
|
|
}
|
|
|
|
struct Natural;
|
|
impl Widget for Natural {
|
|
fn draw(&mut self, painter: &mut Painter) -> Size {
|
|
painter.primitive(RectPrimitive::color(Color::RED));
|
|
Size::from_axis(Axis::X, LayoutLen::px(64), LayoutLen::px(64))
|
|
}
|
|
fn size_hint(&self, _: Axis) -> Option<LayoutLen> {
|
|
Some(LayoutLen::px(64))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn relative_bounds_on_a_hinted_child_track_the_offer_before_its_declared_size() {
|
|
fn tree(h: &mut Harness) -> WidgetId {
|
|
let natural = h.rsc.widgets_mut().add_strong(Natural);
|
|
let id = natural.id();
|
|
h.rsc
|
|
.widgets_mut()
|
|
.set_size_rule(id, Axis::X, SizeRule::Max(Len::rel(0.75)));
|
|
h.rsc.widgets_mut().set_size_rule(
|
|
id,
|
|
Axis::Y,
|
|
SizeRule::Clamp {
|
|
min: Len::rel(0.25),
|
|
max: Len::rel(0.75),
|
|
},
|
|
);
|
|
let inner = h.rsc.widgets_mut().add_strong(Stack {
|
|
children: vec![natural],
|
|
size: StackSize::Child(0),
|
|
});
|
|
let inner_id = inner.id();
|
|
let fill = rect(Color::BLUE).add_strong(&mut h.rsc);
|
|
let overlay = h.rsc.widgets_mut().add_strong(Stack {
|
|
children: vec![fill, inner],
|
|
size: StackSize::Child(0),
|
|
});
|
|
let share = rect(Color::GREEN).add_strong(&mut h.rsc);
|
|
let bounded = rect(Color::GREEN).add_strong(&mut h.rsc);
|
|
h.rsc.widgets_mut().set_size_rule(
|
|
bounded.id(),
|
|
Axis::X,
|
|
SizeRule::Clamp {
|
|
min: Len::rel(0.25),
|
|
max: Len::rel(0.75),
|
|
},
|
|
);
|
|
let fixed = wtext("one line, overflowing whatever it is given")
|
|
.size(16)
|
|
.wrap(false)
|
|
.add_strong(&mut h.rsc);
|
|
h.state.root = Some(h.rsc.widgets_mut().add_strong(Span {
|
|
children: vec![share, bounded, fixed, overlay],
|
|
dir: Dir::LEFT,
|
|
gap: Px::from_int(8),
|
|
}));
|
|
h.rsc.widgets_mut().set_size_rule(
|
|
h.state.root.as_ref().unwrap().id(),
|
|
Axis::Y,
|
|
LayoutLen::rel(1).into(),
|
|
);
|
|
h.frame();
|
|
inner_id
|
|
}
|
|
let mut warm = Harness::new((1920, 1200));
|
|
let a = tree(&mut warm);
|
|
warm.resize((640, 900));
|
|
warm.frame();
|
|
let mut cold = Harness::new((640, 900));
|
|
let b = tree(&mut cold);
|
|
assert_eq!(warm.region(&a), cold.region(&b));
|
|
}
|
|
|
|
#[test]
|
|
fn a_bound_can_extend_an_explicit_share_request() {
|
|
let mut h = Harness::new((300, 100));
|
|
let a = rect(Color::RED)
|
|
.width(leftover(1))
|
|
.min_width(100)
|
|
.add(&mut h.rsc);
|
|
let b = rect(Color::BLUE).add(&mut h.rsc);
|
|
h.set_root((a, b).span(Dir::RIGHT));
|
|
assert_corners!(h, a, (0, 0), (150, 100));
|
|
h.resize((120, 100));
|
|
h.frame();
|
|
assert_corners!(h, a, (0, 0), (100, 100));
|
|
assert_corners!(h, b, (100, 0), (120, 100));
|
|
}
|
|
|
|
#[test]
|
|
fn moving_scroll_content_preserves_its_resolved_expression_size() {
|
|
let mut h = Harness::new((300, 300));
|
|
let leaf = rect(Color::BLUE).add_strong(&mut h.rsc);
|
|
let leaf_id = leaf.id();
|
|
let content = h.rsc.widgets_mut().add_strong(Stack {
|
|
children: vec![leaf],
|
|
size: StackSize::Child(0),
|
|
});
|
|
let content_id = content.id();
|
|
h.rsc
|
|
.widgets_mut()
|
|
.set_size_rule(content_id, Axis::Y, leftover(1).min(120).into());
|
|
let scroll = h
|
|
.rsc
|
|
.widgets_mut()
|
|
.add_strong(Scroll::new(content, Axis::Y));
|
|
let scroll_id = scroll.id();
|
|
h.state.root = Some(scroll);
|
|
for _ in 0..3 {
|
|
h.rsc.widgets_mut().mark_for_redraw(scroll_id);
|
|
h.frame();
|
|
assert_corners!(h, content_id, (0, 90), (300, 210));
|
|
assert_corners!(h, leaf_id, (0, 90), (300, 210));
|
|
}
|
|
h.resize((300, 600));
|
|
h.frame();
|
|
assert_corners!(h, leaf_id, (0, 240), (300, 360));
|
|
}
|