From 657b689fdd9e46d5681015c0d7c0eda6c324c079 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Wed, 29 Dec 2021 11:04:59 +0000 Subject: [PATCH 1/4] Remove transformations from layerdata --- editor/src/document/document_file.rs | 14 +- .../src/document/document_message_handler.rs | 2 +- editor/src/document/layer_panel.rs | 21 +-- editor/src/document/movement_handler.rs | 141 ++++++++++-------- 4 files changed, 88 insertions(+), 90 deletions(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index 3753d32d21..3b331d7d53 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -177,7 +177,7 @@ impl DocumentMessageHandler { pub fn with_name(name: String, ipp: &InputPreprocessor) -> Self { let mut document = Self { name, ..Self::default() }; - document.graphene_document.root.transform = document.layer_data(&[]).calculate_offset_transform(ipp.viewport_bounds.size() / 2., 0.); + document.graphene_document.root.transform = document.movement_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.); document } @@ -475,9 +475,7 @@ impl MessageHandler for DocumentMessageHand fn process_action(&mut self, message: DocumentMessage, ipp: &InputPreprocessor, responses: &mut VecDeque) { use DocumentMessage::*; match message { - Movement(message) => self - .movement_handler - .process_action(message, (Self::layer_data_mut_no_borrow_self(&mut self.layer_data, &[]), &self.graphene_document, ipp), responses), + Movement(message) => self.movement_handler.process_action(message, (&self.graphene_document, ipp), responses), TransformLayers(message) => self .transform_layer_handler .process_action(message, (&mut self.layer_data, &mut self.graphene_document, ipp), responses), @@ -745,9 +743,9 @@ impl MessageHandler for DocumentMessageHand } .into(), ); - let root_layerdata = self.layer_data(&[]); + let document_transform = &self.movement_handler; - let scale = 0.5 + ASYMPTOTIC_EFFECT + root_layerdata.scale * SCALE_EFFECT; + let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform.scale * SCALE_EFFECT; let viewport_size = ipp.viewport_bounds.size(); let viewport_mid = ipp.viewport_bounds.center(); let [bounds1, bounds2] = self.graphene_document.visible_layers_bounding_box().unwrap_or([viewport_mid; 2]); @@ -758,9 +756,9 @@ impl MessageHandler for DocumentMessageHand let scrollbar_multiplier = bounds_length - viewport_size; let scrollbar_size = viewport_size / bounds_length; - let log = root_layerdata.scale.log2(); + let log = document_transform.scale.log2(); let ruler_interval = if log < 0. { 100. * 2_f64.powf(-log.ceil()) } else { 100. / 2_f64.powf(log.ceil()) }; - let ruler_spacing = ruler_interval * root_layerdata.scale; + let ruler_spacing = ruler_interval * document_transform.scale; let ruler_origin = self.graphene_document.root.transform.transform_point2(DVec2::ZERO); diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index e1b64de61b..c975ac8bf4 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -95,7 +95,7 @@ impl DocumentsMessageHandler { } // TODO Fix how this doesn't preserve tab order upon loading new document from file>load - fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque) { + fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque) { // Special case when loading a document on an empty page if replace_first_empty && self.active_document().is_unmodified_default() { responses.push_back(DocumentsMessage::CloseDocument(self.active_document_id).into()); diff --git a/editor/src/document/layer_panel.rs b/editor/src/document/layer_panel.rs index eb967ac778..eef4b2b600 100644 --- a/editor/src/document/layer_panel.rs +++ b/editor/src/document/layer_panel.rs @@ -2,36 +2,17 @@ use glam::{DAffine2, DVec2}; use graphene::layers::{style::ViewMode, BlendMode, Layer, LayerData as DocumentLayerData, LayerDataType}; use graphene::LayerId; use serde::{ser::SerializeStruct, Deserialize, Serialize}; -use std::collections::HashMap; use std::fmt; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)] pub struct LayerData { pub selected: bool, pub expanded: bool, - pub translation: DVec2, - pub rotation: f64, - pub scale: f64, } impl LayerData { pub fn new(expanded: bool) -> LayerData { - LayerData { - selected: false, - expanded, - translation: DVec2::ZERO, - rotation: 0., - scale: 1., - } - } - - pub fn calculate_offset_transform(&self, offset: DVec2, snapped_angle: f64) -> DAffine2 { - // TODO: replace with DAffine2::from_scale_angle_translation and fix the errors - let offset_transform = DAffine2::from_translation(offset); - let scale_transform = DAffine2::from_scale(DVec2::new(self.scale, self.scale)); - let angle_transform = DAffine2::from_angle(snapped_angle); - let translation_transform = DAffine2::from_translation(self.translation); - scale_transform * offset_transform * angle_transform * translation_transform + LayerData { selected: false, expanded } } } diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index 5df7789c54..1990da60aa 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -1,6 +1,6 @@ use crate::consts::VIEWPORT_ROTATE_SNAP_INTERVAL; pub use crate::document::layer_panel::*; -use crate::document::{DocumentMessage, LayerData}; +use crate::document::DocumentMessage; use crate::message_prelude::*; use crate::{ consts::{VIEWPORT_SCROLL_RATE, VIEWPORT_ZOOM_LEVELS, VIEWPORT_ZOOM_MOUSE_RATE, VIEWPORT_ZOOM_SCALE_MAX, VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_WHEEL_RATE}, @@ -9,7 +9,7 @@ use crate::{ use graphene::document::Document; use graphene::Operation as DocumentOperation; -use glam::DVec2; +use glam::{DAffine2, DVec2}; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; @@ -34,42 +34,67 @@ pub enum MovementMessage { TranslateCanvasByViewportFraction(DVec2), } -#[derive(Debug, Clone, Default, PartialEq)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MovementMessageHandler { translating: bool, + pub translation: DVec2, rotating: bool, + pub rotation: f64, zooming: bool, - snapping: bool, - mouse_pos: ViewportPosition, + pub scale: f64, snap_rotate: bool, + mouse_pos: ViewportPosition, +} + +impl Default for MovementMessageHandler { + fn default() -> Self { + Self { + scale: 1., + translating: false, + translation: DVec2::ZERO, + rotating: false, + rotation: 0., + zooming: false, + snap_rotate: false, + mouse_pos: ViewportPosition::default(), + } + } } impl MovementMessageHandler { - pub fn snapped_angle(&self, layerdata: &LayerData) -> f64 { + pub fn snapped_angle(&self) -> f64 { let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians(); if self.snap_rotate { - (layerdata.rotation / increment_radians).round() * increment_radians + (self.rotation / increment_radians).round() * increment_radians } else { - layerdata.rotation + self.rotation } } + pub fn calculate_offset_transform(&self, offset: DVec2) -> DAffine2 { + // TODO: replace with DAffine2::from_scale_angle_translation and fix the errors + let offset_transform = DAffine2::from_translation(offset); + let scale_transform = DAffine2::from_scale(DVec2::new(self.scale, self.scale)); + let angle_transform = DAffine2::from_angle(self.snapped_angle()); + let translation_transform = DAffine2::from_translation(self.translation); + scale_transform * offset_transform * angle_transform * translation_transform + } - fn create_document_transform_from_layerdata(&self, layerdata: &LayerData, viewport_bounds: &ViewportBounds, responses: &mut VecDeque) { + fn create_document_transform(&self, viewport_bounds: &ViewportBounds, responses: &mut VecDeque) { let half_viewport = viewport_bounds.size() / 2.; - let scaled_half_viewport = half_viewport / layerdata.scale; + let scaled_half_viewport = half_viewport / self.scale; responses.push_back( DocumentOperation::SetLayerTransform { path: vec![], - transform: layerdata.calculate_offset_transform(scaled_half_viewport, self.snapped_angle(layerdata)).to_cols_array(), + transform: self.calculate_offset_transform(scaled_half_viewport).to_cols_array(), } .into(), ); } } -impl MessageHandler for MovementMessageHandler { - fn process_action(&mut self, message: MovementMessage, data: (&mut LayerData, &Document, &InputPreprocessor), responses: &mut VecDeque) { - let (layerdata, document, ipp) = data; +impl MessageHandler for MovementMessageHandler { + fn process_action(&mut self, message: MovementMessage, data: (&Document, &InputPreprocessor), responses: &mut VecDeque) { + let (document, ipp) = data; use MovementMessage::*; match message { TranslateCanvasBegin => { @@ -78,18 +103,20 @@ impl MessageHandler { self.rotating = true; - self.snapping = snap; self.snap_rotate = snap; self.mouse_pos = ipp.mouse.position; } - EnableSnapping => self.snapping = true, - DisableSnapping => self.snapping = false, + EnableSnapping => self.snap_rotate = true, + DisableSnapping => { + self.rotation = self.snapped_angle(); + self.snap_rotate = false + } ZoomCanvasBegin => { self.zooming = true; self.mouse_pos = ipp.mouse.position; } TransformCanvasEnd => { - layerdata.rotation = self.snapped_angle(layerdata); + self.rotation = self.snapped_angle(); self.snap_rotate = false; self.translating = false; self.rotating = false; @@ -100,9 +127,9 @@ impl MessageHandler { - layerdata.scale = new.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); - responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: layerdata.scale }.into()); + self.scale = new.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); + responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into()); responses.push_back(ToolMessage::SelectedLayersChanged.into()); responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.create_document_transform(&ipp.viewport_bounds, responses); } IncreaseCanvasZoom => { // TODO: Eliminate redundant code by making this call SetCanvasZoom - layerdata.scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > layerdata.scale).unwrap_or(&layerdata.scale); - responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: layerdata.scale }.into()); + self.scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.scale).unwrap_or(&self.scale); + responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into()); responses.push_back(ToolMessage::SelectedLayersChanged.into()); responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.create_document_transform(&ipp.viewport_bounds, responses); } DecreaseCanvasZoom => { // TODO: Eliminate redundant code by making this call SetCanvasZoom - layerdata.scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < layerdata.scale).unwrap_or(&layerdata.scale); - responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: layerdata.scale }.into()); + self.scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.scale).unwrap_or(&self.scale); + responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into()); responses.push_back(ToolMessage::SelectedLayersChanged.into()); responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.create_document_transform(&ipp.viewport_bounds, responses); } WheelCanvasZoom => { // TODO: Eliminate redundant code by making this call SetCanvasZoom @@ -175,13 +194,13 @@ impl MessageHandler { let delta = match use_y_as_x { @@ -189,13 +208,13 @@ impl MessageHandler (-ipp.mouse.scroll_delta.y as f64, 0.).into(), } * VIEWPORT_SCROLL_RATE; let transformed_delta = document.root.transform.inverse().transform_vector2(delta); - layerdata.translation += transformed_delta; + self.translation += transformed_delta; responses.push_back(ToolMessage::SelectedLayersChanged.into()); - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.create_document_transform(&ipp.viewport_bounds, responses); } SetCanvasRotation(new) => { - layerdata.rotation = new; - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.rotation = new; + self.create_document_transform(&ipp.viewport_bounds, responses); responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: new }.into()); responses.push_back(ToolMessage::SelectedLayersChanged.into()); } @@ -211,27 +230,27 @@ impl MessageHandler { let transformed_delta = document.root.transform.inverse().transform_vector2(delta); - layerdata.translation += transformed_delta; + self.translation += transformed_delta; responses.push_back(ToolMessage::SelectedLayersChanged.into()); - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.create_document_transform(&ipp.viewport_bounds, responses); } TranslateCanvasByViewportFraction(delta) => { let transformed_delta = document.root.transform.inverse().transform_vector2(delta * ipp.viewport_bounds.size()); - layerdata.translation += transformed_delta; + self.translation += transformed_delta; responses.push_back(ToolMessage::SelectedLayersChanged.into()); - self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses); + self.create_document_transform(&ipp.viewport_bounds, responses); } } } From ecf1c3d0f70055655bf3271bd1ac6b5fc7322cf0 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Thu, 30 Dec 2021 11:39:11 +0000 Subject: [PATCH 2/4] Clean up snap rotate --- editor/src/document/movement_handler.rs | 32 ++++++++++--------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index 1990da60aa..8bcee279af 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -1,6 +1,7 @@ use crate::consts::VIEWPORT_ROTATE_SNAP_INTERVAL; pub use crate::document::layer_panel::*; use crate::document::DocumentMessage; +use crate::input::keyboard::Key; use crate::message_prelude::*; use crate::{ consts::{VIEWPORT_SCROLL_RATE, VIEWPORT_ZOOM_LEVELS, VIEWPORT_ZOOM_MOUSE_RATE, VIEWPORT_ZOOM_SCALE_MAX, VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_WHEEL_RATE}, @@ -16,12 +17,10 @@ use std::collections::VecDeque; #[impl_message(Message, DocumentMessage, Movement)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub enum MovementMessage { - MouseMove, + MouseMove { snap_angle: Key }, TranslateCanvasBegin, WheelCanvasTranslate { use_y_as_x: bool }, - RotateCanvasBegin { snap: bool }, - EnableSnapping, - DisableSnapping, + RotateCanvasBegin, ZoomCanvasBegin, TransformCanvasEnd, SetCanvasRotation(f64), @@ -101,16 +100,10 @@ impl MessageHandler for Moveme self.translating = true; self.mouse_pos = ipp.mouse.position; } - RotateCanvasBegin { snap } => { + RotateCanvasBegin => { self.rotating = true; - self.snap_rotate = snap; self.mouse_pos = ipp.mouse.position; } - EnableSnapping => self.snap_rotate = true, - DisableSnapping => { - self.rotation = self.snapped_angle(); - self.snap_rotate = false - } ZoomCanvasBegin => { self.zooming = true; self.mouse_pos = ipp.mouse.position; @@ -122,7 +115,7 @@ impl MessageHandler for Moveme self.rotating = false; self.zooming = false; } - MouseMove => { + MouseMove { snap_angle } => { if self.translating { let delta = ipp.mouse.position - self.mouse_pos; let transformed_delta = document.root.transform.inverse().transform_vector2(delta); @@ -131,7 +124,15 @@ impl MessageHandler for Moveme responses.push_back(ToolMessage::SelectedLayersChanged.into()); self.create_document_transform(&ipp.viewport_bounds, responses); } + if self.rotating { + let new_snap = ipp.keyboard.get(snap_angle as usize); + // When disabling snap, keep the viewed rotation as it was previously. + if !new_snap && self.snap_rotate { + self.rotation = self.snapped_angle(); + } + self.snap_rotate = new_snap; + let half_viewport = ipp.viewport_bounds.size() / 2.; let rotation = { let start_vec = self.mouse_pos - half_viewport; @@ -271,13 +272,6 @@ impl MessageHandler for Moveme TranslateCanvasByViewportFraction, ); - if self.rotating { - let snapping = actions!(MovementMessageDiscriminant; - EnableSnapping, - DisableSnapping, - ); - common.extend(snapping); - } if self.translating || self.rotating || self.zooming { let transforming = actions!(MovementMessageDiscriminant; TransformCanvasEnd, From d896a39958582ad3a5350b97ceadd3406ddb4463 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Thu, 30 Dec 2021 11:39:43 +0000 Subject: [PATCH 3/4] Enable the navigate tool --- editor/src/input/input_mapper.rs | 1 + frontend/src/components/panels/Document.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index f5fc3654b2..374daf27c4 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -194,6 +194,7 @@ impl Default for Mapping { entry! {action=FillMessage::RightMouseDown, key_down=Rmb}, // Tool Actions entry! {action=ToolMessage::ActivateTool(ToolType::Select), key_down=KeyV}, + entry! {action=ToolMessage::ActivateTool(ToolType::Navigate), key_down=KeyZ}, entry! {action=ToolMessage::ActivateTool(ToolType::Eyedropper), key_down=KeyI}, entry! {action=ToolMessage::ActivateTool(ToolType::Fill), key_down=KeyF}, entry! {action=ToolMessage::ActivateTool(ToolType::Path), key_down=KeyA}, diff --git a/frontend/src/components/panels/Document.vue b/frontend/src/components/panels/Document.vue index 2d1875b80d..31d461d81b 100644 --- a/frontend/src/components/panels/Document.vue +++ b/frontend/src/components/panels/Document.vue @@ -71,7 +71,7 @@
- + From 1e11c23ad52c189b9422eaf9a89abe28be15aa00 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Thu, 30 Dec 2021 12:23:19 +0000 Subject: [PATCH 4/4] Implement navigate tool --- editor/src/input/input_mapper.rs | 15 +-- editor/src/tool/tool_message_handler.rs | 1 + editor/src/tool/tools/navigate.rs | 134 ++++++++++++++++++++++-- 3 files changed, 138 insertions(+), 12 deletions(-) diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index 374daf27c4..76752184e5 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -135,8 +135,6 @@ impl Default for Mapping { let mappings = mapping![ // Higher priority than entries in sections below entry! {action=DocumentsMessage::Paste(User), key_down=KeyV, modifiers=[KeyControl]}, - entry! {action=MovementMessage::EnableSnapping, key_down=KeyShift}, - entry! {action=MovementMessage::DisableSnapping, key_up=KeyShift}, // Transform layers entry! {action=TransformLayerMessage::ApplyOperation, key_down=KeyEnter}, entry! {action=TransformLayerMessage::ApplyOperation, key_down=Lmb}, @@ -155,6 +153,12 @@ impl Default for Mapping { entry! {action=SelectMessage::DragStop, key_up=Lmb}, entry! {action=SelectMessage::Abort, key_down=Rmb}, entry! {action=SelectMessage::Abort, key_down=KeyEscape}, + // Navigate + entry! {action=NavigateMessage::MouseMove{snap_angle: KeyControl}, message=InputMapperMessage::PointerMove}, + entry! {action=NavigateMessage::RotateCanvasBegin, key_down=Rmb}, + entry! {action=NavigateMessage::ZoomCanvasBegin, key_down=Lmb}, + entry! {action=NavigateMessage::TransformCanvasEnd, key_up=Rmb}, + entry! {action=NavigateMessage::TransformCanvasEnd, key_up=Lmb}, // Eyedropper entry! {action=EyedropperMessage::LeftMouseDown, key_down=Lmb}, entry! {action=EyedropperMessage::RightMouseDown, key_down=Rmb}, @@ -226,11 +230,9 @@ impl Default for Mapping { entry! {action=TransformLayerMessage::BeginRotate, key_down=KeyR}, entry! {action=TransformLayerMessage::BeginScale, key_down=KeyS}, // Document movement - entry! {action=MovementMessage::MouseMove, message=InputMapperMessage::PointerMove}, - entry! {action=MovementMessage::RotateCanvasBegin{snap:false}, key_down=Mmb, modifiers=[KeyControl]}, - entry! {action=MovementMessage::RotateCanvasBegin{snap:true}, key_down=Mmb, modifiers=[KeyControl, KeyShift]}, + entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift}, message=InputMapperMessage::PointerMove}, + entry! {action=MovementMessage::RotateCanvasBegin, key_down=Mmb, modifiers=[KeyControl]}, entry! {action=MovementMessage::ZoomCanvasBegin, key_down=Mmb, modifiers=[KeyShift]}, - entry! {action=MovementMessage::ZoomCanvasToFitAll, key_down=Key0, modifiers=[KeyControl]}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Mmb}, entry! {action=MovementMessage::TransformCanvasEnd, key_up=Mmb}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Lmb, modifiers=[KeySpace]}, @@ -240,6 +242,7 @@ impl Default for Mapping { entry! {action=MovementMessage::DecreaseCanvasZoom, key_down=KeyMinus, modifiers=[KeyControl]}, entry! {action=MovementMessage::SetCanvasZoom(1.), key_down=Key1, modifiers=[KeyControl]}, entry! {action=MovementMessage::SetCanvasZoom(2.), key_down=Key2, modifiers=[KeyControl]}, + entry! {action=MovementMessage::ZoomCanvasToFitAll, key_down=Key0, modifiers=[KeyControl]}, entry! {action=MovementMessage::WheelCanvasZoom, message=InputMapperMessage::MouseScroll, modifiers=[KeyControl]}, entry! {action=MovementMessage::WheelCanvasTranslate{use_y_as_x: true}, message=InputMapperMessage::MouseScroll, modifiers=[KeyShift]}, entry! {action=MovementMessage::WheelCanvasTranslate{use_y_as_x: false}, message=InputMapperMessage::MouseScroll}, diff --git a/editor/src/tool/tool_message_handler.rs b/editor/src/tool/tool_message_handler.rs index 7a247bf708..9b6c850beb 100644 --- a/editor/src/tool/tool_message_handler.rs +++ b/editor/src/tool/tool_message_handler.rs @@ -79,6 +79,7 @@ impl MessageHandler // Get the Abort state of a tool's FSM let reset_message = |tool| match tool { ToolType::Select => Some(SelectMessage::Abort.into()), + ToolType::Navigate => Some(NavigateMessage::TransformCanvasEnd.into()), ToolType::Path => Some(PathMessage::Abort.into()), ToolType::Pen => Some(PenMessage::Abort.into()), ToolType::Line => Some(LineMessage::Abort.into()), diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index 136b149cc2..f19b12ae92 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -1,20 +1,142 @@ -use crate::message_prelude::*; -use crate::tool::ToolActionHandlerData; +use crate::input::keyboard::MouseMotion; +use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup}; +use crate::tool::{Fsm, ToolActionHandlerData}; +use crate::{input::keyboard::Key, message_prelude::*}; use serde::{Deserialize, Serialize}; #[derive(Default)] -pub struct Navigate; +pub struct Navigate { + fsm_state: NavigateToolFsmState, +} #[impl_message(Message, ToolMessage, Navigate)] #[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum NavigateMessage { - MouseMove, + MouseMove { snap_angle: Key }, + TranslateCanvasBegin, + RotateCanvasBegin, + ZoomCanvasBegin, + TransformCanvasEnd, } impl<'a> MessageHandler> for Navigate { fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque) { - todo!("{}::handle_input {:?} {:?} {:?} ", module_path!(), action, data, responses); + if action == ToolMessage::UpdateHints { + self.fsm_state.update_hints(responses); + return; + } + + let new_state = self.fsm_state.transition(action, data.0, data.1, &mut (), data.2, responses); + + if self.fsm_state != new_state { + self.fsm_state = new_state; + self.fsm_state.update_hints(responses); + } + } + + fn actions(&self) -> ActionList { + use NavigateToolFsmState::*; + match self.fsm_state { + Ready => actions!(NavigateMessageDiscriminant; TranslateCanvasBegin, RotateCanvasBegin, ZoomCanvasBegin), + _ => actions!(NavigateMessageDiscriminant; MouseMove, TransformCanvasEnd), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum NavigateToolFsmState { + Ready, + Translating, + Rotating, + Zooming, +} + +impl Default for NavigateToolFsmState { + fn default() -> Self { + NavigateToolFsmState::Ready + } +} + +impl Fsm for NavigateToolFsmState { + type ToolData = (); + + fn transition( + self, + message: ToolMessage, + _document: &crate::document::DocumentMessageHandler, + _tool_data: &crate::tool::DocumentToolData, + _data: &mut Self::ToolData, + _input: &crate::input::InputPreprocessor, + messages: &mut VecDeque, + ) -> Self { + if let ToolMessage::Navigate(navigate) = message { + use NavigateMessage::*; + match navigate { + MouseMove { snap_angle } => { + messages.push_front(MovementMessage::MouseMove { snap_angle }.into()); + self + } + TranslateCanvasBegin => { + messages.push_front(MovementMessage::TranslateCanvasBegin.into()); + NavigateToolFsmState::Translating + } + RotateCanvasBegin => { + messages.push_front(MovementMessage::RotateCanvasBegin.into()); + NavigateToolFsmState::Rotating + } + ZoomCanvasBegin => { + messages.push_front(MovementMessage::ZoomCanvasBegin.into()); + NavigateToolFsmState::Zooming + } + TransformCanvasEnd => { + messages.push_front(MovementMessage::TransformCanvasEnd.into()); + NavigateToolFsmState::Ready + } + } + } else { + self + } } - advertise_actions!(); + fn update_hints(&self, responses: &mut VecDeque) { + let hint_data = match self { + NavigateToolFsmState::Ready => HintData(vec![ + HintGroup(vec![HintInfo { + key_groups: vec![], + mouse: Some(MouseMotion::MmbDrag), + label: String::from("Translate"), + plus: false, + }]), + HintGroup(vec![ + HintInfo { + key_groups: vec![], + mouse: Some(MouseMotion::RmbDrag), + label: String::from("Rotate (drag around centre)"), + plus: false, + }, + HintInfo { + key_groups: vec![KeysGroup(vec![Key::KeyControl])], + mouse: None, + label: String::from("Snap rotation to 15° increments"), + plus: true, + }, + ]), + HintGroup(vec![HintInfo { + key_groups: vec![], + mouse: Some(MouseMotion::LmbDrag), + label: String::from("Zoom in and out (drag up and down)"), + plus: false, + }]), + ]), + NavigateToolFsmState::Rotating => HintData(vec![HintGroup(vec![HintInfo { + key_groups: vec![KeysGroup(vec![Key::KeyControl])], + mouse: None, + label: String::from("Snap to 15° increments"), + plus: false, + }])]), + _ => HintData(Vec::new()), + }; + + responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into()); + } }