use gpui::{ AnyElement, Entity, IntoElement, Modifiers, MouseButton, MouseDownEvent, MouseUpEvent, TestAppContext, VisualTestContext, point, px, }; use super::{Stage, said, say, settle, stage}; use crate::canvas::{Frame, InfiniteCanvas, Shape, ShapeKind, Tool, ToolLayer, Viewport}; fn at(x: f32, y: f32) -> gpui::Point { point(px(x), px(y)) } /// Presses at `from`, drags in steps to `to`, and lets go. fn drag(from: (f32, f32), to: (f32, f32), cx: &mut VisualTestContext) { cx.simulate_mouse_move(at(from.0, from.1), None, Modifiers::none()); cx.simulate_mouse_down(at(from.0, from.1), MouseButton::Left, Modifiers::none()); for step in 1..=4 { let share = step as f32 / 4.0; let x = from.0 + (to.0 - from.0) * share; let y = from.1 + (to.1 - from.1) * share; cx.simulate_mouse_move(at(x, y), Some(MouseButton::Left), Modifiers::none()); settle(cx); } cx.simulate_mouse_up(at(to.0, to.1), MouseButton::Left, Modifiers::none()); settle(cx); } fn press(x: f32, y: f32, count: usize, cx: &mut VisualTestContext) { cx.simulate_event(MouseDownEvent { button: MouseButton::Left, position: at(x, y), modifiers: Modifiers::none(), click_count: count, first_mouse: false, }); cx.simulate_event(MouseUpEvent { button: MouseButton::Left, position: at(x, y), modifiers: Modifiers::none(), click_count: count, }); settle(cx); } fn pair() -> Vec { vec![ Shape::new( "a", "A", ShapeKind::Rect, Frame::new(0.0, 0.0, 100.0, 100.0), ), Shape::new( "b", "B", ShapeKind::Rect, Frame::new(200.0, 0.0, 100.0, 100.0), ), ] } fn layer(tool: Tool, selected: &[&'static str], owner: Entity) -> AnyElement { let view = Viewport::new(0.0, 0.0, 1.0); let (drawn, chosen, moved, sized) = (owner.clone(), owner.clone(), owner.clone(), owner); let layer = ToolLayer::new("layer", tool, view, pair()) .selected(selected.iter().copied()) .on_draw(move |kind, frame, _, cx| { let what = match kind { ShapeKind::Path { points, .. } => format!("path of {}", points.len()), other => format!("{other:?}"), }; say( &drawn, format!( "draw {what} {} {} {} {}", frame.x, frame.y, frame.w, frame.h ), cx, ) }) .on_select(move |keys, _, cx| say(&chosen, format!("select {keys:?}"), cx)) .on_move(move |keys, by, _, cx| say(&moved, format!("move {keys:?} {by:?}"), cx)) .on_resize(move |key, frame, _, cx| { say(&sized, format!("resize {key} {} {}", frame.w, frame.h), cx) }); InfiniteCanvas::new("plane", view) .shapes(pair()) .layer(layer) .into_any_element() } #[gpui::test] fn a_shape_tool_draws_what_the_drag_spans(cx: &mut TestAppContext) { let (host, cx) = stage(|owner| layer(Tool::Ellipse, &[], owner), cx); drag((110.0, 120.0), (190.0, 180.0), cx); assert_eq!(said(&host, cx), ["draw Ellipse 110 120 80 60"]); } #[gpui::test] fn a_selected_shape_moves_and_snaps_to_its_neighbour(cx: &mut TestAppContext) { let (host, cx) = stage(|owner| layer(Tool::Select, &["a"], owner), cx); drag((50.0, 50.0), (147.0, 50.0), cx); assert_eq!( said(&host, cx), ["move [\"a\"] (100.0, 0.0)"], "its right edge meets b's left" ); } #[gpui::test] fn a_drag_on_empty_space_selects_what_it_holds(cx: &mut TestAppContext) { let (host, cx) = stage(|owner| layer(Tool::Select, &[], owner), cx); drag((350.0, 150.0), (150.0, -10.0), cx); assert_eq!( said(&host, cx), ["select [\"b\"]"], "a runs past the marquee's left" ); } #[gpui::test] fn a_corner_handle_resizes_the_selection(cx: &mut TestAppContext) { let (host, cx) = stage(|owner| layer(Tool::Select, &["a"], owner), cx); drag((100.0, 100.0), (150.0, 120.0), cx); assert_eq!(said(&host, cx), ["resize a 150 120"]); } #[gpui::test] fn the_pen_adds_a_point_a_press_and_a_double_press_ends_the_path(cx: &mut TestAppContext) { let (host, cx) = stage(|owner| layer(Tool::Pen, &[], owner), cx); for (x, y) in [(110.0, 150.0), (160.0, 150.0), (160.0, 200.0)] { press(x, y, 1, cx); } press(160.0, 200.0, 2, cx); assert_eq!(said(&host, cx), ["draw path of 3 110 150 50 50"]); }