use std::{cell::Cell, rc::Rc}; use iris::harness::{Harness, assert_corners}; use iris::prelude::*; struct Counted { draws: Rc>, } 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 { 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 = 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 { 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)); }