diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 7c9d667551..10de40d975 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -9,7 +9,6 @@ use crate::messages::tool::common_functionality::resize::Resize; use crate::messages::tool::common_functionality::snapping; use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint; use crate::messages::tool::common_functionality::snapping::SnapData; -use crate::messages::tool::common_functionality::snapping::SnapManager; use crate::messages::tool::common_functionality::transformation_cage::*; use graph_craft::document::NodeId; use graphene_std::Artboard; @@ -108,7 +107,6 @@ impl Default for ArtboardToolFsmState { struct ArtboardToolData { bounding_box_manager: Option, selected_artboard: Option, - snap_manager: SnapManager, cursor: MouseCursorIcon, drag_start: DVec2, drag_current: DVec2, @@ -188,7 +186,7 @@ impl ArtboardToolData { let center = from_center.then_some(bounds.center_of_transformation); let ignore = self.selected_artboard.map_or(Vec::new(), |layer| vec![layer]); let snap = Some(SizeSnapData { - manager: &mut self.snap_manager, + manager: &mut self.draw.snap_manager, points: &mut self.snap_candidates, snap_data: SnapData::ignore(document, input, &ignore), }); @@ -275,7 +273,7 @@ impl Fsm for ArtboardToolFsmState { } } - tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); + tool_data.draw.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); self } @@ -322,7 +320,7 @@ impl Fsm for ArtboardToolFsmState { let snap_data = SnapData::ignore(document, input, &ignore); let document_to_viewport = document.metadata().document_to_viewport; let [start, current] = [tool_data.drag_start, tool_data.drag_current].map(|point| document_to_viewport.transform_point2(point)); - let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates); + let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.draw.snap_manager, &tool_data.snap_candidates); let size = bounds.bounds[1] - bounds.bounds[0]; let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_delta); @@ -354,6 +352,8 @@ impl Fsm for ArtboardToolFsmState { ArtboardToolFsmState::Dragging } (ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => { + // The draw.calculate_points_ignore_layer uses this value to avoid snapping to itself. + tool_data.draw.layer = tool_data.selected_artboard; let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, center, constrain_axis_or_aspect, true); let viewport_to_document = document.metadata().document_to_viewport.inverse(); let [start, end] = [start, end].map(|point| viewport_to_document.transform_point2(point)); @@ -400,11 +400,11 @@ impl Fsm for ArtboardToolFsmState { .map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false, false, None)); if cursor == MouseCursorIcon::Default && !hovered { - tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position); + tool_data.draw.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position); responses.add(OverlaysMessage::Draw); cursor = MouseCursorIcon::Crosshair; } else { - tool_data.snap_manager.cleanup(responses); + tool_data.draw.cleanup(responses); } if tool_data.cursor != cursor { @@ -445,7 +445,7 @@ impl Fsm for ArtboardToolFsmState { (ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds | ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerUp) => { responses.add(DocumentMessage::EndTransaction); - tool_data.snap_manager.cleanup(responses); + tool_data.draw.cleanup(responses); if let Some(bounds) = &mut tool_data.bounding_box_manager { bounds.original_transforms.clear(); @@ -553,7 +553,7 @@ impl Fsm for ArtboardToolFsmState { (ArtboardToolFsmState::Dragging | ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::Abort) => { responses.add(DocumentMessage::AbortTransaction); - tool_data.snap_manager.cleanup(responses); + tool_data.draw.cleanup(responses); responses.add(OverlaysMessage::Draw); ArtboardToolFsmState::Ready { hovered } @@ -611,17 +611,53 @@ mod test_artboard { .collect() } + #[derive(Debug, PartialEq)] + struct ArtboardLayoutDocument { + position: IVec2, + dimensions: IVec2, + } + impl ArtboardLayoutDocument { + pub fn new(position: impl Into, dimensions: impl Into) -> Self { + Self { + position: position.into(), + dimensions: dimensions.into(), + } + } + } + + /// Check if all of the artboards exist in any ordering + async fn has_artboards(editor: &mut EditorTestUtils, mut expected: Vec) { + let artboards = get_artboards(editor) + .await + .iter() + .map(|row| ArtboardLayoutDocument::new(row.element.location, row.element.dimensions)) + .collect::>(); + assert_eq!(artboards.len(), expected.len(), "incorrect len: actual {:?}, expected {:?}", artboards, expected); + + for artboard in artboards { + let Some(index) = expected.iter().position(|expected| *expected == artboard) else { + panic!("found {:?} that did not match any expected artboards\nexpected {:?}", artboard, expected); + }; + expected.remove(index); + } + } + #[tokio::test] async fn artboard_draw_simple() { let mut editor = EditorTestUtils::create(); editor.new_document().await; editor.drag_tool(ToolType::Artboard, 10.1, 10.8, 19.9, 0.2, ModifierKeys::empty()).await; + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((10, 0), (10, 11))]).await; + } - let artboards = get_artboards(&mut editor).await; - - assert_eq!(artboards.len(), 1); - assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(10, 0)); - assert_eq!(artboards.get(0).unwrap().element.dimensions, IVec2::new(10, 11)); + #[tokio::test] + async fn artboard_snapping() { + let mut editor = EditorTestUtils::create(); + editor.new_document().await; + editor.set_viewport_size(DVec2::splat(-1000.), DVec2::splat(1000.)).await; // Necessary for doing snapping since snaps outside of the viewport are discarded + editor.drag_tool(ToolType::Artboard, 10., 10., 20., 20., ModifierKeys::empty()).await; + editor.drag_tool(ToolType::Artboard, 11., 50., 19., 60., ModifierKeys::empty()).await; + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((10, 10), (10, 10)), ArtboardLayoutDocument::new((10, 50), (10, 10))]).await; } #[tokio::test] @@ -629,11 +665,7 @@ mod test_artboard { let mut editor = EditorTestUtils::create(); editor.new_document().await; editor.drag_tool(ToolType::Artboard, 10., 10., -10., 11., ModifierKeys::SHIFT).await; - - let artboards = get_artboards(&mut editor).await; - assert_eq!(artboards.len(), 1); - assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(-10, 10)); - assert_eq!(artboards.get(0).unwrap().element.dimensions, IVec2::new(20, 20)); + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((-10, 10), (20, 20))]).await; } #[tokio::test] @@ -648,12 +680,9 @@ mod test_artboard { .await; // Viewport coordinates editor.drag_tool(ToolType::Artboard, 0., 0., 0., 10., ModifierKeys::SHIFT).await; - - let artboards = get_artboards(&mut editor).await; - assert_eq!(artboards.len(), 1); - assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(0, 0)); let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 10.); - assert_eq!(artboards.get(0).unwrap().element.dimensions, desired_size.round().as_ivec2()); + + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new(IVec2::new(0, 0), desired_size.round().as_ivec2())]).await; } #[tokio::test] @@ -669,12 +698,9 @@ mod test_artboard { .await; // Viewport coordinates editor.drag_tool(ToolType::Artboard, 0., 0., 0., 10., ModifierKeys::SHIFT | ModifierKeys::ALT).await; - - let artboards = get_artboards(&mut editor).await; - assert_eq!(artboards.len(), 1); - assert_eq!(artboards.get(0).unwrap().element.location, DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2()); - let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 20.); - assert_eq!(artboards.get(0).unwrap().element.dimensions, desired_size.round().as_ivec2()); + let desired_location = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2(); + let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 20.).as_ivec2(); + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new(desired_location, desired_size)]).await; } #[tokio::test] @@ -685,8 +711,7 @@ mod test_artboard { editor.drag_tool(ToolType::Artboard, 10.1, 10.8, 19.9, 0.2, ModifierKeys::default()).await; editor.press(Key::Delete, ModifierKeys::default()).await; - let artboards = get_artboards(&mut editor).await; - assert_eq!(artboards.len(), 0); + has_artboards(&mut editor, vec![]).await; } #[tokio::test] @@ -696,7 +721,27 @@ mod test_artboard { editor.new_document().await; editor.drag_tool_cancel_rmb(ToolType::Artboard).await; - let artboards = get_artboards(&mut editor).await; - assert_eq!(artboards.len(), 0); + has_artboards(&mut editor, vec![]).await; + } + + #[tokio::test] + async fn artboard_move() { + let mut editor = EditorTestUtils::create(); + editor.new_document().await; + editor.drag_tool(ToolType::Artboard, 10., 10., 20., 22., ModifierKeys::empty()).await; // Artboard to drag + editor.drag_tool(ToolType::Artboard, 15., 15., 65., 65., ModifierKeys::empty()).await; // Drag from the middle by (50,50) + + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((60, 60), (10, 12))]).await; + } + #[tokio::test] + async fn artboard_move_snapping() { + let mut editor = EditorTestUtils::create(); + editor.new_document().await; + editor.set_viewport_size(DVec2::splat(-1000.), DVec2::splat(1000.)).await; // Necessary for doing snapping since snaps outside of the viewport are discarded + editor.drag_tool(ToolType::Artboard, 10., 10., 20., 22., ModifierKeys::empty()).await; // Artboard to drag + editor.drag_tool(ToolType::Artboard, 70., 0., 80., 100., ModifierKeys::empty()).await; // Artboard to snap to + editor.drag_tool(ToolType::Artboard, 15., 15., 15. + 49., 15., ModifierKeys::empty()).await; // Drag the artboard so it should snap to the edge + + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((60, 10), (10, 12)), ArtboardLayoutDocument::new((70, 0), (10, 100))]).await; } } diff --git a/editor/src/test_utils.rs b/editor/src/test_utils.rs index addadae0c2..b6ce5de5f8 100644 --- a/editor/src/test_utils.rs +++ b/editor/src/test_utils.rs @@ -1,6 +1,7 @@ use crate::application::Editor; use crate::application::set_uuid_seed; use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; +use crate::messages::input_mapper::utility_types::input_mouse::ViewportBounds; use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition}; use crate::messages::portfolio::utility_types::Platform; use crate::messages::prelude::*; @@ -32,7 +33,6 @@ impl EditorTestUtils { let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok(); editor.handle_message(PortfolioMessage::Init); - Self { editor, runtime } } @@ -293,6 +293,14 @@ impl EditorTestUtils { ) .await; } + + /// Necessary for doing snapping since snaps outside of the viewport are discarded + pub async fn set_viewport_size(&mut self, top_left: DVec2, bottom_right: DVec2) { + self.handle_message(InputPreprocessorMessage::BoundsOfViewports { + bounds_of_viewports: vec![ViewportBounds { top_left, bottom_right }], + }) + .await; + } } pub trait FrontendMessageTestUtils {

AltStyle によって変換されたページ (->オリジナル) /