//! Layout, hit testing and task updates, driven without a window. use std::{cell::RefCell, rc::Rc, time::Duration}; use iris::harness::Harness; use iris::prelude::*; /// `PixelRegion` neither compares nor prints. fn corners(h: &Harness, id: &impl IdLike) -> (f32, f32, f32, f32) { let region = h.region(id).expect("widget drew nothing"); ( region.top_left.x, region.top_left.y, region.bot_right.x, region.bot_right.y, ) } /// A fixed 100 wide, and the rest of the 400 to its neighbour. fn two_rects(h: &mut Harness) -> (WidgetId, WidgetId) { let left = rect(Color::RED).width(100).add(&mut h.rsc); let right = rect(Color::BLUE).add(&mut h.rsc); h.set_root((left, right).span(Dir::RIGHT)); (left.id(), right.id()) } #[test] fn a_span_gives_each_child_the_width_it_asked_for() { let mut h = Harness::new((400, 200)); let (left, right) = two_rects(&mut h); assert_eq!(corners(&h, &left), (0.0, 0.0, 100.0, 200.0)); assert_eq!(corners(&h, &right), (100.0, 0.0, 400.0, 200.0)); } #[test] fn resizing_relays_out_against_the_new_output() { let mut h = Harness::new((400, 200)); let (left, right) = two_rects(&mut h); h.resize((800, 100)); assert!(h.needs_redraw()); h.frame(); assert_eq!(corners(&h, &left), (0.0, 0.0, 100.0, 100.0)); assert_eq!(corners(&h, &right), (100.0, 0.0, 800.0, 100.0)); } #[test] fn a_press_reaches_only_the_widget_under_the_cursor() { let mut h = Harness::new((400, 200)); let clicks = Rc::new(RefCell::new(Vec::new())); let (on_left, on_right) = (clicks.clone(), clicks.clone()); let left = rect(Color::RED) .width(100) .on(CursorSense::click(), move |_, _| { on_left.borrow_mut().push("left") }) .add(&mut h.rsc); let right = rect(Color::BLUE) .on(CursorSense::click(), move |_, _| { on_right.borrow_mut().push("right") }) .add(&mut h.rsc); h.set_root((left, right).span(Dir::RIGHT)); h.click((50, 100)); assert_eq!(*clicks.borrow(), ["left"]); h.click((300, 100)); assert_eq!(*clicks.borrow(), ["left", "right"]); } #[test] fn hover_ends_when_the_cursor_leaves_the_window() { let mut h = Harness::new((400, 200)); let hovered = Rc::new(RefCell::new(0)); let ended = Rc::new(RefCell::new(0)); let (h_count, e_count) = (hovered.clone(), ended.clone()); let widget = rect(Color::RED) .on(CursorSense::HoverStart, move |_, _| { *h_count.borrow_mut() += 1 }) .on(CursorSense::HoverEnd, move |_, _| { *e_count.borrow_mut() += 1 }) .add(&mut h.rsc); h.set_root(widget); h.move_to((200, 100)); assert_eq!((*hovered.borrow(), *ended.borrow()), (1, 0)); // A second sample inside the same widget is not a second hover. h.move_to((210, 100)); assert_eq!((*hovered.borrow(), *ended.borrow()), (1, 0)); h.leave(); assert_eq!((*hovered.borrow(), *ended.borrow()), (1, 1)); } #[test] fn a_task_update_reaches_the_tree() { let mut h = Harness::new((400, 200)); let widget = rect(Color::RED).add(&mut h.rsc); h.set_root(widget.task_on(CursorSense::click(), async move |mut ctx| { ctx.update(move |_, rsc| widget(rsc).color = Color::BLUE); })); h.click((200, 100)); assert!( h.await_update(Duration::from_secs(5)), "the task sent no update" ); assert_eq!(h.rsc[widget].color, Color::BLUE); } #[test] fn a_wheel_scrolls_the_content_and_stops_at_its_end() { let mut h = Harness::new((400, 200)); // Twice the window's height, so there is 200 to scroll. let top = rect(Color::RED).height(200).add(&mut h.rsc); let bottom = rect(Color::BLUE).height(200).add(&mut h.rsc); h.set_root((top, bottom).span(Dir::DOWN).scrollable()); h.move_to((200, 100)); // `Scroll` starts snapped to the end. assert_eq!(corners(&h, &top).1, -200.0); // The handler scales a wheel line by 50. h.scroll((0, 1)); h.frame(); assert_eq!(corners(&h, &top).1, -150.0); h.scroll((0, 10)); h.frame(); assert_eq!(corners(&h, &top).1, 0.0); }