From 4bb248979bffc96ede1a64e7e5a6d61425deab82 Mon Sep 17 00:00:00 2001 From: Trevor Paley <10186337+TheUnlocked@users.noreply.github.com> Date: Sat, 11 Oct 2025 22:57:12 -0700 Subject: [PATCH] Add shift-to-constrain along X/Y axis for panning --- .../document/navigation/navigation_message.rs | 1 + .../navigation/navigation_message_handler.rs | 44 +++++++++++++++++-- .../document/navigation/utility_types.rs | 1 + 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message.rs b/editor/src/messages/portfolio/document/navigation/navigation_message.rs index a55f8dbef4..ef8a206c61 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message.rs @@ -10,6 +10,7 @@ pub enum NavigationMessage { BeginCanvasTilt { was_dispatched_from_menu: bool }, BeginCanvasZoom, CanvasPan { delta: DVec2 }, + CanvasPanSet { position: DVec2 }, CanvasPanAbortPrepare { x_not_y_axis: bool }, CanvasPanAbort { x_not_y_axis: bool }, CanvasPanByViewportFraction { delta: DVec2 }, diff --git a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs index 259b60e995..b8ab35f90e 100644 --- a/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs +++ b/editor/src/messages/portfolio/document/navigation/navigation_message_handler.rs @@ -75,14 +75,20 @@ impl MessageHandler> for Navigat responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing }); responses.add(FrontendMessage::UpdateInputHints { - hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]), + hint_data: HintData(vec![ + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), + HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain to Axis")]), + ]), }); self.mouse_position = ipp.mouse.position; let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { return; }; - self.navigation_operation = NavigationOperation::Pan { pan_original_for_abort: ptz.pan }; + self.navigation_operation = NavigationOperation::Pan { + pan_original_for_abort: ptz.pan, + pan_raw_viewport_delta: DVec2::ZERO, + }; } NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu } => { let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { @@ -142,6 +148,16 @@ impl MessageHandler> for Navigat responses.add(EventMessage::CanvasTransformed); responses.add(DocumentMessage::PTZUpdate); } + NavigationMessage::CanvasPanSet { position } => { + let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { + log::error!("Could not get PTZ in CanvasPanSet"); + return; + }; + + ptz.pan = position; + responses.add(EventMessage::CanvasTransformed); + responses.add(DocumentMessage::PTZUpdate); + } NavigationMessage::CanvasPanAbortPrepare { x_not_y_axis } => { let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else { log::error!("Could not get PTZ in CanvasPanAbortPrepare"); @@ -407,9 +423,29 @@ impl MessageHandler> for Navigat NavigationMessage::PointerMove { snap } => { match self.navigation_operation { NavigationOperation::None => {} - NavigationOperation::Pan { .. } => { + NavigationOperation::Pan { + pan_original_for_abort, + pan_raw_viewport_delta, + } => { let delta = ipp.mouse.position - self.mouse_position; - responses.add(NavigationMessage::CanvasPan { delta }); + + let pan_raw_viewport_delta = pan_raw_viewport_delta + delta; + + self.navigation_operation = NavigationOperation::Pan { + pan_original_for_abort, + pan_raw_viewport_delta, + }; + + let mut locked_delta = pan_raw_viewport_delta; + + if ipp.keyboard.get(snap as usize) { + if locked_delta.x.abs() >= locked_delta.y.abs() { locked_delta.y = 0. } else { locked_delta.x = 0. } + } + + let document_to_viewport = self.calculate_offset_transform(ipp.viewport_bounds.center(), ptz); + let new_pan = pan_original_for_abort + document_to_viewport.inverse().transform_vector2(locked_delta); + + responses.add(NavigationMessage::CanvasPanSet { position: new_pan }); } NavigationOperation::Tilt { tilt_raw_not_snapped, diff --git a/editor/src/messages/portfolio/document/navigation/utility_types.rs b/editor/src/messages/portfolio/document/navigation/utility_types.rs index 09684b890e..cf6c5df424 100644 --- a/editor/src/messages/portfolio/document/navigation/utility_types.rs +++ b/editor/src/messages/portfolio/document/navigation/utility_types.rs @@ -6,6 +6,7 @@ pub enum NavigationOperation { None, Pan { pan_original_for_abort: DVec2, + pan_raw_viewport_delta: DVec2, }, Tilt { tilt_original_for_abort: f64,