From ed5a8f56ed44a4368904fc948d228c18e35aa4be Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Tue, 4 Jan 2022 19:12:58 +0000 Subject: [PATCH 01/10] Snap zoom --- editor/src/document/document_file.rs | 8 +-- editor/src/document/movement_handler.rs | 77 +++++++++++-------------- editor/src/input/input_mapper.rs | 6 +- editor/src/tool/tools/navigate.rs | 32 +++++++--- 4 files changed, 65 insertions(+), 58 deletions(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index f33fb1404f..7e4ba29f92 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -760,9 +760,9 @@ impl MessageHandler for DocumentMessageHand } .into(), ); - let document_transform = &self.movement_handler; + let document_transform_scale = self.movement_handler.snapped_scale(); - let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform.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]); @@ -773,9 +773,9 @@ impl MessageHandler for DocumentMessageHand let scrollbar_multiplier = bounds_length - viewport_size; let scrollbar_size = viewport_size / bounds_length; - let log = document_transform.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 * document_transform.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/movement_handler.rs b/editor/src/document/movement_handler.rs index e5d625bf82..f334856589 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -17,7 +17,7 @@ use std::collections::VecDeque; #[impl_message(Message, DocumentMessage, Movement)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub enum MovementMessage { - MouseMove { snap_angle: Key }, + MouseMove { snap_angle: Key, snap_zoom: Key }, TranslateCanvasBegin, WheelCanvasTranslate { use_y_as_x: bool }, RotateCanvasBegin, @@ -42,6 +42,7 @@ pub struct MovementMessageHandler { zooming: bool, pub scale: f64, snap_rotate: bool, + snap_scale: bool, mouse_pos: ViewportPosition, } @@ -55,6 +56,7 @@ impl Default for MovementMessageHandler { rotation: 0., zooming: false, snap_rotate: false, + snap_scale: false, mouse_pos: ViewportPosition::default(), } } @@ -69,10 +71,20 @@ impl MovementMessageHandler { self.rotation } } + pub fn snapped_scale(&self) -> f64 { + if self.snap_scale { + *VIEWPORT_ZOOM_LEVELS + .iter() + .min_by(|a, b| (**a - self.scale).abs().partial_cmp(&(**b - self.scale).abs()).unwrap()) + .unwrap_or(&self.scale) + } else { + self.scale + } + } 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 scale_transform = DAffine2::from_scale(DVec2::splat(self.snapped_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 @@ -80,7 +92,7 @@ impl MovementMessageHandler { fn create_document_transform(&self, viewport_bounds: &ViewportBounds, responses: &mut VecDeque) { let half_viewport = viewport_bounds.size() / 2.; - let scaled_half_viewport = half_viewport / self.scale; + let scaled_half_viewport = half_viewport / self.snapped_scale(); responses.push_back( DocumentOperation::SetLayerTransform { path: vec![], @@ -110,20 +122,19 @@ impl MessageHandler for Moveme } TransformCanvasEnd => { self.rotation = self.snapped_angle(); + self.scale = self.snapped_scale(); responses.push_back(ToolMessage::DocumentIsDirty.into()); self.snap_rotate = false; + self.snap_scale = false; self.translating = false; self.rotating = false; self.zooming = false; } - MouseMove { snap_angle } => { + MouseMove { snap_angle, snap_zoom } => { if self.translating { let delta = ipp.mouse.position - self.mouse_pos; - let transformed_delta = document.root.transform.inverse().transform_vector2(delta); - self.translation += transformed_delta; - responses.push_back(ToolMessage::DocumentIsDirty.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(TranslateCanvas(delta).into()); } if self.rotating { @@ -141,48 +152,37 @@ impl MessageHandler for Moveme start_vec.angle_between(end_vec) }; - self.rotation += rotation; - responses.push_back(ToolMessage::DocumentIsDirty.into()); - responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: self.snapped_angle() }.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(SetCanvasRotation(self.rotation + rotation).into()); } if self.zooming { + let new_snap = ipp.keyboard.get(snap_zoom as usize); + // When disabling snap, keep the viewed zoom as it was previously. + if !new_snap && self.snap_scale { + self.scale = self.snapped_scale(); + } + self.snap_scale = new_snap; + let difference = self.mouse_pos.y as f64 - ipp.mouse.position.y as f64; let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE; - let new = (self.scale * amount).clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); - self.scale = new; - responses.push_back(ToolMessage::DocumentIsDirty.into()); - responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(SetCanvasZoom(self.scale * amount).into()); } self.mouse_pos = ipp.mouse.position; } SetCanvasZoom(new) => { 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(FrontendMessage::SetCanvasZoom { new_zoom: self.snapped_scale() }.into()); responses.push_back(ToolMessage::DocumentIsDirty.into()); responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); self.create_document_transform(&ipp.viewport_bounds, responses); } IncreaseCanvasZoom => { - // TODO: Eliminate redundant code by making this call SetCanvasZoom - 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::DocumentIsDirty.into()); - responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(SetCanvasZoom(*VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.scale).unwrap_or(&self.scale)).into()); } DecreaseCanvasZoom => { - // TODO: Eliminate redundant code by making this call SetCanvasZoom - 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::DocumentIsDirty.into()); - responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(SetCanvasZoom(*VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.scale).unwrap_or(&self.scale)).into()); } WheelCanvasZoom => { - // TODO: Eliminate redundant code by making this call SetCanvasZoom let scroll = ipp.mouse.scroll_delta.scroll_delta(); let mouse = ipp.mouse.position; let viewport_bounds = ipp.viewport_bounds.size(); @@ -195,24 +195,15 @@ impl MessageHandler for Moveme let mouse_fraction = mouse / viewport_bounds; let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction); - let transformed_delta = document.root.transform.inverse().transform_vector2(delta); - let new = (self.scale * zoom_factor).clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); - self.scale = new; - self.translation += transformed_delta; - responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into()); - responses.push_back(ToolMessage::DocumentIsDirty.into()); - responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(TranslateCanvas(delta).into()); + responses.push_back(SetCanvasZoom(self.scale * zoom_factor).into()); } WheelCanvasTranslate { use_y_as_x } => { let delta = match use_y_as_x { false => -ipp.mouse.scroll_delta.as_dvec2(), true => (-ipp.mouse.scroll_delta.y as f64, 0.).into(), } * VIEWPORT_SCROLL_RATE; - let transformed_delta = document.root.transform.inverse().transform_vector2(delta); - self.translation += transformed_delta; - responses.push_back(ToolMessage::DocumentIsDirty.into()); - self.create_document_transform(&ipp.viewport_bounds, responses); + responses.push_back(TranslateCanvas(delta).into()); } SetCanvasRotation(new_radians) => { self.rotation = new_radians; diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index fdc5eac129..e5e70609e8 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -154,11 +154,13 @@ impl Default for Mapping { 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::MouseMove{snap_angle: KeyControl, snap_zoom: KeyShift}, message=InputMapperMessage::PointerMove}, + entry! {action=NavigateMessage::TranslateCanvasBegin, key_down=Mmb}, 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}, + entry! {action=NavigateMessage::TransformCanvasEnd, key_up=Mmb}, // Eyedropper entry! {action=EyedropperMessage::LeftMouseDown, key_down=Lmb}, entry! {action=EyedropperMessage::RightMouseDown, key_down=Rmb}, @@ -231,7 +233,7 @@ impl Default for Mapping { entry! {action=TransformLayerMessage::BeginRotate, key_down=KeyR}, entry! {action=TransformLayerMessage::BeginScale, key_down=KeyS}, // Document movement - entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift}, message=InputMapperMessage::PointerMove}, + entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift, snap_zoom: KeyControl}, message=InputMapperMessage::PointerMove}, entry! {action=MovementMessage::RotateCanvasBegin, key_down=Mmb, modifiers=[KeyControl]}, entry! {action=MovementMessage::ZoomCanvasBegin, key_down=Mmb, modifiers=[KeyShift]}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Mmb}, diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index 6e584f3ad9..e7ff0bfd12 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -12,7 +12,7 @@ pub struct Navigate { #[impl_message(Message, ToolMessage, Navigate)] #[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum NavigateMessage { - MouseMove { snap_angle: Key }, + MouseMove { snap_angle: Key, snap_zoom: Key }, TranslateCanvasBegin, RotateCanvasBegin, ZoomCanvasBegin, @@ -73,8 +73,8 @@ impl Fsm for NavigateToolFsmState { if let ToolMessage::Navigate(navigate) = message { use NavigateMessage::*; match navigate { - MouseMove { snap_angle } => { - messages.push_front(MovementMessage::MouseMove { snap_angle }.into()); + MouseMove { snap_angle, snap_zoom } => { + messages.push_front(MovementMessage::MouseMove { snap_angle, snap_zoom }.into()); self } TranslateCanvasBegin => { @@ -126,12 +126,20 @@ impl Fsm for NavigateToolFsmState { 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, - }]), + HintGroup(vec![ + HintInfo { + key_groups: vec![], + mouse: Some(MouseMotion::LmbDrag), + label: String::from("Zoom in and out (drag up and down)"), + plus: false, + }, + HintInfo { + key_groups: vec![KeysGroup(vec![Key::KeyShift])], + mouse: None, + label: String::from("Snap zoom to nearest increment"), + plus: true, + }, + ]), ]), NavigateToolFsmState::Rotating => HintData(vec![HintGroup(vec![HintInfo { key_groups: vec![KeysGroup(vec![Key::KeyControl])], @@ -139,6 +147,12 @@ impl Fsm for NavigateToolFsmState { label: String::from("Snap to 15° increments"), plus: false, }])]), + NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo { + key_groups: vec![KeysGroup(vec![Key::KeyShift])], + mouse: None, + label: String::from("Snap zoom to nearest increment"), + plus: false, + }])]), _ => HintData(Vec::new()), }; From 585e1e2f3c949de478fc23e32afa7b58ffcb0df7 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Tue, 4 Jan 2022 19:33:25 +0000 Subject: [PATCH 02/10] Navigate zoom from centre --- editor/src/document/movement_handler.rs | 31 ++++++++++++++++--------- editor/src/input/input_mapper.rs | 2 +- editor/src/tool/tools/navigate.rs | 27 +++++++++++++++++---- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index f334856589..af85a9eb39 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -17,7 +17,7 @@ use std::collections::VecDeque; #[impl_message(Message, DocumentMessage, Movement)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub enum MovementMessage { - MouseMove { snap_angle: Key, snap_zoom: Key }, + MouseMove { snap_angle: Key, snap_zoom: Key, zoom_from_viewport: Option }, TranslateCanvasBegin, WheelCanvasTranslate { use_y_as_x: bool }, RotateCanvasBegin, @@ -101,6 +101,14 @@ impl MovementMessageHandler { .into(), ); } + pub fn centre_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> [Message; 2] { + let new_viewport_bounds = viewport_bounds / zoom_factor; + let delta_size = viewport_bounds - new_viewport_bounds; + let mouse_fraction = mouse / viewport_bounds; + let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction); + + [MovementMessage::TranslateCanvas(delta).into(), MovementMessage::SetCanvasZoom(self.scale * zoom_factor).into()] + } } impl MessageHandler for MovementMessageHandler { @@ -130,7 +138,11 @@ impl MessageHandler for Moveme self.rotating = false; self.zooming = false; } - MouseMove { snap_angle, snap_zoom } => { + MouseMove { + snap_angle, + snap_zoom, + zoom_from_viewport, + } => { if self.translating { let delta = ipp.mouse.position - self.mouse_pos; @@ -165,7 +177,11 @@ impl MessageHandler for Moveme let difference = self.mouse_pos.y as f64 - ipp.mouse.position.y as f64; let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE; - responses.push_back(SetCanvasZoom(self.scale * amount).into()); + if let Some(mouse) = zoom_from_viewport { + responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), amount, mouse)); + } else { + responses.push_back(SetCanvasZoom(self.scale * amount).into()); + } } self.mouse_pos = ipp.mouse.position; } @@ -184,19 +200,12 @@ impl MessageHandler for Moveme } WheelCanvasZoom => { let scroll = ipp.mouse.scroll_delta.scroll_delta(); - let mouse = ipp.mouse.position; - let viewport_bounds = ipp.viewport_bounds.size(); let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE; if ipp.mouse.scroll_delta.y > 0 { zoom_factor = 1. / zoom_factor }; - let new_viewport_bounds = viewport_bounds / zoom_factor; - let delta_size = viewport_bounds - new_viewport_bounds; - let mouse_fraction = mouse / viewport_bounds; - let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction); - responses.push_back(TranslateCanvas(delta).into()); - responses.push_back(SetCanvasZoom(self.scale * zoom_factor).into()); + responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position)); } WheelCanvasTranslate { use_y_as_x } => { let delta = match use_y_as_x { diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index e5e70609e8..8071dbd3be 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -233,7 +233,7 @@ impl Default for Mapping { entry! {action=TransformLayerMessage::BeginRotate, key_down=KeyR}, entry! {action=TransformLayerMessage::BeginScale, key_down=KeyS}, // Document movement - entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift, snap_zoom: KeyControl}, message=InputMapperMessage::PointerMove}, + entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift, snap_zoom: KeyControl, zoom_from_viewport: None}, message=InputMapperMessage::PointerMove}, entry! {action=MovementMessage::RotateCanvasBegin, key_down=Mmb, modifiers=[KeyControl]}, entry! {action=MovementMessage::ZoomCanvasBegin, key_down=Mmb, modifiers=[KeyShift]}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Mmb}, diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index e7ff0bfd12..6783871f7f 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -2,11 +2,13 @@ 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 glam::DVec2; use serde::{Deserialize, Serialize}; #[derive(Default)] pub struct Navigate { fsm_state: NavigateToolFsmState, + data: NavigateToolData, } #[impl_message(Message, ToolMessage, Navigate)] @@ -27,7 +29,7 @@ impl<'a> MessageHandler> for Navigate { return; } - let new_state = self.fsm_state.transition(action, data.0, data.1, &mut (), data.2, responses); + let new_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses); if self.fsm_state != new_state { self.fsm_state = new_state; @@ -58,34 +60,49 @@ impl Default for NavigateToolFsmState { } } +#[derive(Clone, Debug, Default)] +struct NavigateToolData { + drag_start: DVec2, +} + impl Fsm for NavigateToolFsmState { - type ToolData = (); + type ToolData = NavigateToolData; fn transition( self, message: ToolMessage, _document: &crate::document::DocumentMessageHandler, _tool_data: &crate::tool::DocumentToolData, - _data: &mut Self::ToolData, - _input: &crate::input::InputPreprocessor, + 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, snap_zoom } => { - messages.push_front(MovementMessage::MouseMove { snap_angle, snap_zoom }.into()); + messages.push_front( + MovementMessage::MouseMove { + snap_angle, + snap_zoom, + zoom_from_viewport: Some(data.drag_start), + } + .into(), + ); self } TranslateCanvasBegin => { + data.drag_start = input.mouse.position; messages.push_front(MovementMessage::TranslateCanvasBegin.into()); NavigateToolFsmState::Translating } RotateCanvasBegin => { + data.drag_start = input.mouse.position; messages.push_front(MovementMessage::RotateCanvasBegin.into()); NavigateToolFsmState::Rotating } ZoomCanvasBegin => { + data.drag_start = input.mouse.position; messages.push_front(MovementMessage::ZoomCanvasBegin.into()); NavigateToolFsmState::Zooming } From aed5d047cfa1a4af3ad01b8b12b564b109179443 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Tue, 4 Jan 2022 20:12:02 +0000 Subject: [PATCH 03/10] Ctrl to snap zoom in navigate --- editor/src/input/input_mapper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index 8071dbd3be..98bb55ca0f 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -154,7 +154,7 @@ impl Default for Mapping { entry! {action=SelectMessage::Abort, key_down=Rmb}, entry! {action=SelectMessage::Abort, key_down=KeyEscape}, // Navigate - entry! {action=NavigateMessage::MouseMove{snap_angle: KeyControl, snap_zoom: KeyShift}, message=InputMapperMessage::PointerMove}, + entry! {action=NavigateMessage::MouseMove{snap_angle: KeyControl, snap_zoom: KeyControl}, message=InputMapperMessage::PointerMove}, entry! {action=NavigateMessage::TranslateCanvasBegin, key_down=Mmb}, entry! {action=NavigateMessage::RotateCanvasBegin, key_down=Rmb}, entry! {action=NavigateMessage::ZoomCanvasBegin, key_down=Lmb}, From 2a995bd246f20ce77f2d7375a1abe01635534f16 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Tue, 4 Jan 2022 20:20:51 +0000 Subject: [PATCH 04/10] Use ctrl for global snap rotate --- editor/src/document/movement_handler.rs | 30 ++++++++++++++++++++----- editor/src/input/input_mapper.rs | 2 +- editor/src/tool/tools/navigate.rs | 1 + 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index af85a9eb39..8b3d5fd08c 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -17,9 +17,16 @@ use std::collections::VecDeque; #[impl_message(Message, DocumentMessage, Movement)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub enum MovementMessage { - MouseMove { snap_angle: Key, snap_zoom: Key, zoom_from_viewport: Option }, + MouseMove { + snap_angle: Key, + wait_for_snap_angle_release: bool, + snap_zoom: Key, + zoom_from_viewport: Option, + }, TranslateCanvasBegin, - WheelCanvasTranslate { use_y_as_x: bool }, + WheelCanvasTranslate { + use_y_as_x: bool, + }, RotateCanvasBegin, ZoomCanvasBegin, TransformCanvasEnd, @@ -37,12 +44,17 @@ pub enum MovementMessage { pub struct MovementMessageHandler { translating: bool, pub translation: DVec2, + rotating: bool, pub rotation: f64, + zooming: bool, pub scale: f64, + snap_rotate: bool, snap_scale: bool, + released_snap_rotate: bool, + mouse_pos: ViewportPosition, } @@ -56,6 +68,7 @@ impl Default for MovementMessageHandler { rotation: 0., zooming: false, snap_rotate: false, + released_snap_rotate: false, snap_scale: false, mouse_pos: ViewportPosition::default(), } @@ -133,6 +146,7 @@ impl MessageHandler for Moveme self.scale = self.snapped_scale(); responses.push_back(ToolMessage::DocumentIsDirty.into()); self.snap_rotate = false; + self.released_snap_rotate = false; self.snap_scale = false; self.translating = false; self.rotating = false; @@ -140,6 +154,7 @@ impl MessageHandler for Moveme } MouseMove { snap_angle, + wait_for_snap_angle_release, snap_zoom, zoom_from_viewport, } => { @@ -151,11 +166,14 @@ impl MessageHandler for Moveme 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(); + if !(wait_for_snap_angle_release && new_snap && !self.released_snap_rotate) { + // 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; + self.released_snap_rotate = true; } - self.snap_rotate = new_snap; let half_viewport = ipp.viewport_bounds.size() / 2.; let rotation = { diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index 98bb55ca0f..0cae1f0d27 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -233,7 +233,7 @@ impl Default for Mapping { entry! {action=TransformLayerMessage::BeginRotate, key_down=KeyR}, entry! {action=TransformLayerMessage::BeginScale, key_down=KeyS}, // Document movement - entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift, snap_zoom: KeyControl, zoom_from_viewport: None}, message=InputMapperMessage::PointerMove}, + entry! {action=MovementMessage::MouseMove{snap_angle: KeyControl, wait_for_snap_angle_release: true, snap_zoom: KeyControl, zoom_from_viewport: None}, message=InputMapperMessage::PointerMove}, entry! {action=MovementMessage::RotateCanvasBegin, key_down=Mmb, modifiers=[KeyControl]}, entry! {action=MovementMessage::ZoomCanvasBegin, key_down=Mmb, modifiers=[KeyShift]}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Mmb}, diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index 6783871f7f..f36cbf93c3 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -84,6 +84,7 @@ impl Fsm for NavigateToolFsmState { messages.push_front( MovementMessage::MouseMove { snap_angle, + wait_for_snap_angle_release: false, snap_zoom, zoom_from_viewport: Some(data.drag_start), } From 3100d6b1a1ec748f127694a4de94502ffc14c35d Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Tue, 4 Jan 2022 20:23:57 +0000 Subject: [PATCH 05/10] Fix the rotation input on snap rotate --- editor/src/document/movement_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index 8b3d5fd08c..0079d0287c 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -236,7 +236,7 @@ impl MessageHandler for Moveme self.rotation = new_radians; self.create_document_transform(&ipp.viewport_bounds, responses); responses.push_back(ToolMessage::DocumentIsDirty.into()); - responses.push_back(FrontendMessage::SetCanvasRotation { new_radians }.into()); + responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: self.snapped_angle() }.into()); } ZoomCanvasToFitAll => { if let Some([pos1, pos2]) = document.visible_layers_bounding_box() { From b7ed91c538502d662590322fe6f79bcb07a27361 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Wed, 5 Jan 2022 18:46:27 +0000 Subject: [PATCH 06/10] Update hint to use ctrl --- editor/src/tool/tools/navigate.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index f36cbf93c3..4e4dec1112 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -152,9 +152,9 @@ impl Fsm for NavigateToolFsmState { plus: false, }, HintInfo { - key_groups: vec![KeysGroup(vec![Key::KeyShift])], + key_groups: vec![KeysGroup(vec![Key::KeyControl])], mouse: None, - label: String::from("Snap zoom to nearest increment"), + label: String::from("Snap to increment"), plus: true, }, ]), @@ -166,9 +166,9 @@ impl Fsm for NavigateToolFsmState { plus: false, }])]), NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo { - key_groups: vec![KeysGroup(vec![Key::KeyShift])], + key_groups: vec![KeysGroup(vec![Key::KeyControl])], mouse: None, - label: String::from("Snap zoom to nearest increment"), + label: String::from("Snap to increment"), plus: false, }])]), _ => HintData(Vec::new()), From 9e1d6ecdf3ee3665f3088a492a11d62e30a65b5f Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Wed, 5 Jan 2022 18:46:59 +0000 Subject: [PATCH 07/10] Fix mouse centre on drag --- editor/src/document/movement_handler.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index 0079d0287c..177dbb5580 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -185,6 +185,8 @@ impl MessageHandler for Moveme responses.push_back(SetCanvasRotation(self.rotation + rotation).into()); } if self.zooming { + let zoom_start = self.snapped_scale(); + let new_snap = ipp.keyboard.get(snap_zoom as usize); // When disabling snap, keep the viewed zoom as it was previously. if !new_snap && self.snap_scale { @@ -196,7 +198,9 @@ impl MessageHandler for Moveme let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE; if let Some(mouse) = zoom_from_viewport { - responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), amount, mouse)); + self.scale *= amount; + let zoom_factor = self.snapped_scale() / zoom_start; + responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse)); } else { responses.push_back(SetCanvasZoom(self.scale * amount).into()); } From 39db442233c486550b502640ac295c6376f0c812 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Wed, 5 Jan 2022 19:06:36 +0000 Subject: [PATCH 08/10] Click to zoom in --- editor/src/document/movement_handler.rs | 26 +++++++++++++++++++------ editor/src/input/input_mapper.rs | 8 +++++--- editor/src/tool/tools/navigate.rs | 19 +++++++++++++++++- frontend/wasm/src/api.rs | 4 ++-- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index 177dbb5580..c82a871e0f 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -32,8 +32,12 @@ pub enum MovementMessage { TransformCanvasEnd, SetCanvasRotation(f64), SetCanvasZoom(f64), - IncreaseCanvasZoom, - DecreaseCanvasZoom, + IncreaseCanvasZoom { + centre_mouse: bool, + }, + DecreaseCanvasZoom { + centre_mouse: bool, + }, WheelCanvasZoom, ZoomCanvasToFitAll, TranslateCanvas(DVec2), @@ -214,11 +218,21 @@ impl MessageHandler for Moveme responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); self.create_document_transform(&ipp.viewport_bounds, responses); } - IncreaseCanvasZoom => { - responses.push_back(SetCanvasZoom(*VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.scale).unwrap_or(&self.scale)).into()); + IncreaseCanvasZoom { centre_mouse } => { + let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.scale).unwrap_or(&self.scale); + if centre_mouse { + responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); + } else { + responses.push_back(SetCanvasZoom(new_scale).into()); + } } - DecreaseCanvasZoom => { - responses.push_back(SetCanvasZoom(*VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.scale).unwrap_or(&self.scale)).into()); + DecreaseCanvasZoom { centre_mouse } => { + let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.scale).unwrap_or(&self.scale); + if centre_mouse { + responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); + } else { + responses.push_back(SetCanvasZoom(new_scale).into()); + } } WheelCanvasZoom => { let scroll = ipp.mouse.scroll_delta.scroll_delta(); diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index 0cae1f0d27..6733b07224 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -154,6 +154,8 @@ impl Default for Mapping { entry! {action=SelectMessage::Abort, key_down=Rmb}, entry! {action=SelectMessage::Abort, key_down=KeyEscape}, // Navigate + entry! {action=NavigateMessage::ClickZoom{zoom_in: false}, key_up=Lmb, modifiers=[KeyShift]}, + entry! {action=NavigateMessage::ClickZoom{zoom_in: true}, key_up=Lmb}, entry! {action=NavigateMessage::MouseMove{snap_angle: KeyControl, snap_zoom: KeyControl}, message=InputMapperMessage::PointerMove}, entry! {action=NavigateMessage::TranslateCanvasBegin, key_down=Mmb}, entry! {action=NavigateMessage::RotateCanvasBegin, key_down=Rmb}, @@ -240,9 +242,9 @@ impl Default for Mapping { entry! {action=MovementMessage::TransformCanvasEnd, key_up=Mmb}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Lmb, modifiers=[KeySpace]}, entry! {action=MovementMessage::TransformCanvasEnd, key_up=Lmb, modifiers=[KeySpace]}, - entry! {action=MovementMessage::IncreaseCanvasZoom, key_down=KeyPlus, modifiers=[KeyControl]}, - entry! {action=MovementMessage::IncreaseCanvasZoom, key_down=KeyEquals, modifiers=[KeyControl]}, - entry! {action=MovementMessage::DecreaseCanvasZoom, key_down=KeyMinus, modifiers=[KeyControl]}, + entry! {action=MovementMessage::IncreaseCanvasZoom{centre_mouse: false}, key_down=KeyPlus, modifiers=[KeyControl]}, + entry! {action=MovementMessage::IncreaseCanvasZoom{centre_mouse: false}, key_down=KeyEquals, modifiers=[KeyControl]}, + entry! {action=MovementMessage::DecreaseCanvasZoom{centre_mouse: false}, 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]}, diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index 4e4dec1112..b92c9758ac 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -14,6 +14,7 @@ pub struct Navigate { #[impl_message(Message, ToolMessage, Navigate)] #[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)] pub enum NavigateMessage { + ClickZoom { zoom_in: bool }, MouseMove { snap_angle: Key, snap_zoom: Key }, TranslateCanvasBegin, RotateCanvasBegin, @@ -41,7 +42,7 @@ impl<'a> MessageHandler> for Navigate { use NavigateToolFsmState::*; match self.fsm_state { Ready => actions!(NavigateMessageDiscriminant; TranslateCanvasBegin, RotateCanvasBegin, ZoomCanvasBegin), - _ => actions!(NavigateMessageDiscriminant; MouseMove, TransformCanvasEnd), + _ => actions!(NavigateMessageDiscriminant; ClickZoom, MouseMove, TransformCanvasEnd), } } } @@ -80,6 +81,22 @@ impl Fsm for NavigateToolFsmState { if let ToolMessage::Navigate(navigate) = message { use NavigateMessage::*; match navigate { + ClickZoom { zoom_in } => { + messages.push_front(MovementMessage::TransformCanvasEnd.into()); + + if data.drag_start == (input.mouse.position) { + messages.push_front( + if zoom_in { + MovementMessage::IncreaseCanvasZoom { centre_mouse: true } + } else { + MovementMessage::DecreaseCanvasZoom { centre_mouse: true } + } + .into(), + ); + } + + NavigateToolFsmState::Ready + } MouseMove { snap_angle, snap_zoom } => { messages.push_front( MovementMessage::MouseMove { diff --git a/frontend/wasm/src/api.rs b/frontend/wasm/src/api.rs index e4ac0951b1..6a035d59a8 100644 --- a/frontend/wasm/src/api.rs +++ b/frontend/wasm/src/api.rs @@ -438,13 +438,13 @@ impl JsEditorHandle { /// Zoom in to the next step pub fn increase_canvas_zoom(&self) { - let message = MovementMessage::IncreaseCanvasZoom; + let message = MovementMessage::IncreaseCanvasZoom { centre_mouse: false }; self.dispatch(message); } /// Zoom out to the next step pub fn decrease_canvas_zoom(&self) { - let message = MovementMessage::DecreaseCanvasZoom; + let message = MovementMessage::DecreaseCanvasZoom { centre_mouse: false }; self.dispatch(message); } From 9f4b2a1dcae9220ec2b4789a4321c94b8b3f89ab Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Wed, 5 Jan 2022 19:11:51 +0000 Subject: [PATCH 09/10] Clean up centre zoom --- editor/src/document/movement_handler.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index c82a871e0f..f4f21adeca 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -118,13 +118,13 @@ impl MovementMessageHandler { .into(), ); } - pub fn centre_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> [Message; 2] { + pub fn centre_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> Message { let new_viewport_bounds = viewport_bounds / zoom_factor; let delta_size = viewport_bounds - new_viewport_bounds; let mouse_fraction = mouse / viewport_bounds; let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction); - [MovementMessage::TranslateCanvas(delta).into(), MovementMessage::SetCanvasZoom(self.scale * zoom_factor).into()] + MovementMessage::TranslateCanvas(delta).into() } } @@ -201,12 +201,13 @@ impl MessageHandler for Moveme let difference = self.mouse_pos.y as f64 - ipp.mouse.position.y as f64; let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE; + self.scale *= amount; if let Some(mouse) = zoom_from_viewport { - self.scale *= amount; let zoom_factor = self.snapped_scale() / zoom_start; - responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse)); + responses.push_back(SetCanvasZoom(self.scale).into()); + responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse)); } else { - responses.push_back(SetCanvasZoom(self.scale * amount).into()); + responses.push_back(SetCanvasZoom(self.scale).into()); } } self.mouse_pos = ipp.mouse.position; @@ -221,18 +222,16 @@ impl MessageHandler for Moveme IncreaseCanvasZoom { centre_mouse } => { let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.scale).unwrap_or(&self.scale); if centre_mouse { - responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); - } else { - responses.push_back(SetCanvasZoom(new_scale).into()); + responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); } + responses.push_back(SetCanvasZoom(new_scale).into()); } DecreaseCanvasZoom { centre_mouse } => { let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.scale).unwrap_or(&self.scale); if centre_mouse { - responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); - } else { - responses.push_back(SetCanvasZoom(new_scale).into()); + responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); } + responses.push_back(SetCanvasZoom(new_scale).into()); } WheelCanvasZoom => { let scroll = ipp.mouse.scroll_delta.scroll_delta(); @@ -241,7 +240,8 @@ impl MessageHandler for Moveme zoom_factor = 1. / zoom_factor }; - responses.extend(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position)); + responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position)); + responses.push_back(SetCanvasZoom(self.scale * zoom_factor).into()); } WheelCanvasTranslate { use_y_as_x } => { let delta = match use_y_as_x { From f882a814c546d6958e841099b53e30a72d38feaa Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Thu, 6 Jan 2022 02:03:35 -0800 Subject: [PATCH 10/10] Update user input hints; tweak some variable names for clarity and standardization --- editor/src/document/movement_handler.rs | 166 ++++++++++++------------ editor/src/input/input_mapper.rs | 32 ++--- editor/src/tool/tools/navigate.rs | 68 ++++++---- frontend/wasm/src/api.rs | 4 +- 4 files changed, 144 insertions(+), 126 deletions(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index f4f21adeca..8d16878b51 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -33,10 +33,10 @@ pub enum MovementMessage { SetCanvasRotation(f64), SetCanvasZoom(f64), IncreaseCanvasZoom { - centre_mouse: bool, + center_on_mouse: bool, }, DecreaseCanvasZoom { - centre_mouse: bool, + center_on_mouse: bool, }, WheelCanvasZoom, ZoomCanvasToFitAll, @@ -46,35 +46,37 @@ pub enum MovementMessage { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MovementMessageHandler { - translating: bool, - pub translation: DVec2, + pub pan: DVec2, + panning: bool, + snap_tilt: bool, + snap_tilt_released: bool, - rotating: bool, - pub rotation: f64, + pub tilt: f64, + tilting: bool, + pub zoom: f64, zooming: bool, - pub scale: f64, + snap_zoom: bool, - snap_rotate: bool, - snap_scale: bool, - released_snap_rotate: bool, - - mouse_pos: ViewportPosition, + mouse_position: ViewportPosition, } impl Default for MovementMessageHandler { fn default() -> Self { Self { - scale: 1., - translating: false, - translation: DVec2::ZERO, - rotating: false, - rotation: 0., + pan: DVec2::ZERO, + panning: false, + snap_tilt: false, + snap_tilt_released: false, + + tilt: 0., + tilting: false, + + zoom: 1., zooming: false, - snap_rotate: false, - released_snap_rotate: false, - snap_scale: false, - mouse_pos: ViewportPosition::default(), + snap_zoom: false, + + mouse_position: ViewportPosition::default(), } } } @@ -82,28 +84,30 @@ impl Default for MovementMessageHandler { impl MovementMessageHandler { pub fn snapped_angle(&self) -> f64 { let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians(); - if self.snap_rotate { - (self.rotation / increment_radians).round() * increment_radians + if self.snap_tilt { + (self.tilt / increment_radians).round() * increment_radians } else { - self.rotation + self.tilt } } + pub fn snapped_scale(&self) -> f64 { - if self.snap_scale { + if self.snap_zoom { *VIEWPORT_ZOOM_LEVELS .iter() - .min_by(|a, b| (**a - self.scale).abs().partial_cmp(&(**b - self.scale).abs()).unwrap()) - .unwrap_or(&self.scale) + .min_by(|a, b| (**a - self.zoom).abs().partial_cmp(&(**b - self.zoom).abs()).unwrap()) + .unwrap_or(&self.zoom) } else { - self.scale + self.zoom } } + 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::splat(self.snapped_scale())); let angle_transform = DAffine2::from_angle(self.snapped_angle()); - let translation_transform = DAffine2::from_translation(self.translation); + let translation_transform = DAffine2::from_translation(self.pan); scale_transform * offset_transform * angle_transform * translation_transform } @@ -118,7 +122,7 @@ impl MovementMessageHandler { .into(), ); } - pub fn centre_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> Message { + pub fn center_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> Message { let new_viewport_bounds = viewport_bounds / zoom_factor; let delta_size = viewport_bounds - new_viewport_bounds; let mouse_fraction = mouse / viewport_bounds; @@ -134,26 +138,26 @@ impl MessageHandler for Moveme use MovementMessage::*; match message { TranslateCanvasBegin => { - self.translating = true; - self.mouse_pos = ipp.mouse.position; + self.panning = true; + self.mouse_position = ipp.mouse.position; } RotateCanvasBegin => { - self.rotating = true; - self.mouse_pos = ipp.mouse.position; + self.tilting = true; + self.mouse_position = ipp.mouse.position; } ZoomCanvasBegin => { self.zooming = true; - self.mouse_pos = ipp.mouse.position; + self.mouse_position = ipp.mouse.position; } TransformCanvasEnd => { - self.rotation = self.snapped_angle(); - self.scale = self.snapped_scale(); + self.tilt = self.snapped_angle(); + self.zoom = self.snapped_scale(); responses.push_back(ToolMessage::DocumentIsDirty.into()); - self.snap_rotate = false; - self.released_snap_rotate = false; - self.snap_scale = false; - self.translating = false; - self.rotating = false; + self.snap_tilt = false; + self.snap_tilt_released = false; + self.snap_zoom = false; + self.panning = false; + self.tilting = false; self.zooming = false; } MouseMove { @@ -162,74 +166,76 @@ impl MessageHandler for Moveme snap_zoom, zoom_from_viewport, } => { - if self.translating { - let delta = ipp.mouse.position - self.mouse_pos; + if self.panning { + let delta = ipp.mouse.position - self.mouse_position; responses.push_back(TranslateCanvas(delta).into()); } - if self.rotating { + if self.tilting { let new_snap = ipp.keyboard.get(snap_angle as usize); - if !(wait_for_snap_angle_release && new_snap && !self.released_snap_rotate) { + if !(wait_for_snap_angle_release && new_snap && !self.snap_tilt_released) { // When disabling snap, keep the viewed rotation as it was previously. - if !new_snap && self.snap_rotate { - self.rotation = self.snapped_angle(); + if !new_snap && self.snap_tilt { + self.tilt = self.snapped_angle(); } - self.snap_rotate = new_snap; - self.released_snap_rotate = true; + self.snap_tilt = new_snap; + self.snap_tilt_released = true; } let half_viewport = ipp.viewport_bounds.size() / 2.; let rotation = { - let start_vec = self.mouse_pos - half_viewport; + let start_vec = self.mouse_position - half_viewport; let end_vec = ipp.mouse.position - half_viewport; start_vec.angle_between(end_vec) }; - responses.push_back(SetCanvasRotation(self.rotation + rotation).into()); + responses.push_back(SetCanvasRotation(self.tilt + rotation).into()); } + if self.zooming { let zoom_start = self.snapped_scale(); let new_snap = ipp.keyboard.get(snap_zoom as usize); - // When disabling snap, keep the viewed zoom as it was previously. - if !new_snap && self.snap_scale { - self.scale = self.snapped_scale(); + // When disabling snap, keep the viewed zoom as it was previously + if !new_snap && self.snap_zoom { + self.zoom = self.snapped_scale(); } - self.snap_scale = new_snap; + self.snap_zoom = new_snap; - let difference = self.mouse_pos.y as f64 - ipp.mouse.position.y as f64; + let difference = self.mouse_position.y as f64 - ipp.mouse.position.y as f64; let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE; - self.scale *= amount; + self.zoom *= amount; if let Some(mouse) = zoom_from_viewport { let zoom_factor = self.snapped_scale() / zoom_start; - responses.push_back(SetCanvasZoom(self.scale).into()); - responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse)); + + responses.push_back(SetCanvasZoom(self.zoom).into()); + responses.push_back(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse)); } else { - responses.push_back(SetCanvasZoom(self.scale).into()); + responses.push_back(SetCanvasZoom(self.zoom).into()); } } - self.mouse_pos = ipp.mouse.position; + self.mouse_position = ipp.mouse.position; } SetCanvasZoom(new) => { - self.scale = new.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); + self.zoom = new.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX); responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.snapped_scale() }.into()); responses.push_back(ToolMessage::DocumentIsDirty.into()); responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); self.create_document_transform(&ipp.viewport_bounds, responses); } - IncreaseCanvasZoom { centre_mouse } => { - let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.scale).unwrap_or(&self.scale); - if centre_mouse { - responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); + IncreaseCanvasZoom { center_on_mouse } => { + let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > self.zoom).unwrap_or(&self.zoom); + if center_on_mouse { + responses.push_back(self.center_zoom(ipp.viewport_bounds.size(), new_scale / self.zoom, ipp.mouse.position)); } responses.push_back(SetCanvasZoom(new_scale).into()); } - DecreaseCanvasZoom { centre_mouse } => { - let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.scale).unwrap_or(&self.scale); - if centre_mouse { - responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), new_scale / self.scale, ipp.mouse.position)); + DecreaseCanvasZoom { center_on_mouse } => { + let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < self.zoom).unwrap_or(&self.zoom); + if center_on_mouse { + responses.push_back(self.center_zoom(ipp.viewport_bounds.size(), new_scale / self.zoom, ipp.mouse.position)); } responses.push_back(SetCanvasZoom(new_scale).into()); } @@ -240,8 +246,8 @@ impl MessageHandler for Moveme zoom_factor = 1. / zoom_factor }; - responses.push_back(self.centre_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position)); - responses.push_back(SetCanvasZoom(self.scale * zoom_factor).into()); + responses.push_back(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position)); + responses.push_back(SetCanvasZoom(self.zoom * zoom_factor).into()); } WheelCanvasTranslate { use_y_as_x } => { let delta = match use_y_as_x { @@ -251,7 +257,7 @@ impl MessageHandler for Moveme responses.push_back(TranslateCanvas(delta).into()); } SetCanvasRotation(new_radians) => { - self.rotation = new_radians; + self.tilt = new_radians; self.create_document_transform(&ipp.viewport_bounds, responses); responses.push_back(ToolMessage::DocumentIsDirty.into()); responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: self.snapped_angle() }.into()); @@ -268,9 +274,9 @@ impl MessageHandler for Moveme let size = 1. / size; let new_scale = size.min_element(); - self.translation += center; - self.scale *= new_scale; - responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into()); + self.pan += center; + self.zoom *= new_scale; + responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.zoom }.into()); responses.push_back(ToolMessage::DocumentIsDirty.into()); responses.push_back(DocumentMessage::DirtyRenderDocumentInOutlineView.into()); self.create_document_transform(&ipp.viewport_bounds, responses); @@ -279,14 +285,14 @@ impl MessageHandler for Moveme TranslateCanvas(delta) => { let transformed_delta = document.root.transform.inverse().transform_vector2(delta); - self.translation += transformed_delta; + self.pan += transformed_delta; responses.push_back(ToolMessage::DocumentIsDirty.into()); self.create_document_transform(&ipp.viewport_bounds, responses); } TranslateCanvasByViewportFraction(delta) => { let transformed_delta = document.root.transform.inverse().transform_vector2(delta * ipp.viewport_bounds.size()); - self.translation += transformed_delta; + self.pan += transformed_delta; responses.push_back(ToolMessage::DocumentIsDirty.into()); self.create_document_transform(&ipp.viewport_bounds, responses); } @@ -309,7 +315,7 @@ impl MessageHandler for Moveme TranslateCanvasByViewportFraction, ); - if self.translating || self.rotating || self.zooming { + if self.panning || self.tilting || self.zooming { let transforming = actions!(MovementMessageDiscriminant; TransformCanvasEnd, ); diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index 6733b07224..223a2504f5 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -146,17 +146,17 @@ impl Default for Mapping { entry! {action=TransformLayerMessage::TypeNegate, key_down=KeyMinus}, entry! {action=TransformLayerMessage::TypeDecimalPoint, key_down=KeyComma}, entry! {action=TransformLayerMessage::TypeDecimalPoint, key_down=KeyPeriod}, - entry! {action=TransformLayerMessage::MouseMove{slow_key: KeyShift, snap_key: KeyControl}, triggers=[KeyShift, KeyControl]}, + entry! {action=TransformLayerMessage::MouseMove { slow_key: KeyShift, snap_key: KeyControl }, triggers=[KeyShift, KeyControl]}, // Select - entry! {action=SelectMessage::MouseMove{snap_angle: KeyShift}, message=InputMapperMessage::PointerMove}, - entry! {action=SelectMessage::DragStart{add_to_selection: KeyShift}, key_down=Lmb}, + entry! {action=SelectMessage::MouseMove { snap_angle: KeyShift }, message=InputMapperMessage::PointerMove}, + entry! {action=SelectMessage::DragStart { add_to_selection: KeyShift }, key_down=Lmb}, 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::ClickZoom{zoom_in: false}, key_up=Lmb, modifiers=[KeyShift]}, - entry! {action=NavigateMessage::ClickZoom{zoom_in: true}, key_up=Lmb}, - entry! {action=NavigateMessage::MouseMove{snap_angle: KeyControl, snap_zoom: KeyControl}, message=InputMapperMessage::PointerMove}, + entry! {action=NavigateMessage::ClickZoom { zoom_in: false }, key_up=Lmb, modifiers=[KeyShift]}, + entry! {action=NavigateMessage::ClickZoom { zoom_in: true }, key_up=Lmb}, + entry! {action=NavigateMessage::MouseMove { snap_angle: KeyControl, snap_zoom: KeyControl }, message=InputMapperMessage::PointerMove}, entry! {action=NavigateMessage::TranslateCanvasBegin, key_down=Mmb}, entry! {action=NavigateMessage::RotateCanvasBegin, key_down=Rmb}, entry! {action=NavigateMessage::ZoomCanvasBegin, key_down=Lmb}, @@ -171,25 +171,25 @@ impl Default for Mapping { entry! {action=RectangleMessage::DragStop, key_up=Lmb}, entry! {action=RectangleMessage::Abort, key_down=Rmb}, entry! {action=RectangleMessage::Abort, key_down=KeyEscape}, - entry! {action=RectangleMessage::Resize{center: KeyAlt, lock_ratio: KeyShift}, triggers=[KeyAlt, KeyShift]}, + entry! {action=RectangleMessage::Resize { center: KeyAlt, lock_ratio: KeyShift }, triggers=[KeyAlt, KeyShift]}, // Ellipse entry! {action=EllipseMessage::DragStart, key_down=Lmb}, entry! {action=EllipseMessage::DragStop, key_up=Lmb}, entry! {action=EllipseMessage::Abort, key_down=Rmb}, entry! {action=EllipseMessage::Abort, key_down=KeyEscape}, - entry! {action=EllipseMessage::Resize{center: KeyAlt, lock_ratio: KeyShift}, triggers=[KeyAlt, KeyShift]}, + entry! {action=EllipseMessage::Resize { center: KeyAlt, lock_ratio: KeyShift }, triggers=[KeyAlt, KeyShift]}, // Shape entry! {action=ShapeMessage::DragStart, key_down=Lmb}, entry! {action=ShapeMessage::DragStop, key_up=Lmb}, entry! {action=ShapeMessage::Abort, key_down=Rmb}, entry! {action=ShapeMessage::Abort, key_down=KeyEscape}, - entry! {action=ShapeMessage::Resize{center: KeyAlt, lock_ratio: KeyShift}, triggers=[KeyAlt, KeyShift]}, + entry! {action=ShapeMessage::Resize { center: KeyAlt, lock_ratio: KeyShift }, triggers=[KeyAlt, KeyShift]}, // Line entry! {action=LineMessage::DragStart, key_down=Lmb}, entry! {action=LineMessage::DragStop, key_up=Lmb}, entry! {action=LineMessage::Abort, key_down=Rmb}, entry! {action=LineMessage::Abort, key_down=KeyEscape}, - entry! {action=LineMessage::Redraw{center: KeyAlt, lock_angle: KeyControl, snap_angle: KeyShift}, triggers=[KeyAlt, KeyShift, KeyControl]}, + entry! {action=LineMessage::Redraw { center: KeyAlt, lock_angle: KeyControl, snap_angle: KeyShift }, triggers=[KeyAlt, KeyShift, KeyControl]}, // Pen entry! {action=PenMessage::PointerMove, message=InputMapperMessage::PointerMove}, entry! {action=PenMessage::DragStart, key_down=Lmb}, @@ -235,22 +235,22 @@ impl Default for Mapping { entry! {action=TransformLayerMessage::BeginRotate, key_down=KeyR}, entry! {action=TransformLayerMessage::BeginScale, key_down=KeyS}, // Document movement - entry! {action=MovementMessage::MouseMove{snap_angle: KeyControl, wait_for_snap_angle_release: true, snap_zoom: KeyControl, zoom_from_viewport: None}, message=InputMapperMessage::PointerMove}, + entry! {action=MovementMessage::MouseMove { snap_angle: KeyControl, wait_for_snap_angle_release: true, snap_zoom: KeyControl, zoom_from_viewport: None }, message=InputMapperMessage::PointerMove}, entry! {action=MovementMessage::RotateCanvasBegin, key_down=Mmb, modifiers=[KeyControl]}, entry! {action=MovementMessage::ZoomCanvasBegin, key_down=Mmb, modifiers=[KeyShift]}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Mmb}, entry! {action=MovementMessage::TransformCanvasEnd, key_up=Mmb}, entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Lmb, modifiers=[KeySpace]}, entry! {action=MovementMessage::TransformCanvasEnd, key_up=Lmb, modifiers=[KeySpace]}, - entry! {action=MovementMessage::IncreaseCanvasZoom{centre_mouse: false}, key_down=KeyPlus, modifiers=[KeyControl]}, - entry! {action=MovementMessage::IncreaseCanvasZoom{centre_mouse: false}, key_down=KeyEquals, modifiers=[KeyControl]}, - entry! {action=MovementMessage::DecreaseCanvasZoom{centre_mouse: false}, key_down=KeyMinus, modifiers=[KeyControl]}, + entry! {action=MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }, key_down=KeyPlus, modifiers=[KeyControl]}, + entry! {action=MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }, key_down=KeyEquals, modifiers=[KeyControl]}, + entry! {action=MovementMessage::DecreaseCanvasZoom { center_on_mouse: false }, 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}, + 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}, entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(1., 0.)), key_down=KeyPageUp, modifiers=[KeyShift]}, entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(-1., 0.)), key_down=KeyPageDown, modifiers=[KeyShift]}, entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(0., 1.)), key_down=KeyPageUp}, diff --git a/editor/src/tool/tools/navigate.rs b/editor/src/tool/tools/navigate.rs index b92c9758ac..85a650971b 100644 --- a/editor/src/tool/tools/navigate.rs +++ b/editor/src/tool/tools/navigate.rs @@ -50,8 +50,8 @@ impl<'a> MessageHandler> for Navigate { #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum NavigateToolFsmState { Ready, - Translating, - Rotating, + Panning, + Tilting, Zooming, } @@ -84,15 +84,13 @@ impl Fsm for NavigateToolFsmState { ClickZoom { zoom_in } => { messages.push_front(MovementMessage::TransformCanvasEnd.into()); - if data.drag_start == (input.mouse.position) { - messages.push_front( - if zoom_in { - MovementMessage::IncreaseCanvasZoom { centre_mouse: true } - } else { - MovementMessage::DecreaseCanvasZoom { centre_mouse: true } - } - .into(), - ); + // Mouse has not moved from mousedown to mouseup + if data.drag_start == input.mouse.position { + messages.push_front(if zoom_in { + MovementMessage::IncreaseCanvasZoom { center_on_mouse: true }.into() + } else { + MovementMessage::DecreaseCanvasZoom { center_on_mouse: true }.into() + }); } NavigateToolFsmState::Ready @@ -112,12 +110,12 @@ impl Fsm for NavigateToolFsmState { TranslateCanvasBegin => { data.drag_start = input.mouse.position; messages.push_front(MovementMessage::TranslateCanvasBegin.into()); - NavigateToolFsmState::Translating + NavigateToolFsmState::Panning } RotateCanvasBegin => { data.drag_start = input.mouse.position; messages.push_front(MovementMessage::RotateCanvasBegin.into()); - NavigateToolFsmState::Rotating + NavigateToolFsmState::Tilting } ZoomCanvasBegin => { data.drag_start = input.mouse.position; @@ -141,23 +139,17 @@ impl Fsm for NavigateToolFsmState { 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)"), + mouse: Some(MouseMotion::Lmb), + label: String::from("Zoom In"), plus: false, }, HintInfo { - key_groups: vec![KeysGroup(vec![Key::KeyControl])], + key_groups: vec![KeysGroup(vec![Key::KeyShift])], mouse: None, - label: String::from("Snap rotation to 15° increments"), + label: String::from("Zoom Out"), plus: true, }, ]), @@ -165,27 +157,47 @@ impl Fsm for NavigateToolFsmState { HintInfo { key_groups: vec![], mouse: Some(MouseMotion::LmbDrag), - label: String::from("Zoom in and out (drag up and down)"), + label: String::from("Zoom"), + plus: false, + }, + HintInfo { + key_groups: vec![KeysGroup(vec![Key::KeyControl])], + mouse: None, + label: String::from("Snap Increments"), + plus: true, + }, + ]), + HintGroup(vec![HintInfo { + key_groups: vec![], + mouse: Some(MouseMotion::MmbDrag), + label: String::from("Pan"), + plus: false, + }]), + HintGroup(vec![ + HintInfo { + key_groups: vec![], + mouse: Some(MouseMotion::RmbDrag), + label: String::from("Tilt"), plus: false, }, HintInfo { key_groups: vec![KeysGroup(vec![Key::KeyControl])], mouse: None, - label: String::from("Snap to increment"), + label: String::from("Snap 15°"), plus: true, }, ]), ]), - NavigateToolFsmState::Rotating => HintData(vec![HintGroup(vec![HintInfo { + NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo { key_groups: vec![KeysGroup(vec![Key::KeyControl])], mouse: None, - label: String::from("Snap to 15° increments"), + label: String::from("Snap 15°"), plus: false, }])]), NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo { key_groups: vec![KeysGroup(vec![Key::KeyControl])], mouse: None, - label: String::from("Snap to increment"), + label: String::from("Snap Increments"), plus: false, }])]), _ => HintData(Vec::new()), diff --git a/frontend/wasm/src/api.rs b/frontend/wasm/src/api.rs index 6a035d59a8..f5502445a3 100644 --- a/frontend/wasm/src/api.rs +++ b/frontend/wasm/src/api.rs @@ -438,13 +438,13 @@ impl JsEditorHandle { /// Zoom in to the next step pub fn increase_canvas_zoom(&self) { - let message = MovementMessage::IncreaseCanvasZoom { centre_mouse: false }; + let message = MovementMessage::IncreaseCanvasZoom { center_on_mouse: false }; self.dispatch(message); } /// Zoom out to the next step pub fn decrease_canvas_zoom(&self) { - let message = MovementMessage::DecreaseCanvasZoom { centre_mouse: false }; + let message = MovementMessage::DecreaseCanvasZoom { center_on_mouse: false }; self.dispatch(message); }