mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 19:58:13 +08:00
Polish and fix small bugs with tilt and zoom navigation
This commit is contained in:
@@ -35,14 +35,16 @@ pub fn default_mapping() -> Mapping {
|
|||||||
// ===============
|
// ===============
|
||||||
//
|
//
|
||||||
// NavigationMessage
|
// NavigationMessage
|
||||||
entry!(
|
entry!(PointerMove; refresh_keys=[Control], action_dispatch=NavigationMessage::PointerMove { snap: Control }),
|
||||||
PointerMove;
|
entry!(KeyUp(Lmb); action_dispatch=NavigationMessage::EndCanvasPTZ { abort_transform: false }),
|
||||||
refresh_keys=[Control],
|
entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::EndCanvasPTZ { abort_transform: false }),
|
||||||
action_dispatch=NavigationMessage::PointerMove { snap_angle: Control, wait_for_snap_angle_release: true, snap_zoom: Control, zoom_from_viewport: None },
|
entry!(KeyUp(Rmb); action_dispatch=NavigationMessage::EndCanvasPTZ { abort_transform: false }),
|
||||||
),
|
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::EndCanvasPTZ { abort_transform: true }),
|
||||||
entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Lmb }),
|
entry!(KeyDown(Escape); action_dispatch=NavigationMessage::EndCanvasPTZ { abort_transform: true }),
|
||||||
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Mmb }),
|
entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::EndCanvasPTZWithClick { commit_key: Lmb }),
|
||||||
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Rmb }),
|
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::EndCanvasPTZWithClick { commit_key: Mmb }),
|
||||||
|
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::EndCanvasPTZWithClick { commit_key: Rmb }),
|
||||||
|
//
|
||||||
// ===============
|
// ===============
|
||||||
// NORMAL PRIORITY
|
// NORMAL PRIORITY
|
||||||
// ===============
|
// ===============
|
||||||
@@ -116,14 +118,11 @@ pub fn default_mapping() -> Mapping {
|
|||||||
entry!(KeyDown(Escape); action_dispatch=ArtboardToolMessage::Abort),
|
entry!(KeyDown(Escape); action_dispatch=ArtboardToolMessage::Abort),
|
||||||
//
|
//
|
||||||
// NavigateToolMessage
|
// NavigateToolMessage
|
||||||
entry!(KeyUp(Lmb); modifiers=[Shift], action_dispatch=NavigateToolMessage::ClickZoom { zoom_in: false }),
|
|
||||||
entry!(KeyUp(Lmb); action_dispatch=NavigateToolMessage::ClickZoom { zoom_in: true }),
|
|
||||||
entry!(PointerMove; refresh_keys=[Control], action_dispatch=NavigateToolMessage::PointerMove { snap_angle: Control, snap_zoom: Control }),
|
|
||||||
entry!(KeyDown(Lmb); modifiers=[Alt], action_dispatch=NavigateToolMessage::RotateCanvasBegin),
|
|
||||||
entry!(KeyDown(Lmb); action_dispatch=NavigateToolMessage::ZoomCanvasBegin),
|
entry!(KeyDown(Lmb); action_dispatch=NavigateToolMessage::ZoomCanvasBegin),
|
||||||
entry!(KeyUp(Rmb); action_dispatch=NavigateToolMessage::TransformCanvasEnd),
|
entry!(KeyDown(Lmb); modifiers=[Alt], action_dispatch=NavigateToolMessage::TiltCanvasBegin),
|
||||||
entry!(KeyUp(Lmb); action_dispatch=NavigateToolMessage::TransformCanvasEnd),
|
entry!(PointerMove; refresh_keys=[Control], action_dispatch=NavigateToolMessage::PointerMove { snap: Control }),
|
||||||
entry!(KeyUp(Mmb); action_dispatch=NavigateToolMessage::TransformCanvasEnd),
|
entry!(KeyUp(Lmb); action_dispatch=NavigateToolMessage::PointerUp { zoom_in: true }),
|
||||||
|
entry!(KeyUp(Lmb); modifiers=[Shift], action_dispatch=NavigateToolMessage::PointerUp { zoom_in: false }),
|
||||||
//
|
//
|
||||||
// EyedropperToolMessage
|
// EyedropperToolMessage
|
||||||
entry!(KeyDown(Lmb); action_dispatch=EyedropperToolMessage::SamplePrimaryColorBegin),
|
entry!(KeyDown(Lmb); action_dispatch=EyedropperToolMessage::SamplePrimaryColorBegin),
|
||||||
@@ -351,25 +350,21 @@ pub fn default_mapping() -> Mapping {
|
|||||||
entry!(KeyDown(Digit9); action_dispatch=TransformLayerMessage::TypeDigit { digit: 9 }),
|
entry!(KeyDown(Digit9); action_dispatch=TransformLayerMessage::TypeDigit { digit: 9 }),
|
||||||
//
|
//
|
||||||
// NavigationMessage
|
// NavigationMessage
|
||||||
entry!(KeyDown(Mmb); modifiers=[Alt], action_dispatch=NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false }),
|
entry!(KeyDown(Mmb); modifiers=[Alt], action_dispatch=NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu: false }),
|
||||||
entry!(KeyDown(Mmb); modifiers=[Shift], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
entry!(KeyDown(Mmb); modifiers=[Shift], action_dispatch=NavigationMessage::BeginCanvasZoom),
|
||||||
entry!(KeyDown(Lmb); modifiers=[Shift, Space], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
entry!(KeyDown(Lmb); modifiers=[Shift, Space], action_dispatch=NavigationMessage::BeginCanvasZoom),
|
||||||
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TranslateCanvasBegin),
|
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::BeginCanvasPan),
|
||||||
entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: false }),
|
entry!(KeyDown(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::BeginCanvasPan),
|
||||||
entry!(KeyDown(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TranslateCanvasBegin),
|
entry!(KeyDown(NumpadAdd); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }),
|
||||||
entry!(KeyUp(Lmb); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: false }),
|
entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }),
|
||||||
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: true }),
|
entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }),
|
||||||
entry!(KeyDown(Escape); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: true }),
|
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasZoomMouseWheel),
|
||||||
entry!(KeyDown(NumpadAdd); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }),
|
||||||
entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }),
|
||||||
entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }),
|
entry!(KeyDown(PageUp); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(1., 0.) }),
|
||||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasZoom),
|
entry!(KeyDown(PageDown); modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(-1., 0.) }),
|
||||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
|
entry!(KeyDown(PageUp); action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(0., 1.) }),
|
||||||
entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }),
|
entry!(KeyDown(PageDown); action_dispatch=NavigationMessage::CanvasPanByViewportFraction { delta: DVec2::new(0., -1.) }),
|
||||||
entry!(KeyDown(PageUp); modifiers=[Shift], action_dispatch=NavigationMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(1., 0.) }),
|
|
||||||
entry!(KeyDown(PageDown); modifiers=[Shift], action_dispatch=NavigationMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(-1., 0.) }),
|
|
||||||
entry!(KeyDown(PageUp); action_dispatch=NavigationMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(0., 1.) }),
|
|
||||||
entry!(KeyDown(PageDown); action_dispatch=NavigationMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(0., -1.) }),
|
|
||||||
entry!(KeyDown(Period); action_dispatch=NavigationMessage::FitViewportToSelection),
|
entry!(KeyDown(Period); action_dispatch=NavigationMessage::FitViewportToSelection),
|
||||||
//
|
//
|
||||||
// PortfolioMessage
|
// PortfolioMessage
|
||||||
@@ -429,14 +424,14 @@ pub fn zoom_with_scroll() -> Mapping {
|
|||||||
let mut mapping = default_mapping();
|
let mut mapping = default_mapping();
|
||||||
|
|
||||||
let remove = [
|
let remove = [
|
||||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasZoom),
|
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasZoomMouseWheel),
|
||||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
|
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }),
|
||||||
entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }),
|
entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }),
|
||||||
];
|
];
|
||||||
let add = [
|
let add = [
|
||||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
|
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: true }),
|
||||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }),
|
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::CanvasPanMouseWheel { use_y_as_x: false }),
|
||||||
entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasZoom),
|
entry!(WheelScroll; action_dispatch=NavigationMessage::CanvasZoomMouseWheel),
|
||||||
];
|
];
|
||||||
|
|
||||||
apply_mapping_patch(&mut mapping, remove, add);
|
apply_mapping_patch(&mut mapping, remove, add);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for
|
|||||||
// TODO: Extend this to multiple viewports instead of setting it to the value of this last loop iteration
|
// TODO: Extend this to multiple viewports instead of setting it to the value of this last loop iteration
|
||||||
self.viewport_bounds = bounds;
|
self.viewport_bounds = bounds;
|
||||||
|
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta: DVec2::ZERO });
|
responses.add(NavigationMessage::CanvasPan { delta: DVec2::ZERO });
|
||||||
responses.add(FrontendMessage::TriggerViewportResize);
|
responses.add(FrontendMessage::TriggerViewportResize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::utility_types::error::EditorError;
|
|||||||
use super::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, OptionBoundsSnapping, OptionPointSnapping, SnappingOptions, SnappingState};
|
use super::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, OptionBoundsSnapping, OptionPointSnapping, SnappingOptions, SnappingState};
|
||||||
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
||||||
use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH};
|
use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH};
|
||||||
use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING};
|
use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL};
|
||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
@@ -571,7 +571,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||||
}
|
}
|
||||||
DocumentMessage::RenderRulers => {
|
DocumentMessage::RenderRulers => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom);
|
let document_transform_scale = self.navigation_handler.snapped_zoom(self.navigation.zoom);
|
||||||
|
|
||||||
let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO);
|
let ruler_origin = self.metadata().document_to_viewport.transform_point2(DVec2::ZERO);
|
||||||
let log = document_transform_scale.log2();
|
let log = document_transform_scale.log2();
|
||||||
@@ -586,7 +586,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
DocumentMessage::RenderScrollbars => {
|
DocumentMessage::RenderScrollbars => {
|
||||||
let document_transform_scale = self.navigation_handler.snapped_scale(self.navigation.zoom);
|
let document_transform_scale = self.navigation_handler.snapped_zoom(self.navigation.zoom);
|
||||||
|
|
||||||
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
|
||||||
|
|
||||||
@@ -849,16 +849,17 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
responses.add(DocumentMessage::RenderScrollbars);
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
DocumentMessage::ZoomCanvasTo100Percent => {
|
DocumentMessage::ZoomCanvasTo100Percent => {
|
||||||
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 1. });
|
responses.add_front(NavigationMessage::CanvasZoomSet { zoom_factor: 1. });
|
||||||
}
|
}
|
||||||
DocumentMessage::ZoomCanvasTo200Percent => {
|
DocumentMessage::ZoomCanvasTo200Percent => {
|
||||||
responses.add_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. });
|
responses.add_front(NavigationMessage::CanvasZoomSet { zoom_factor: 2. });
|
||||||
}
|
}
|
||||||
DocumentMessage::ZoomCanvasToFitAll => {
|
DocumentMessage::ZoomCanvasToFitAll => {
|
||||||
if let Some(bounds) = self.metadata().document_bounds_document_space(true) {
|
if let Some(bounds) = self.metadata().document_bounds_document_space(true) {
|
||||||
responses.add(NavigationMessage::SetCanvasTilt { angle_radians: 0. });
|
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. });
|
||||||
responses.add(NavigationMessage::FitViewportToBounds { bounds, prevent_zoom_past_100: true });
|
responses.add(NavigationMessage::FitViewportToBounds { bounds, prevent_zoom_past_100: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1362,18 +1363,19 @@ impl DocumentMessageHandler {
|
|||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
IconButton::new("ZoomIn", 24)
|
IconButton::new("ZoomIn", 24)
|
||||||
.tooltip("Zoom In")
|
.tooltip("Zoom In")
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::IncreaseCanvasZoom))
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
||||||
.on_update(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into())
|
.on_update(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("ZoomOut", 24)
|
IconButton::new("ZoomOut", 24)
|
||||||
.tooltip("Zoom Out")
|
.tooltip("Zoom Out")
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::DecreaseCanvasZoom))
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
||||||
.on_update(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into())
|
.on_update(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new("ZoomReset", 24)
|
IconButton::new("ZoomReset", 24)
|
||||||
.tooltip("Reset Tilt and Zoom to 100%")
|
.tooltip("Reset Tilt and Zoom to 100%")
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::ResetCanvasTiltAndZoomTo100Percent))
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
||||||
.on_update(|_| NavigationMessage::ResetCanvasTiltAndZoomTo100Percent.into())
|
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
||||||
|
.disabled(self.navigation.tilt.abs() < 1e-4 && (self.navigation.zoom - 1.).abs() < 1e-4)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
PopoverButton::new()
|
PopoverButton::new()
|
||||||
.popover_layout(vec![
|
.popover_layout(vec![
|
||||||
@@ -1404,34 +1406,48 @@ impl DocumentMessageHandler {
|
|||||||
])
|
])
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(self.navigation_handler.snapped_scale(self.navigation.zoom) * 100.))
|
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.navigation.zoom) * 100.))
|
||||||
.unit("%")
|
.unit("%")
|
||||||
.min(0.000001)
|
.min(0.000001)
|
||||||
.max(1000000.)
|
.max(1000000.)
|
||||||
.tooltip("Document zoom within the viewport")
|
.tooltip("Document zoom within the viewport")
|
||||||
.on_update(|number_input: &NumberInput| {
|
.on_update(|number_input: &NumberInput| {
|
||||||
NavigationMessage::SetCanvasZoom {
|
NavigationMessage::CanvasZoomSet {
|
||||||
zoom_factor: number_input.value.unwrap() / 100.,
|
zoom_factor: number_input.value.unwrap() / 100.,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.increment_behavior(NumberInputIncrementBehavior::Callback)
|
.increment_behavior(NumberInputIncrementBehavior::Callback)
|
||||||
.increment_callback_decrease(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into())
|
.increment_callback_decrease(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||||
.increment_callback_increase(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into())
|
.increment_callback_increase(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let tilt_value = self.navigation_handler.snapped_angle(self.navigation.tilt) / (std::f64::consts::PI / 180.);
|
let tilt_value = self.navigation_handler.snapped_tilt(self.navigation.tilt) / (std::f64::consts::PI / 180.);
|
||||||
if tilt_value.abs() > 0.00001 {
|
if tilt_value.abs() > 0.00001 {
|
||||||
widgets.extend([
|
widgets.extend([
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
NumberInput::new(Some(tilt_value))
|
NumberInput::new(Some(tilt_value))
|
||||||
.unit("°")
|
.unit("°")
|
||||||
.step(15.)
|
.increment_behavior(NumberInputIncrementBehavior::Callback)
|
||||||
|
.increment_callback_increase(|number_input: &NumberInput| {
|
||||||
|
let one = 1. + f64::EPSILON * 100.;
|
||||||
|
NavigationMessage::CanvasTiltSet {
|
||||||
|
angle_radians: ((number_input.value.unwrap() / VIEWPORT_ROTATE_SNAP_INTERVAL + one).floor() * VIEWPORT_ROTATE_SNAP_INTERVAL).to_radians(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.increment_callback_decrease(|number_input: &NumberInput| {
|
||||||
|
let one = 1. + f64::EPSILON * 100.;
|
||||||
|
NavigationMessage::CanvasTiltSet {
|
||||||
|
angle_radians: ((number_input.value.unwrap() / VIEWPORT_ROTATE_SNAP_INTERVAL - one).ceil() * VIEWPORT_ROTATE_SNAP_INTERVAL).to_radians(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
.tooltip("Document tilt within the viewport")
|
.tooltip("Document tilt within the viewport")
|
||||||
.on_update(|number_input: &NumberInput| {
|
.on_update(|number_input: &NumberInput| {
|
||||||
NavigationMessage::SetCanvasTilt {
|
NavigationMessage::CanvasTiltSet {
|
||||||
angle_radians: number_input.value.unwrap() * (std::f64::consts::PI / 180.),
|
angle_radians: number_input.value.unwrap().to_radians(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,49 +7,21 @@ use glam::DVec2;
|
|||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum NavigationMessage {
|
pub enum NavigationMessage {
|
||||||
// Messages
|
// Messages
|
||||||
DecreaseCanvasZoom {
|
BeginCanvasPan,
|
||||||
center_on_mouse: bool,
|
BeginCanvasTilt { was_dispatched_from_menu: bool },
|
||||||
},
|
BeginCanvasZoom,
|
||||||
FitViewportToBounds {
|
CanvasPan { delta: DVec2 },
|
||||||
bounds: [DVec2; 2],
|
CanvasPanByViewportFraction { delta: DVec2 },
|
||||||
prevent_zoom_past_100: bool,
|
CanvasPanMouseWheel { use_y_as_x: bool },
|
||||||
},
|
CanvasTiltResetAndZoomTo100Percent,
|
||||||
|
CanvasTiltSet { angle_radians: f64 },
|
||||||
|
CanvasZoomDecrease { center_on_mouse: bool },
|
||||||
|
CanvasZoomIncrease { center_on_mouse: bool },
|
||||||
|
CanvasZoomMouseWheel,
|
||||||
|
CanvasZoomSet { zoom_factor: f64 },
|
||||||
|
EndCanvasPTZ { abort_transform: bool },
|
||||||
|
EndCanvasPTZWithClick { commit_key: Key },
|
||||||
|
FitViewportToBounds { bounds: [DVec2; 2], prevent_zoom_past_100: bool },
|
||||||
FitViewportToSelection,
|
FitViewportToSelection,
|
||||||
IncreaseCanvasZoom {
|
PointerMove { snap: Key },
|
||||||
center_on_mouse: bool,
|
|
||||||
},
|
|
||||||
PointerMove {
|
|
||||||
snap_angle: Key,
|
|
||||||
wait_for_snap_angle_release: bool,
|
|
||||||
snap_zoom: Key,
|
|
||||||
zoom_from_viewport: Option<DVec2>,
|
|
||||||
},
|
|
||||||
ResetCanvasTiltAndZoomTo100Percent,
|
|
||||||
RotateCanvasBegin {
|
|
||||||
was_dispatched_from_menu: bool,
|
|
||||||
},
|
|
||||||
SetCanvasTilt {
|
|
||||||
angle_radians: f64,
|
|
||||||
},
|
|
||||||
SetCanvasZoom {
|
|
||||||
zoom_factor: f64,
|
|
||||||
},
|
|
||||||
TransformCanvasEnd {
|
|
||||||
abort_transform: bool,
|
|
||||||
},
|
|
||||||
TransformFromMenuEnd {
|
|
||||||
commit_key: Key,
|
|
||||||
},
|
|
||||||
TranslateCanvas {
|
|
||||||
delta: DVec2,
|
|
||||||
},
|
|
||||||
TranslateCanvasBegin,
|
|
||||||
TranslateCanvasByViewportFraction {
|
|
||||||
delta: DVec2,
|
|
||||||
},
|
|
||||||
WheelCanvasTranslate {
|
|
||||||
use_y_as_x: bool,
|
|
||||||
},
|
|
||||||
WheelCanvasZoom,
|
|
||||||
ZoomCanvasBegin,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,162 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||||||
let old_zoom = ptz.zoom;
|
let old_zoom = ptz.zoom;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
NavigationMessage::DecreaseCanvasZoom { center_on_mouse } => {
|
NavigationMessage::BeginCanvasPan => {
|
||||||
|
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()])]),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.mouse_position = ipp.mouse.position;
|
||||||
|
self.navigation_operation = NavigationOperation::Pan { pan_original_for_abort: ptz.pan };
|
||||||
|
}
|
||||||
|
NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu } => {
|
||||||
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
|
||||||
|
responses.add(FrontendMessage::UpdateInputHints {
|
||||||
|
hint_data: HintData(vec![
|
||||||
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
|
HintGroup(vec![HintInfo {
|
||||||
|
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||||
|
key_groups_mac: None,
|
||||||
|
mouse: None,
|
||||||
|
label: String::from("Snap 15°"),
|
||||||
|
plus: false,
|
||||||
|
slash: false,
|
||||||
|
}]),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.navigation_operation = NavigationOperation::Tilt {
|
||||||
|
tilt_original_for_abort: ptz.tilt,
|
||||||
|
tilt_raw_not_snapped: ptz.tilt,
|
||||||
|
snap: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.mouse_position = ipp.mouse.position;
|
||||||
|
self.finish_operation_with_click = was_dispatched_from_menu;
|
||||||
|
}
|
||||||
|
NavigationMessage::BeginCanvasZoom => {
|
||||||
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
||||||
|
responses.add(FrontendMessage::UpdateInputHints {
|
||||||
|
hint_data: HintData(vec![
|
||||||
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
|
HintGroup(vec![HintInfo {
|
||||||
|
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||||
|
key_groups_mac: None,
|
||||||
|
mouse: None,
|
||||||
|
label: String::from("Increments"),
|
||||||
|
plus: false,
|
||||||
|
slash: false,
|
||||||
|
}]),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.navigation_operation = NavigationOperation::Zoom {
|
||||||
|
zoom_raw_not_snapped: ptz.zoom,
|
||||||
|
zoom_original_for_abort: ptz.zoom,
|
||||||
|
snap: false,
|
||||||
|
};
|
||||||
|
self.mouse_position = ipp.mouse.position;
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasPan { delta } => {
|
||||||
|
let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta);
|
||||||
|
|
||||||
|
ptz.pan += transformed_delta;
|
||||||
|
responses.add(BroadcastEvent::CanvasTransformed);
|
||||||
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasPanByViewportFraction { delta } => {
|
||||||
|
let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
||||||
|
|
||||||
|
ptz.pan += transformed_delta;
|
||||||
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasPanMouseWheel { 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;
|
||||||
|
responses.add(NavigationMessage::CanvasPan { delta });
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasTiltResetAndZoomTo100Percent => {
|
||||||
|
ptz.tilt = 0.;
|
||||||
|
ptz.zoom = 1.;
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasTiltSet { angle_radians } => {
|
||||||
|
ptz.tilt = angle_radians;
|
||||||
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom);
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().rev().find(|scale| **scale < ptz.zoom).unwrap_or(&ptz.zoom);
|
||||||
if center_on_mouse {
|
if center_on_mouse {
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
||||||
}
|
}
|
||||||
responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: new_scale });
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasZoomIncrease { center_on_mouse } => {
|
||||||
|
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom);
|
||||||
|
if center_on_mouse {
|
||||||
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
||||||
|
}
|
||||||
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: new_scale });
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasZoomMouseWheel => {
|
||||||
|
let scroll = ipp.mouse.scroll_delta.scroll_delta();
|
||||||
|
let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE;
|
||||||
|
if ipp.mouse.scroll_delta.y > 0 {
|
||||||
|
zoom_factor = 1. / zoom_factor
|
||||||
|
}
|
||||||
|
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
||||||
|
|
||||||
|
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
||||||
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom * zoom_factor });
|
||||||
|
}
|
||||||
|
NavigationMessage::CanvasZoomSet { zoom_factor } => {
|
||||||
|
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||||
|
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
|
}
|
||||||
|
NavigationMessage::EndCanvasPTZ { abort_transform } => {
|
||||||
|
// If an abort was requested, reset the active PTZ value to its original state
|
||||||
|
if abort_transform && self.navigation_operation != NavigationOperation::None {
|
||||||
|
match self.navigation_operation {
|
||||||
|
NavigationOperation::None => {}
|
||||||
|
NavigationOperation::Tilt { tilt_original_for_abort, .. } => {
|
||||||
|
ptz.tilt = tilt_original_for_abort;
|
||||||
|
}
|
||||||
|
NavigationOperation::Pan { pan_original_for_abort, .. } => {
|
||||||
|
ptz.pan = pan_original_for_abort;
|
||||||
|
}
|
||||||
|
NavigationOperation::Zoom { zoom_original_for_abort, .. } => {
|
||||||
|
ptz.zoom = zoom_original_for_abort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final chance to apply snapping if the key was pressed during this final frame
|
||||||
|
ptz.tilt = self.snapped_tilt(ptz.tilt);
|
||||||
|
ptz.zoom = self.snapped_zoom(ptz.zoom);
|
||||||
|
|
||||||
|
// Reset the navigation operation now that it's done
|
||||||
|
self.navigation_operation = NavigationOperation::None;
|
||||||
|
|
||||||
|
// Send the final messages to close out the operation
|
||||||
|
responses.add(BroadcastEvent::CanvasTransformed);
|
||||||
|
responses.add(ToolMessage::UpdateCursor);
|
||||||
|
responses.add(ToolMessage::UpdateHints);
|
||||||
|
responses.add(NavigateToolMessage::End);
|
||||||
|
}
|
||||||
|
NavigationMessage::EndCanvasPTZWithClick { commit_key } => {
|
||||||
|
self.finish_operation_with_click = false;
|
||||||
|
|
||||||
|
let abort_transform = commit_key == Key::Rmb;
|
||||||
|
responses.add(NavigationMessage::EndCanvasPTZ { abort_transform });
|
||||||
}
|
}
|
||||||
NavigationMessage::FitViewportToBounds {
|
NavigationMessage::FitViewportToBounds {
|
||||||
bounds: [pos1, pos2],
|
bounds: [pos1, pos2],
|
||||||
@@ -80,257 +230,95 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavigationMessage::IncreaseCanvasZoom { center_on_mouse } => {
|
NavigationMessage::PointerMove { snap } => {
|
||||||
let new_scale = *VIEWPORT_ZOOM_LEVELS.iter().find(|scale| **scale > ptz.zoom).unwrap_or(&ptz.zoom);
|
|
||||||
if center_on_mouse {
|
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), new_scale / ptz.zoom, ipp.mouse.position));
|
|
||||||
}
|
|
||||||
responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: new_scale });
|
|
||||||
}
|
|
||||||
NavigationMessage::PointerMove {
|
|
||||||
snap_angle,
|
|
||||||
wait_for_snap_angle_release,
|
|
||||||
snap_zoom,
|
|
||||||
zoom_from_viewport,
|
|
||||||
} => {
|
|
||||||
match self.navigation_operation {
|
match self.navigation_operation {
|
||||||
NavigationOperation::None => {}
|
NavigationOperation::None => {}
|
||||||
NavigationOperation::Pan { .. } => {
|
NavigationOperation::Pan { .. } => {
|
||||||
let delta = ipp.mouse.position - self.mouse_position;
|
let delta = ipp.mouse.position - self.mouse_position;
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta });
|
responses.add(NavigationMessage::CanvasPan { delta });
|
||||||
}
|
}
|
||||||
NavigationOperation::Rotate {
|
NavigationOperation::Tilt {
|
||||||
snap_tilt,
|
tilt_raw_not_snapped,
|
||||||
snap_tilt_released,
|
tilt_original_for_abort,
|
||||||
pre_commit_tilt,
|
..
|
||||||
} => {
|
} => {
|
||||||
let new_snap = ipp.keyboard.get(snap_angle as usize);
|
let tilt_raw_not_snapped = {
|
||||||
|
let half_viewport = ipp.viewport_bounds.size() / 2.;
|
||||||
if !(wait_for_snap_angle_release && new_snap && !snap_tilt_released) {
|
|
||||||
// When disabling snap, keep the viewed rotation as it was previously.
|
|
||||||
if !new_snap && snap_tilt {
|
|
||||||
ptz.tilt = self.snapped_angle(ptz.tilt);
|
|
||||||
}
|
|
||||||
self.navigation_operation = NavigationOperation::Rotate {
|
|
||||||
pre_commit_tilt,
|
|
||||||
snap_tilt: new_snap,
|
|
||||||
snap_tilt_released: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let half_viewport = ipp.viewport_bounds.size() / 2.;
|
|
||||||
let rotation = {
|
|
||||||
let start_offset = self.mouse_position - half_viewport;
|
let start_offset = self.mouse_position - half_viewport;
|
||||||
let end_offset = ipp.mouse.position - half_viewport;
|
let end_offset = ipp.mouse.position - half_viewport;
|
||||||
start_offset.angle_between(end_offset)
|
let angle = start_offset.angle_between(end_offset);
|
||||||
|
|
||||||
|
tilt_raw_not_snapped + angle
|
||||||
|
};
|
||||||
|
ptz.tilt = self.snapped_tilt(tilt_raw_not_snapped);
|
||||||
|
|
||||||
|
let snap = ipp.keyboard.get(snap as usize);
|
||||||
|
|
||||||
|
self.navigation_operation = NavigationOperation::Tilt {
|
||||||
|
tilt_original_for_abort,
|
||||||
|
tilt_raw_not_snapped,
|
||||||
|
snap,
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(NavigationMessage::SetCanvasTilt { angle_radians: ptz.tilt + rotation });
|
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: ptz.tilt });
|
||||||
}
|
}
|
||||||
NavigationOperation::Zoom { snap_zoom_enabled, pre_commit_zoom } => {
|
NavigationOperation::Zoom {
|
||||||
let zoom_start = self.snapped_scale(ptz.zoom);
|
zoom_raw_not_snapped,
|
||||||
|
zoom_original_for_abort,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let zoom_raw_not_snapped = {
|
||||||
|
let vertical_delta = self.mouse_position.y - ipp.mouse.position.y;
|
||||||
|
let amount = vertical_delta * VIEWPORT_ZOOM_MOUSE_RATE;
|
||||||
|
let updated_zoom = zoom_raw_not_snapped * (1. + amount);
|
||||||
|
|
||||||
let new_snap = ipp.keyboard.get(snap_zoom as usize);
|
updated_zoom * Self::clamp_zoom(updated_zoom, document_bounds, old_zoom, ipp)
|
||||||
// When disabling snap, keep the viewed zoom as it was previously
|
};
|
||||||
if !new_snap && snap_zoom_enabled {
|
ptz.zoom = self.snapped_zoom(zoom_raw_not_snapped);
|
||||||
ptz.zoom = self.snapped_scale(ptz.zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
if snap_zoom_enabled != new_snap {
|
let snap = ipp.keyboard.get(snap as usize);
|
||||||
self.navigation_operation = NavigationOperation::Zoom {
|
|
||||||
pre_commit_zoom,
|
|
||||||
snap_zoom_enabled: new_snap,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let difference = self.mouse_position.y - ipp.mouse.position.y;
|
self.navigation_operation = NavigationOperation::Zoom {
|
||||||
let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE;
|
zoom_raw_not_snapped,
|
||||||
|
zoom_original_for_abort,
|
||||||
|
snap,
|
||||||
|
};
|
||||||
|
|
||||||
ptz.zoom *= amount;
|
responses.add(NavigationMessage::CanvasZoomSet { zoom_factor: ptz.zoom });
|
||||||
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
|
||||||
|
|
||||||
if let Some(mouse) = zoom_from_viewport {
|
|
||||||
let zoom_factor = self.snapped_scale(ptz.zoom) / zoom_start;
|
|
||||||
|
|
||||||
responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: ptz.zoom });
|
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse));
|
|
||||||
} else {
|
|
||||||
responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: ptz.zoom });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mouse_position = ipp.mouse.position;
|
self.mouse_position = ipp.mouse.position;
|
||||||
}
|
}
|
||||||
NavigationMessage::ResetCanvasTiltAndZoomTo100Percent => {
|
|
||||||
ptz.tilt = 0.;
|
|
||||||
ptz.zoom = 1.;
|
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
}
|
|
||||||
NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu } => {
|
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
|
|
||||||
responses.add(FrontendMessage::UpdateInputHints {
|
|
||||||
hint_data: HintData(vec![
|
|
||||||
// TODO: Fix bug where canceling doesn't work except with the Navigate tool active
|
|
||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
|
||||||
HintGroup(vec![HintInfo {
|
|
||||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: None,
|
|
||||||
label: String::from("Snap 15°"),
|
|
||||||
plus: false,
|
|
||||||
slash: false,
|
|
||||||
}]),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.navigation_operation = NavigationOperation::Rotate {
|
|
||||||
pre_commit_tilt: ptz.tilt,
|
|
||||||
snap_tilt_released: false,
|
|
||||||
snap_tilt: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.mouse_position = ipp.mouse.position;
|
|
||||||
self.finish_operation_with_click = was_dispatched_from_menu;
|
|
||||||
}
|
|
||||||
NavigationMessage::SetCanvasTilt { angle_radians } => {
|
|
||||||
ptz.tilt = angle_radians;
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
|
||||||
}
|
|
||||||
NavigationMessage::SetCanvasZoom { zoom_factor } => {
|
|
||||||
ptz.zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
|
||||||
ptz.zoom *= Self::clamp_zoom(ptz.zoom, document_bounds, old_zoom, ipp);
|
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
}
|
|
||||||
NavigationMessage::TransformCanvasEnd { abort_transform } => {
|
|
||||||
if abort_transform {
|
|
||||||
match self.navigation_operation {
|
|
||||||
NavigationOperation::None => {}
|
|
||||||
NavigationOperation::Rotate { pre_commit_tilt, .. } => {
|
|
||||||
ptz.tilt = pre_commit_tilt;
|
|
||||||
responses.add(NavigationMessage::SetCanvasTilt { angle_radians: pre_commit_tilt });
|
|
||||||
}
|
|
||||||
NavigationOperation::Pan { pre_commit_pan, .. } => {
|
|
||||||
ptz.pan = pre_commit_pan;
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
}
|
|
||||||
NavigationOperation::Zoom { pre_commit_zoom, .. } => {
|
|
||||||
ptz.zoom = pre_commit_zoom;
|
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ptz.tilt = self.snapped_angle(ptz.tilt);
|
|
||||||
ptz.zoom = self.snapped_scale(ptz.zoom);
|
|
||||||
responses.add(BroadcastEvent::CanvasTransformed);
|
|
||||||
responses.add(ToolMessage::UpdateCursor);
|
|
||||||
responses.add(ToolMessage::UpdateHints);
|
|
||||||
self.navigation_operation = NavigationOperation::None;
|
|
||||||
}
|
|
||||||
NavigationMessage::TransformFromMenuEnd { commit_key } => {
|
|
||||||
let abort_transform = commit_key == Key::Rmb;
|
|
||||||
self.finish_operation_with_click = false;
|
|
||||||
responses.add(NavigationMessage::TransformCanvasEnd { abort_transform });
|
|
||||||
}
|
|
||||||
NavigationMessage::TranslateCanvas { delta } => {
|
|
||||||
let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta);
|
|
||||||
|
|
||||||
ptz.pan += transformed_delta;
|
|
||||||
responses.add(BroadcastEvent::CanvasTransformed);
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
}
|
|
||||||
NavigationMessage::TranslateCanvasBegin => {
|
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateInputHints {
|
|
||||||
// TODO: Fix bug where canceling doesn't work except with the Navigate tool active
|
|
||||||
hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.mouse_position = ipp.mouse.position;
|
|
||||||
self.navigation_operation = NavigationOperation::Pan { pre_commit_pan: ptz.pan };
|
|
||||||
}
|
|
||||||
NavigationMessage::TranslateCanvasByViewportFraction { delta } => {
|
|
||||||
let transformed_delta = metadata.document_to_viewport.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
|
||||||
|
|
||||||
ptz.pan += transformed_delta;
|
|
||||||
self.create_document_transform(ipp.viewport_bounds.center(), ptz, responses);
|
|
||||||
}
|
|
||||||
NavigationMessage::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;
|
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta });
|
|
||||||
}
|
|
||||||
NavigationMessage::WheelCanvasZoom => {
|
|
||||||
let scroll = ipp.mouse.scroll_delta.scroll_delta();
|
|
||||||
let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE;
|
|
||||||
if ipp.mouse.scroll_delta.y > 0 {
|
|
||||||
zoom_factor = 1. / zoom_factor
|
|
||||||
}
|
|
||||||
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
|
||||||
|
|
||||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, ipp.mouse.position));
|
|
||||||
responses.add(NavigationMessage::SetCanvasZoom { zoom_factor: ptz.zoom * zoom_factor });
|
|
||||||
}
|
|
||||||
NavigationMessage::ZoomCanvasBegin => {
|
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
|
||||||
responses.add(FrontendMessage::UpdateInputHints {
|
|
||||||
hint_data: HintData(vec![
|
|
||||||
// TODO: Fix bug where canceling doesn't work except with the Navigate tool active
|
|
||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
|
||||||
HintGroup(vec![HintInfo {
|
|
||||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
|
||||||
key_groups_mac: None,
|
|
||||||
mouse: None,
|
|
||||||
label: String::from("Increments"),
|
|
||||||
plus: false,
|
|
||||||
slash: false,
|
|
||||||
}]),
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.navigation_operation = NavigationOperation::Zoom {
|
|
||||||
pre_commit_zoom: ptz.zoom,
|
|
||||||
snap_zoom_enabled: false,
|
|
||||||
};
|
|
||||||
self.mouse_position = ipp.mouse.position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
let mut common = actions!(NavigationMessageDiscriminant;
|
let mut common = actions!(NavigationMessageDiscriminant;
|
||||||
TranslateCanvasBegin,
|
BeginCanvasPan,
|
||||||
RotateCanvasBegin,
|
BeginCanvasTilt,
|
||||||
ZoomCanvasBegin,
|
BeginCanvasZoom,
|
||||||
SetCanvasTilt,
|
CanvasPan,
|
||||||
WheelCanvasZoom,
|
CanvasPanByViewportFraction,
|
||||||
IncreaseCanvasZoom,
|
CanvasPanMouseWheel,
|
||||||
DecreaseCanvasZoom,
|
CanvasTiltSet,
|
||||||
WheelCanvasTranslate,
|
CanvasZoomDecrease,
|
||||||
TranslateCanvas,
|
CanvasZoomIncrease,
|
||||||
TranslateCanvasByViewportFraction,
|
CanvasZoomMouseWheel,
|
||||||
FitViewportToSelection,
|
FitViewportToSelection,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.navigation_operation != NavigationOperation::None {
|
if self.navigation_operation != NavigationOperation::None {
|
||||||
let transforming = actions!(NavigationMessageDiscriminant;
|
let transforming = actions!(NavigationMessageDiscriminant;
|
||||||
|
EndCanvasPTZ,
|
||||||
PointerMove,
|
PointerMove,
|
||||||
TransformCanvasEnd,
|
|
||||||
);
|
);
|
||||||
common.extend(transforming);
|
common.extend(transforming);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.finish_operation_with_click {
|
if self.finish_operation_with_click {
|
||||||
let transforming_from_menu = actions!(NavigationMessageDiscriminant;
|
let transforming_from_menu = actions!(NavigationMessageDiscriminant;
|
||||||
TransformFromMenuEnd,
|
EndCanvasPTZWithClick,
|
||||||
);
|
);
|
||||||
|
|
||||||
common.extend(transforming_from_menu);
|
common.extend(transforming_from_menu);
|
||||||
@@ -341,17 +329,17 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NavigationMessageHandler {
|
impl NavigationMessageHandler {
|
||||||
pub fn snapped_angle(&self, tilt: f64) -> f64 {
|
pub fn snapped_tilt(&self, tilt: f64) -> f64 {
|
||||||
let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians();
|
let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians();
|
||||||
if let NavigationOperation::Rotate { snap_tilt: true, .. } = self.navigation_operation {
|
if matches!(self.navigation_operation, NavigationOperation::Tilt { snap: true, .. }) {
|
||||||
(tilt / increment_radians).round() * increment_radians
|
(tilt / increment_radians).round() * increment_radians
|
||||||
} else {
|
} else {
|
||||||
tilt
|
tilt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapped_scale(&self, zoom: f64) -> f64 {
|
pub fn snapped_zoom(&self, zoom: f64) -> f64 {
|
||||||
if let NavigationOperation::Zoom { snap_zoom_enabled: true, .. } = self.navigation_operation {
|
if matches!(self.navigation_operation, NavigationOperation::Zoom { snap: true, .. }) {
|
||||||
*VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom)
|
*VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom)
|
||||||
} else {
|
} else {
|
||||||
zoom
|
zoom
|
||||||
@@ -359,16 +347,16 @@ impl NavigationMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_offset_transform(&self, viewport_center: DVec2, pan: DVec2, tilt: f64, zoom: f64) -> DAffine2 {
|
pub fn calculate_offset_transform(&self, viewport_center: DVec2, pan: DVec2, tilt: f64, zoom: f64) -> DAffine2 {
|
||||||
let scaled_center = viewport_center / self.snapped_scale(zoom);
|
let scaled_center = viewport_center / self.snapped_zoom(zoom);
|
||||||
|
|
||||||
// Try to avoid fractional coordinates to reduce anti aliasing.
|
// Try to avoid fractional coordinates to reduce anti aliasing.
|
||||||
let scale = self.snapped_scale(zoom);
|
let scale = self.snapped_zoom(zoom);
|
||||||
let rounded_pan = ((pan + scaled_center) * scale).round() / scale - scaled_center;
|
let rounded_pan = ((pan + scaled_center) * scale).round() / scale - scaled_center;
|
||||||
|
|
||||||
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
|
// TODO: replace with DAffine2::from_scale_angle_translation and fix the errors
|
||||||
let offset_transform = DAffine2::from_translation(scaled_center);
|
let offset_transform = DAffine2::from_translation(scaled_center);
|
||||||
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
|
let scale_transform = DAffine2::from_scale(DVec2::splat(scale));
|
||||||
let angle_transform = DAffine2::from_angle(self.snapped_angle(tilt));
|
let angle_transform = DAffine2::from_angle(self.snapped_tilt(tilt));
|
||||||
let translation_transform = DAffine2::from_translation(rounded_pan);
|
let translation_transform = DAffine2::from_translation(rounded_pan);
|
||||||
scale_transform * offset_transform * angle_transform * translation_transform
|
scale_transform * offset_transform * angle_transform * translation_transform
|
||||||
}
|
}
|
||||||
@@ -384,17 +372,17 @@ impl NavigationMessageHandler {
|
|||||||
let mouse_fraction = mouse / viewport_bounds;
|
let mouse_fraction = mouse / viewport_bounds;
|
||||||
let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction);
|
let delta = delta_size * (DVec2::splat(0.5) - mouse_fraction);
|
||||||
|
|
||||||
NavigationMessage::TranslateCanvas { delta }.into()
|
NavigationMessage::CanvasPan { delta }.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clamp_zoom(zoom: f64, document_bounds: Option<[DVec2; 2]>, old_zoom: f64, ipp: &InputPreprocessorMessageHandler) -> f64 {
|
pub fn clamp_zoom(zoom: f64, document_bounds: Option<[DVec2; 2]>, old_zoom: f64, ipp: &InputPreprocessorMessageHandler) -> f64 {
|
||||||
let document_size = (document_bounds.map(|[min, max]| max - min).unwrap_or_default() / old_zoom) * zoom;
|
let document_size = (document_bounds.map(|[min, max]| max - min).unwrap_or_default() / old_zoom) * zoom;
|
||||||
let scale_factor = (document_size / ipp.viewport_bounds.size()).max_element();
|
let scale_factor = (document_size / ipp.viewport_bounds.size()).max_element();
|
||||||
|
|
||||||
if scale_factor > f64::EPSILON * 100. && scale_factor.is_finite() && scale_factor < VIEWPORT_ZOOM_MIN_FRACTION_COVER {
|
if scale_factor <= f64::EPSILON * 100. || !scale_factor.is_finite() || scale_factor >= VIEWPORT_ZOOM_MIN_FRACTION_COVER {
|
||||||
VIEWPORT_ZOOM_MIN_FRACTION_COVER / scale_factor
|
return 1.;
|
||||||
} else {
|
|
||||||
1.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VIEWPORT_ZOOM_MIN_FRACTION_COVER / scale_factor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,16 @@ pub enum NavigationOperation {
|
|||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
Pan {
|
Pan {
|
||||||
pre_commit_pan: DVec2,
|
pan_original_for_abort: DVec2,
|
||||||
},
|
},
|
||||||
Rotate {
|
Tilt {
|
||||||
pre_commit_tilt: f64,
|
tilt_original_for_abort: f64,
|
||||||
snap_tilt: bool,
|
tilt_raw_not_snapped: f64,
|
||||||
snap_tilt_released: bool,
|
snap: bool,
|
||||||
},
|
},
|
||||||
Zoom {
|
Zoom {
|
||||||
pre_commit_zoom: f64,
|
zoom_raw_not_snapped: f64,
|
||||||
snap_zoom_enabled: bool,
|
zoom_original_for_abort: f64,
|
||||||
|
snap: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,15 +269,15 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
vec![
|
vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Tilt".into(),
|
label: "Tilt".into(),
|
||||||
shortcut: action_keys!(NavigationMessageDiscriminant::RotateCanvasBegin),
|
shortcut: action_keys!(NavigationMessageDiscriminant::BeginCanvasTilt),
|
||||||
action: MenuBarEntry::create_action(|_| NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: true }.into()),
|
action: MenuBarEntry::create_action(|_| NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu: true }.into()),
|
||||||
disabled: no_active_document,
|
disabled: no_active_document,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Reset Tilt".into(),
|
label: "Reset Tilt".into(),
|
||||||
shortcut: action_keys!(NavigationMessageDiscriminant::SetCanvasTilt),
|
shortcut: action_keys!(NavigationMessageDiscriminant::CanvasTiltSet),
|
||||||
action: MenuBarEntry::create_action(|_| NavigationMessage::SetCanvasTilt { angle_radians: 0.into() }.into()),
|
action: MenuBarEntry::create_action(|_| NavigationMessage::CanvasTiltSet { angle_radians: 0.into() }.into()),
|
||||||
disabled: no_active_document,
|
disabled: no_active_document,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
@@ -285,15 +285,17 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
vec![
|
vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Zoom In".into(),
|
label: "Zoom In".into(),
|
||||||
shortcut: action_keys!(NavigationMessageDiscriminant::IncreaseCanvasZoom),
|
icon: Some("ZoomIn".into()),
|
||||||
action: MenuBarEntry::create_action(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
|
shortcut: action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease),
|
||||||
|
action: MenuBarEntry::create_action(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into()),
|
||||||
disabled: no_active_document,
|
disabled: no_active_document,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
label: "Zoom Out".into(),
|
label: "Zoom Out".into(),
|
||||||
shortcut: action_keys!(NavigationMessageDiscriminant::DecreaseCanvasZoom),
|
icon: Some("ZoomOut".into()),
|
||||||
action: MenuBarEntry::create_action(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
|
shortcut: action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease),
|
||||||
|
action: MenuBarEntry::create_action(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into()),
|
||||||
disabled: no_active_document,
|
disabled: no_active_document,
|
||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
let document_id = DocumentId(generate_uuid());
|
let document_id = DocumentId(generate_uuid());
|
||||||
if self.active_document().is_some() {
|
if self.active_document().is_some() {
|
||||||
responses.add(BroadcastEvent::ToolAbort);
|
responses.add(BroadcastEvent::ToolAbort);
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() });
|
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
||||||
}
|
}
|
||||||
|
|
||||||
self.load_document(new_document, document_id, responses);
|
self.load_document(new_document, document_id, responses);
|
||||||
@@ -464,7 +464,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
responses.add(BroadcastEvent::ToolAbort);
|
responses.add(BroadcastEvent::ToolAbort);
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() });
|
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
||||||
}
|
}
|
||||||
@@ -637,7 +637,7 @@ impl PortfolioMessageHandler {
|
|||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
responses.add(ToolMessage::InitTools);
|
responses.add(ToolMessage::InitTools);
|
||||||
responses.add(NodeGraphMessage::Init);
|
responses.add(NodeGraphMessage::Init);
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() });
|
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
||||||
responses.add(PropertiesPanelMessage::Clear);
|
responses.add(PropertiesPanelMessage::Clear);
|
||||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ impl AutoPanning {
|
|||||||
|
|
||||||
let time_delta = input.frame_time.frame_duration()?.as_secs_f64();
|
let time_delta = input.frame_time.frame_duration()?.as_secs_f64();
|
||||||
let delta = (shift_percent * DRAG_BEYOND_VIEWPORT_SPEED_FACTOR * viewport_size * time_delta).round();
|
let delta = (shift_percent * DRAG_BEYOND_VIEWPORT_SPEED_FACTOR * viewport_size * time_delta).round();
|
||||||
responses.add(NavigationMessage::TranslateCanvas { delta });
|
responses.add(NavigationMessage::CanvasPan { delta });
|
||||||
Some(delta)
|
Some(delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ pub enum NavigateToolMessage {
|
|||||||
Abort,
|
Abort,
|
||||||
|
|
||||||
// Tool-specific messages
|
// Tool-specific messages
|
||||||
ClickZoom { zoom_in: bool },
|
PointerUp { zoom_in: bool },
|
||||||
PointerMove { snap_angle: Key, snap_zoom: Key },
|
PointerMove { snap: Key },
|
||||||
RotateCanvasBegin,
|
TiltCanvasBegin,
|
||||||
TransformCanvasEnd,
|
|
||||||
TranslateCanvasBegin,
|
|
||||||
ZoomCanvasBegin,
|
ZoomCanvasBegin,
|
||||||
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolMetadata for NavigateTool {
|
impl ToolMetadata for NavigateTool {
|
||||||
@@ -47,14 +46,15 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Navigat
|
|||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
match self.fsm_state {
|
match self.fsm_state {
|
||||||
NavigateToolFsmState::Ready => actions!(NavigateToolMessageDiscriminant;
|
NavigateToolFsmState::Ready => actions!(NavigateToolMessageDiscriminant;
|
||||||
TranslateCanvasBegin,
|
TiltCanvasBegin,
|
||||||
RotateCanvasBegin,
|
|
||||||
ZoomCanvasBegin,
|
ZoomCanvasBegin,
|
||||||
),
|
),
|
||||||
_ => actions!(NavigateToolMessageDiscriminant;
|
NavigateToolFsmState::Tilting | NavigateToolFsmState::Zooming => actions!(NavigateToolMessageDiscriminant;
|
||||||
ClickZoom,
|
PointerMove,
|
||||||
|
),
|
||||||
|
NavigateToolFsmState::ZoomOrClickZooming => actions!(NavigateToolMessageDiscriminant;
|
||||||
|
PointerUp,
|
||||||
PointerMove,
|
PointerMove,
|
||||||
TransformCanvasEnd,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,14 +73,14 @@ impl ToolTransition for NavigateTool {
|
|||||||
enum NavigateToolFsmState {
|
enum NavigateToolFsmState {
|
||||||
#[default]
|
#[default]
|
||||||
Ready,
|
Ready,
|
||||||
Panning,
|
|
||||||
Tilting,
|
Tilting,
|
||||||
|
ZoomOrClickZooming,
|
||||||
Zooming,
|
Zooming,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct NavigateToolData {
|
struct NavigateToolData {
|
||||||
drag_start: DVec2,
|
drag_start: Option<DVec2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fsm for NavigateToolFsmState {
|
impl Fsm for NavigateToolFsmState {
|
||||||
@@ -100,50 +100,48 @@ impl Fsm for NavigateToolFsmState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match navigate {
|
match navigate {
|
||||||
NavigateToolMessage::ClickZoom { zoom_in } => {
|
NavigateToolMessage::PointerUp { zoom_in } => {
|
||||||
responses.add_front(NavigationMessage::TransformCanvasEnd { abort_transform: false });
|
if self == NavigateToolFsmState::ZoomOrClickZooming {
|
||||||
|
// Mouse has not moved from pointerdown to pointerup
|
||||||
// Mouse has not moved from pointerdown to pointerup
|
if tool_data.drag_start == Some(input.mouse.position) {
|
||||||
if tool_data.drag_start == input.mouse.position {
|
responses.add_front(if zoom_in {
|
||||||
responses.add_front(if zoom_in {
|
NavigationMessage::CanvasZoomIncrease { center_on_mouse: true }
|
||||||
NavigationMessage::IncreaseCanvasZoom { center_on_mouse: true }
|
} else {
|
||||||
} else {
|
NavigationMessage::CanvasZoomDecrease { center_on_mouse: true }
|
||||||
NavigationMessage::DecreaseCanvasZoom { center_on_mouse: true }
|
});
|
||||||
});
|
}
|
||||||
|
} else {
|
||||||
|
responses.add_front(NavigationMessage::EndCanvasPTZ { abort_transform: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tool_data.drag_start = None;
|
||||||
NavigateToolFsmState::Ready
|
NavigateToolFsmState::Ready
|
||||||
}
|
}
|
||||||
NavigateToolMessage::PointerMove { snap_angle, snap_zoom } => {
|
NavigateToolMessage::PointerMove { snap } => {
|
||||||
responses.add_front(NavigationMessage::PointerMove {
|
if self == NavigateToolFsmState::ZoomOrClickZooming {
|
||||||
snap_angle,
|
responses.add_front(NavigationMessage::BeginCanvasZoom);
|
||||||
wait_for_snap_angle_release: false,
|
NavigateToolFsmState::Zooming
|
||||||
snap_zoom,
|
} else {
|
||||||
zoom_from_viewport: Some(tool_data.drag_start),
|
responses.add_front(NavigationMessage::PointerMove { snap });
|
||||||
});
|
self
|
||||||
self
|
}
|
||||||
}
|
}
|
||||||
NavigateToolMessage::TranslateCanvasBegin => {
|
NavigateToolMessage::TiltCanvasBegin => {
|
||||||
tool_data.drag_start = input.mouse.position;
|
responses.add_front(NavigationMessage::BeginCanvasTilt { was_dispatched_from_menu: false });
|
||||||
responses.add_front(NavigationMessage::TranslateCanvasBegin);
|
|
||||||
NavigateToolFsmState::Panning
|
|
||||||
}
|
|
||||||
NavigateToolMessage::RotateCanvasBegin => {
|
|
||||||
tool_data.drag_start = input.mouse.position;
|
|
||||||
responses.add_front(NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false });
|
|
||||||
NavigateToolFsmState::Tilting
|
NavigateToolFsmState::Tilting
|
||||||
}
|
}
|
||||||
NavigateToolMessage::ZoomCanvasBegin => {
|
NavigateToolMessage::ZoomCanvasBegin => {
|
||||||
tool_data.drag_start = input.mouse.position;
|
// Wait to decide between zooming and click zooming based on whether the next event is a PointerMove or PointerUp
|
||||||
responses.add_front(NavigationMessage::ZoomCanvasBegin);
|
tool_data.drag_start = Some(input.mouse.position);
|
||||||
NavigateToolFsmState::Zooming
|
NavigateToolFsmState::ZoomOrClickZooming
|
||||||
}
|
}
|
||||||
NavigateToolMessage::TransformCanvasEnd => {
|
NavigateToolMessage::End => {
|
||||||
responses.add_front(NavigationMessage::TransformCanvasEnd { abort_transform: false });
|
tool_data.drag_start = None;
|
||||||
NavigateToolFsmState::Ready
|
NavigateToolFsmState::Ready
|
||||||
}
|
}
|
||||||
NavigateToolMessage::Abort => {
|
NavigateToolMessage::Abort => {
|
||||||
responses.add_front(NavigationMessage::TransformCanvasEnd { abort_transform: false });
|
responses.add_front(NavigationMessage::EndCanvasPTZ { abort_transform: false });
|
||||||
|
tool_data.drag_start = None;
|
||||||
NavigateToolFsmState::Ready
|
NavigateToolFsmState::Ready
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +149,7 @@ impl Fsm for NavigateToolFsmState {
|
|||||||
|
|
||||||
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
fn update_hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
let hint_data = match self {
|
let hint_data = match self {
|
||||||
NavigateToolFsmState::Ready => HintData(vec![
|
NavigateToolFsmState::Ready | NavigateToolFsmState::ZoomOrClickZooming => HintData(vec![
|
||||||
HintGroup(vec![
|
HintGroup(vec![
|
||||||
HintInfo::mouse(MouseMotion::MmbDrag, ""),
|
HintInfo::mouse(MouseMotion::MmbDrag, ""),
|
||||||
HintInfo::keys_and_mouse([Key::Space], MouseMotion::LmbDrag, "Pan").prepend_slash(),
|
HintInfo::keys_and_mouse([Key::Space], MouseMotion::LmbDrag, "Pan").prepend_slash(),
|
||||||
@@ -168,7 +166,6 @@ impl Fsm for NavigateToolFsmState {
|
|||||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
HintGroup(vec![HintInfo::keys([Key::Control], "Increments")]),
|
HintGroup(vec![HintInfo::keys([Key::Control], "Increments")]),
|
||||||
]),
|
]),
|
||||||
_ => HintData(Vec::new()),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||||
@@ -177,9 +174,8 @@ impl Fsm for NavigateToolFsmState {
|
|||||||
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
|
fn update_cursor(&self, responses: &mut VecDeque<Message>) {
|
||||||
let cursor = match *self {
|
let cursor = match *self {
|
||||||
NavigateToolFsmState::Ready => MouseCursorIcon::ZoomIn,
|
NavigateToolFsmState::Ready => MouseCursorIcon::ZoomIn,
|
||||||
NavigateToolFsmState::Panning => MouseCursorIcon::Grabbing,
|
|
||||||
NavigateToolFsmState::Tilting => MouseCursorIcon::Default,
|
NavigateToolFsmState::Tilting => MouseCursorIcon::Default,
|
||||||
NavigateToolFsmState::Zooming => MouseCursorIcon::ZoomIn,
|
NavigateToolFsmState::Zooming | NavigateToolFsmState::ZoomOrClickZooming => MouseCursorIcon::ZoomIn,
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor });
|
||||||
|
|||||||
@@ -139,26 +139,26 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateCanvasX(newValue: number) {
|
function panCanvasX(newValue: number) {
|
||||||
const delta = newValue - scrollbarPos.x;
|
const delta = newValue - scrollbarPos.x;
|
||||||
scrollbarPos.x = newValue;
|
scrollbarPos.x = newValue;
|
||||||
editor.instance.translateCanvas(-delta * scrollbarMultiplier.x, 0);
|
editor.instance.panCanvas(-delta * scrollbarMultiplier.x, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateCanvasY(newValue: number) {
|
function panCanvasY(newValue: number) {
|
||||||
const delta = newValue - scrollbarPos.y;
|
const delta = newValue - scrollbarPos.y;
|
||||||
scrollbarPos.y = newValue;
|
scrollbarPos.y = newValue;
|
||||||
editor.instance.translateCanvas(0, -delta * scrollbarMultiplier.y);
|
editor.instance.panCanvas(0, -delta * scrollbarMultiplier.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pageX(delta: number) {
|
function pageX(delta: number) {
|
||||||
const move = delta < 0 ? 1 : -1;
|
const move = delta < 0 ? 1 : -1;
|
||||||
editor.instance.translateCanvasByFraction(move, 0);
|
editor.instance.panCanvasByFraction(move, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pageY(delta: number) {
|
function pageY(delta: number) {
|
||||||
const move = delta < 0 ? 1 : -1;
|
const move = delta < 0 ? 1 : -1;
|
||||||
editor.instance.translateCanvasByFraction(0, move);
|
editor.instance.panCanvasByFraction(0, move);
|
||||||
}
|
}
|
||||||
|
|
||||||
function canvasPointerDown(e: PointerEvent) {
|
function canvasPointerDown(e: PointerEvent) {
|
||||||
@@ -506,7 +506,7 @@
|
|||||||
direction="Vertical"
|
direction="Vertical"
|
||||||
handleLength={scrollbarSize.y}
|
handleLength={scrollbarSize.y}
|
||||||
handlePosition={scrollbarPos.y}
|
handlePosition={scrollbarPos.y}
|
||||||
on:handlePosition={({ detail }) => translateCanvasY(detail)}
|
on:handlePosition={({ detail }) => panCanvasY(detail)}
|
||||||
on:pressTrack={({ detail }) => pageY(detail)}
|
on:pressTrack={({ detail }) => pageY(detail)}
|
||||||
/>
|
/>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
@@ -516,7 +516,7 @@
|
|||||||
direction="Horizontal"
|
direction="Horizontal"
|
||||||
handleLength={scrollbarSize.x}
|
handleLength={scrollbarSize.x}
|
||||||
handlePosition={scrollbarPos.x}
|
handlePosition={scrollbarPos.x}
|
||||||
on:handlePosition={({ detail }) => translateCanvasX(detail)}
|
on:handlePosition={({ detail }) => panCanvasX(detail)}
|
||||||
on:pressTrack={({ detail }) => pageX(detail)}
|
on:pressTrack={({ detail }) => pageX(detail)}
|
||||||
/>
|
/>
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
|
|||||||
@@ -633,16 +633,16 @@ impl JsEditorHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Translates document (in viewport coords)
|
/// Translates document (in viewport coords)
|
||||||
#[wasm_bindgen(js_name = translateCanvas)]
|
#[wasm_bindgen(js_name = panCanvas)]
|
||||||
pub fn translate_canvas(&self, delta_x: f64, delta_y: f64) {
|
pub fn translate_canvas(&self, delta_x: f64, delta_y: f64) {
|
||||||
let message = NavigationMessage::TranslateCanvas { delta: (delta_x, delta_y).into() };
|
let message = NavigationMessage::CanvasPan { delta: (delta_x, delta_y).into() };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates document (in viewport coords)
|
/// Translates document (in viewport coords)
|
||||||
#[wasm_bindgen(js_name = translateCanvasByFraction)]
|
#[wasm_bindgen(js_name = panCanvasByFraction)]
|
||||||
pub fn translate_canvas_by_fraction(&self, delta_x: f64, delta_y: f64) {
|
pub fn translate_canvas_by_fraction(&self, delta_x: f64, delta_y: f64) {
|
||||||
let message = NavigationMessage::TranslateCanvasByViewportFraction { delta: (delta_x, delta_y).into() };
|
let message = NavigationMessage::CanvasPanByViewportFraction { delta: (delta_x, delta_y).into() };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user