mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 14:58:05 +08:00
Rework navigation tool hints and navigation shortcuts (#1419)
* Added Pan,Tilt,Zoom to View menu + Tilt shortcut changes * formating * fixed menus, added action for when dispatch happens from menu * set key mappings to spec * fix labels for navigation tool * fix some spacing * add action that locks spacebar for navigation when using it as a modifier so the node overlay does not toggle * escape from the node-graph-overlay, set toggle to ctrl+space, fix demo art * move all transform state into an enum * rename confusing flag * fix demo art and tests --------- Co-authored-by: Ezbaze <rybitwamateusz3@gmail.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -79,7 +79,7 @@ pub const DEFAULT_FONT_FAMILY: &str = "Merriweather";
|
||||
pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
||||
|
||||
// Document
|
||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.17"; // Remember to update the demo artwork in /demos with both this version number and the contents so it remains editable
|
||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.18"; // Remember to update the demo artwork in /demos with both this version number and the contents so it remains editable
|
||||
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
||||
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
||||
pub const MAX_UNDO_HISTORY_LEN: usize = 100; // TODO: Add this to user preferences
|
||||
|
||||
@@ -37,6 +37,9 @@ pub fn default_mapping() -> Mapping {
|
||||
refresh_keys=[Control],
|
||||
action_dispatch=NavigationMessage::PointerMove { snap_angle: Control, wait_for_snap_angle_release: true, snap_zoom: Control, zoom_from_viewport: None },
|
||||
),
|
||||
entry!(KeyDown(Lmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Lmb }),
|
||||
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Mmb }),
|
||||
entry!(KeyDown(Rmb); action_dispatch=NavigationMessage::TransformFromMenuEnd { commit_key: Key::Rmb }),
|
||||
// NORMAL PRIORITY:
|
||||
//
|
||||
// NodeGraphMessage
|
||||
@@ -106,8 +109,7 @@ pub fn default_mapping() -> Mapping {
|
||||
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(Mmb); action_dispatch=NavigateToolMessage::TranslateCanvasBegin),
|
||||
entry!(KeyDown(Rmb); action_dispatch=NavigateToolMessage::RotateCanvasBegin),
|
||||
entry!(KeyDown(Lmb); modifiers=[Alt], action_dispatch=NavigateToolMessage::RotateCanvasBegin),
|
||||
entry!(KeyDown(Lmb); action_dispatch=NavigateToolMessage::ZoomCanvasBegin),
|
||||
entry!(KeyUp(Rmb); action_dispatch=NavigateToolMessage::TransformCanvasEnd),
|
||||
entry!(KeyUp(Lmb); action_dispatch=NavigateToolMessage::TransformCanvasEnd),
|
||||
@@ -315,12 +317,15 @@ pub fn default_mapping() -> Mapping {
|
||||
entry!(KeyDown(KeyS); action_dispatch=TransformLayerMessage::BeginScale),
|
||||
//
|
||||
// NavigationMessage
|
||||
entry!(KeyDown(Mmb); modifiers=[Control], action_dispatch=NavigationMessage::RotateCanvasBegin),
|
||||
entry!(KeyDown(Lmb); modifiers=[Alt], action_dispatch=NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false }),
|
||||
entry!(KeyDown(Mmb); modifiers=[Alt], action_dispatch=NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false }),
|
||||
entry!(KeyDown(Mmb); modifiers=[Shift], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
||||
entry!(KeyDown(Lmb); modifiers=[Shift, Space], action_dispatch=NavigationMessage::ZoomCanvasBegin),
|
||||
entry!(KeyDown(Mmb); action_dispatch=NavigationMessage::TranslateCanvasBegin),
|
||||
entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::TransformCanvasEnd),
|
||||
entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: false }),
|
||||
entry!(KeyDown(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TranslateCanvasBegin),
|
||||
entry!(KeyUp(Lmb); action_dispatch=NavigationMessage::TransformCanvasEnd),
|
||||
entry!(KeyUp(Lmb); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: false }),
|
||||
entry!(KeyUp(Rmb); action_dispatch=NavigationMessage::TransformCanvasEnd { abort_transform: true }),
|
||||
entry!(KeyDown(NumpadAdd); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
||||
entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
||||
entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }),
|
||||
@@ -334,8 +339,8 @@ pub fn default_mapping() -> Mapping {
|
||||
entry!(KeyDown(Period); action_dispatch=NavigationMessage::FitViewportToSelection),
|
||||
//
|
||||
// PortfolioMessage
|
||||
entry!(KeyUp(Space); action_dispatch=PortfolioMessage::GraphViewOverlayToggle),
|
||||
entry!(KeyDownNoRepeat(Space); action_dispatch=PortfolioMessage::GraphViewOverlayToggleDisabled { disabled: false }),
|
||||
entry!(KeyDown(Space); modifiers=[Control], action_dispatch=PortfolioMessage::GraphViewOverlayToggle),
|
||||
entry!(KeyUp(Escape); action_dispatch=PortfolioMessage::GraphViewOverlay { open: false }),
|
||||
entry!(KeyDown(Tab); modifiers=[Control], action_dispatch=PortfolioMessage::NextDocument),
|
||||
entry!(KeyDown(Tab); modifiers=[Control, Shift], action_dispatch=PortfolioMessage::PrevDocument),
|
||||
entry!(KeyDown(KeyW); modifiers=[Accel], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
|
||||
|
||||
@@ -27,14 +27,21 @@ pub enum NavigationMessage {
|
||||
snap_zoom: Key,
|
||||
zoom_from_viewport: Option<DVec2>,
|
||||
},
|
||||
RotateCanvasBegin,
|
||||
RotateCanvasBegin {
|
||||
was_dispatched_from_menu: bool,
|
||||
},
|
||||
SetCanvasRotation {
|
||||
angle_radians: f64,
|
||||
},
|
||||
SetCanvasZoom {
|
||||
zoom_factor: f64,
|
||||
},
|
||||
TransformCanvasEnd,
|
||||
TransformCanvasEnd {
|
||||
abort_transform: bool,
|
||||
},
|
||||
TransformFromMenuEnd {
|
||||
commit_key: Key,
|
||||
},
|
||||
TranslateCanvas {
|
||||
delta: DVec2,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::consts::{
|
||||
VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR, VIEWPORT_ZOOM_WHEEL_RATE,
|
||||
};
|
||||
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup};
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, KeysGroup, MouseMotion};
|
||||
use crate::messages::input_mapper::utility_types::input_mouse::{ViewportBounds, ViewportPosition};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
@@ -15,39 +15,35 @@ use graphene_core::renderer::format_transform_matrix;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
enum TransformOperation {
|
||||
None,
|
||||
Pan { pre_commit_pan: DVec2 },
|
||||
Rotate { pre_commit_tilt: f64, snap_tilt: bool, snap_tilt_released: bool },
|
||||
Zoom { pre_commit_zoom: f64, snap_zoom_enabled: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct NavigationMessageHandler {
|
||||
pub pan: DVec2,
|
||||
panning: bool,
|
||||
snap_tilt: bool,
|
||||
snap_tilt_released: bool,
|
||||
|
||||
pub tilt: f64,
|
||||
tilting: bool,
|
||||
|
||||
pub zoom: f64,
|
||||
zooming: bool,
|
||||
snap_zoom: bool,
|
||||
|
||||
transform_operation: TransformOperation,
|
||||
mouse_position: ViewportPosition,
|
||||
finish_operation_with_click: bool,
|
||||
}
|
||||
|
||||
impl Default for NavigationMessageHandler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pan: DVec2::ZERO,
|
||||
panning: false,
|
||||
snap_tilt: false,
|
||||
snap_tilt_released: false,
|
||||
|
||||
tilt: 0.,
|
||||
tilting: false,
|
||||
|
||||
zoom: 1.,
|
||||
zooming: false,
|
||||
snap_zoom: false,
|
||||
|
||||
mouse_position: ViewportPosition::default(),
|
||||
finish_operation_with_click: false,
|
||||
transform_operation: TransformOperation::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,75 +120,99 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||
snap_zoom,
|
||||
zoom_from_viewport,
|
||||
} => {
|
||||
if self.panning {
|
||||
let delta = ipp.mouse.position - self.mouse_position;
|
||||
match self.transform_operation {
|
||||
TransformOperation::None => {}
|
||||
TransformOperation::Pan { .. } => {
|
||||
let delta = ipp.mouse.position - self.mouse_position;
|
||||
responses.add(TranslateCanvas { delta });
|
||||
}
|
||||
TransformOperation::Rotate {
|
||||
snap_tilt,
|
||||
snap_tilt_released,
|
||||
pre_commit_tilt,
|
||||
} => {
|
||||
let new_snap = ipp.keyboard.get(snap_angle as usize);
|
||||
|
||||
responses.add(TranslateCanvas { delta });
|
||||
}
|
||||
|
||||
if self.tilting {
|
||||
let new_snap = ipp.keyboard.get(snap_angle as usize);
|
||||
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_tilt {
|
||||
self.tilt = self.snapped_angle();
|
||||
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 {
|
||||
self.tilt = self.snapped_angle();
|
||||
}
|
||||
self.transform_operation = TransformOperation::Rotate {
|
||||
pre_commit_tilt,
|
||||
snap_tilt: new_snap,
|
||||
snap_tilt_released: true,
|
||||
};
|
||||
}
|
||||
self.snap_tilt = new_snap;
|
||||
self.snap_tilt_released = true;
|
||||
|
||||
let half_viewport = ipp.viewport_bounds.size() / 2.;
|
||||
let rotation = {
|
||||
let start_offset = self.mouse_position - half_viewport;
|
||||
let end_offset = ipp.mouse.position - half_viewport;
|
||||
start_offset.angle_between(end_offset)
|
||||
};
|
||||
|
||||
responses.add(SetCanvasRotation { angle_radians: self.tilt + rotation });
|
||||
}
|
||||
TransformOperation::Zoom { snap_zoom_enabled, pre_commit_zoom } => {
|
||||
let zoom_start = self.snapped_scale();
|
||||
|
||||
let half_viewport = ipp.viewport_bounds.size() / 2.;
|
||||
let rotation = {
|
||||
let start_offset = self.mouse_position - half_viewport;
|
||||
let end_offset = ipp.mouse.position - half_viewport;
|
||||
start_offset.angle_between(end_offset)
|
||||
};
|
||||
let new_snap = ipp.keyboard.get(snap_zoom as usize);
|
||||
// When disabling snap, keep the viewed zoom as it was previously
|
||||
if !new_snap && snap_zoom_enabled {
|
||||
self.zoom = self.snapped_scale();
|
||||
}
|
||||
|
||||
responses.add(SetCanvasRotation { angle_radians: self.tilt + rotation });
|
||||
}
|
||||
if snap_zoom_enabled != new_snap {
|
||||
self.transform_operation = TransformOperation::Zoom {
|
||||
pre_commit_zoom,
|
||||
snap_zoom_enabled: new_snap,
|
||||
};
|
||||
}
|
||||
|
||||
if self.zooming {
|
||||
let zoom_start = self.snapped_scale();
|
||||
let difference = self.mouse_position.y - ipp.mouse.position.y;
|
||||
let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE;
|
||||
|
||||
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_zoom {
|
||||
self.zoom = self.snapped_scale();
|
||||
}
|
||||
self.snap_zoom = new_snap;
|
||||
self.zoom *= amount;
|
||||
self.zoom *= Self::clamp_zoom(self.zoom, document_bounds, old_zoom, ipp);
|
||||
|
||||
let difference = self.mouse_position.y - ipp.mouse.position.y;
|
||||
let amount = 1. + difference * VIEWPORT_ZOOM_MOUSE_RATE;
|
||||
if let Some(mouse) = zoom_from_viewport {
|
||||
let zoom_factor = self.snapped_scale() / zoom_start;
|
||||
|
||||
self.zoom *= amount;
|
||||
self.zoom *= Self::clamp_zoom(self.zoom, document_bounds, old_zoom, ipp);
|
||||
|
||||
if let Some(mouse) = zoom_from_viewport {
|
||||
let zoom_factor = self.snapped_scale() / zoom_start;
|
||||
|
||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom });
|
||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse));
|
||||
} else {
|
||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom });
|
||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom });
|
||||
responses.add(self.center_zoom(ipp.viewport_bounds.size(), zoom_factor, mouse));
|
||||
} else {
|
||||
responses.add(SetCanvasZoom { zoom_factor: self.zoom });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.mouse_position = ipp.mouse.position;
|
||||
}
|
||||
RotateCanvasBegin => {
|
||||
RotateCanvasBegin { was_dispatched_from_menu } => {
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
|
||||
responses.add(FrontendMessage::UpdateInputHints {
|
||||
hint_data: HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}])]),
|
||||
hint_data: HintData(vec![
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Confirm")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, "Abort")]),
|
||||
]),
|
||||
});
|
||||
|
||||
self.tilting = true;
|
||||
self.transform_operation = TransformOperation::Rotate {
|
||||
pre_commit_tilt: self.tilt,
|
||||
snap_tilt_released: false,
|
||||
snap_tilt: false,
|
||||
};
|
||||
|
||||
self.mouse_position = ipp.mouse.position;
|
||||
self.finish_operation_with_click = was_dispatched_from_menu;
|
||||
}
|
||||
SetCanvasRotation { angle_radians } => {
|
||||
self.tilt = angle_radians;
|
||||
@@ -208,19 +228,38 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||
}
|
||||
TransformCanvasEnd => {
|
||||
TransformCanvasEnd { abort_transform } => {
|
||||
if abort_transform {
|
||||
match self.transform_operation {
|
||||
TransformOperation::None => {}
|
||||
TransformOperation::Rotate { pre_commit_tilt, .. } => {
|
||||
responses.add(SetCanvasRotation { angle_radians: pre_commit_tilt });
|
||||
}
|
||||
TransformOperation::Pan { pre_commit_pan, .. } => {
|
||||
self.pan = pre_commit_pan;
|
||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||
}
|
||||
TransformOperation::Zoom { pre_commit_zoom, .. } => {
|
||||
self.zoom = pre_commit_zoom;
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
self.create_document_transform(&ipp.viewport_bounds, responses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tilt = self.snapped_angle();
|
||||
self.zoom = self.snapped_scale();
|
||||
responses.add(BroadcastEvent::CanvasTransformed);
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
responses.add(ToolMessage::UpdateCursor);
|
||||
responses.add(ToolMessage::UpdateHints);
|
||||
self.snap_tilt = false;
|
||||
self.snap_tilt_released = false;
|
||||
self.snap_zoom = false;
|
||||
self.panning = false;
|
||||
self.tilting = false;
|
||||
self.zooming = false;
|
||||
self.transform_operation = TransformOperation::None;
|
||||
responses.add(PortfolioMessage::GraphViewOverlayToggleDisabled { disabled: false });
|
||||
}
|
||||
TransformFromMenuEnd { commit_key } => {
|
||||
let abort_transform = commit_key == Key::Rmb;
|
||||
self.finish_operation_with_click = false;
|
||||
responses.add(TransformCanvasEnd { abort_transform });
|
||||
}
|
||||
TranslateCanvas { delta } => {
|
||||
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
|
||||
@@ -232,13 +271,16 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||
}
|
||||
TranslateCanvasBegin => {
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Grabbing });
|
||||
responses.add(FrontendMessage::UpdateInputHints { hint_data: HintData(Vec::new()) });
|
||||
|
||||
responses.add(FrontendMessage::UpdateInputHints {
|
||||
hint_data: HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, "Abort")])]),
|
||||
});
|
||||
// Because the pan key shares the Spacebar with toggling the graph view overlay, now that we've begun panning,
|
||||
// we need to prevent the graph view overlay from toggling when the Spacebar is released.
|
||||
// we need to prevent the graph view overlay from toggling when the control key is pressed.
|
||||
responses.add(PortfolioMessage::GraphViewOverlayToggleDisabled { disabled: true });
|
||||
|
||||
self.panning = true;
|
||||
self.mouse_position = ipp.mouse.position;
|
||||
self.transform_operation = TransformOperation::Pan { pre_commit_pan: self.pan };
|
||||
}
|
||||
TranslateCanvasByViewportFraction { delta } => {
|
||||
let transformed_delta = document.root.transform.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
|
||||
@@ -268,17 +310,27 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||
ZoomCanvasBegin => {
|
||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::ZoomIn });
|
||||
responses.add(FrontendMessage::UpdateInputHints {
|
||||
hint_data: HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
plus: false,
|
||||
}])]),
|
||||
hint_data: HintData(vec![
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, "Abort")]),
|
||||
]),
|
||||
});
|
||||
|
||||
self.zooming = true;
|
||||
self.transform_operation = TransformOperation::Zoom {
|
||||
pre_commit_zoom: self.zoom,
|
||||
snap_zoom_enabled: false,
|
||||
};
|
||||
self.mouse_position = ipp.mouse.position;
|
||||
|
||||
// Because the zoom key shares the Spacebar with toggling the graph view overlay, now that we've begun zooming,
|
||||
// we need to prevent the graph view overlay from toggling when the control key is pressed.
|
||||
responses.add(PortfolioMessage::GraphViewOverlayToggleDisabled { disabled: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,13 +350,22 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||
FitViewportToSelection,
|
||||
);
|
||||
|
||||
if self.panning || self.tilting || self.zooming {
|
||||
if self.transform_operation != TransformOperation::None {
|
||||
let transforming = actions!(NavigationMessageDiscriminant;
|
||||
PointerMove,
|
||||
TransformCanvasEnd,
|
||||
);
|
||||
common.extend(transforming);
|
||||
}
|
||||
|
||||
if self.finish_operation_with_click {
|
||||
let transforming_from_menu = actions!(NavigationMessageDiscriminant;
|
||||
TransformFromMenuEnd,
|
||||
);
|
||||
|
||||
common.extend(transforming_from_menu);
|
||||
}
|
||||
|
||||
common
|
||||
}
|
||||
}
|
||||
@@ -312,7 +373,7 @@ impl MessageHandler<NavigationMessage, (&Document, Option<[DVec2; 2]>, &InputPre
|
||||
impl NavigationMessageHandler {
|
||||
pub fn snapped_angle(&self) -> f64 {
|
||||
let increment_radians: f64 = VIEWPORT_ROTATE_SNAP_INTERVAL.to_radians();
|
||||
if self.snap_tilt {
|
||||
if let TransformOperation::Rotate { snap_tilt: true, .. } = self.transform_operation {
|
||||
(self.tilt / increment_radians).round() * increment_radians
|
||||
} else {
|
||||
self.tilt
|
||||
@@ -320,7 +381,7 @@ impl NavigationMessageHandler {
|
||||
}
|
||||
|
||||
pub fn snapped_scale(&self) -> f64 {
|
||||
if self.snap_zoom {
|
||||
if let TransformOperation::Zoom { snap_zoom_enabled: true, .. } = self.transform_operation {
|
||||
*VIEWPORT_ZOOM_LEVELS
|
||||
.iter()
|
||||
.min_by(|a, b| (**a - self.zoom).abs().partial_cmp(&(**b - self.zoom).abs()).unwrap())
|
||||
|
||||
@@ -257,36 +257,68 @@ impl LayoutHolder for MenuBarMessageHandler {
|
||||
MenuBarEntry::new_root(
|
||||
"View".into(),
|
||||
no_active_document,
|
||||
MenuBarEntryChildren(vec![vec![
|
||||
MenuBarEntry {
|
||||
label: "Zoom to Selected".into(),
|
||||
MenuBarEntryChildren(vec![
|
||||
vec![
|
||||
MenuBarEntry {
|
||||
label: "Tilt".into(),
|
||||
shortcut: action_keys!(NavigationMessageDiscriminant::RotateCanvasBegin),
|
||||
action: MenuBarEntry::create_action(|_| NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: true }.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Reset Tilt".into(),
|
||||
shortcut: action_keys!(NavigationMessageDiscriminant::SetCanvasRotation),
|
||||
action: MenuBarEntry::create_action(|_| NavigationMessage::SetCanvasRotation { angle_radians: 0.into() }.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
],
|
||||
vec![
|
||||
MenuBarEntry {
|
||||
label: "Zoom In".into(),
|
||||
shortcut: action_keys!(NavigationMessageDiscriminant::IncreaseCanvasZoom),
|
||||
action: MenuBarEntry::create_action(|_| NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom Out".into(),
|
||||
shortcut: action_keys!(NavigationMessageDiscriminant::DecreaseCanvasZoom),
|
||||
action: MenuBarEntry::create_action(|_| NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom to Fit".into(),
|
||||
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasToFitAll),
|
||||
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasToFitAll.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom to 100%".into(),
|
||||
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent),
|
||||
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasTo100Percent.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom to 200%".into(),
|
||||
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo200Percent),
|
||||
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasTo200Percent.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
],
|
||||
vec![MenuBarEntry {
|
||||
label: "Frame Selected".into(),
|
||||
shortcut: action_keys!(NavigationMessageDiscriminant::FitViewportToSelection),
|
||||
action: MenuBarEntry::create_action(|_| NavigationMessage::FitViewportToSelection.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom to Fit".into(),
|
||||
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasToFitAll),
|
||||
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasToFitAll.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom to 100%".into(),
|
||||
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo100Percent),
|
||||
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasTo100Percent.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
MenuBarEntry {
|
||||
label: "Zoom to 200%".into(),
|
||||
shortcut: action_keys!(DocumentMessageDiscriminant::ZoomCanvasTo200Percent),
|
||||
action: MenuBarEntry::create_action(|_| DocumentMessage::ZoomCanvasTo200Percent.into()),
|
||||
disabled: no_active_document,
|
||||
..MenuBarEntry::default()
|
||||
},
|
||||
]]),
|
||||
}],
|
||||
]),
|
||||
),
|
||||
MenuBarEntry::new_root(
|
||||
"Help".into(),
|
||||
|
||||
@@ -557,6 +557,11 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||
PrevDocument,
|
||||
);
|
||||
|
||||
if self.graph_view_overlay_open {
|
||||
let escape = actions!(PortfolioMessageDiscriminant; GraphViewOverlay);
|
||||
common.extend(escape);
|
||||
}
|
||||
|
||||
if let Some(document) = self.active_document() {
|
||||
if document.layer_metadata.values().any(|data| data.selected) {
|
||||
let select = actions!(PortfolioMessageDiscriminant;
|
||||
|
||||
@@ -117,7 +117,7 @@ impl Fsm for NavigateToolFsmState {
|
||||
|
||||
match navigate {
|
||||
ClickZoom { zoom_in } => {
|
||||
responses.add_front(NavigationMessage::TransformCanvasEnd);
|
||||
responses.add_front(NavigationMessage::TransformCanvasEnd { abort_transform: false });
|
||||
|
||||
// Mouse has not moved from pointerdown to pointerup
|
||||
if tool_data.drag_start == input.mouse.position {
|
||||
@@ -146,7 +146,7 @@ impl Fsm for NavigateToolFsmState {
|
||||
}
|
||||
RotateCanvasBegin => {
|
||||
tool_data.drag_start = input.mouse.position;
|
||||
responses.add_front(NavigationMessage::RotateCanvasBegin);
|
||||
responses.add_front(NavigationMessage::RotateCanvasBegin { was_dispatched_from_menu: false });
|
||||
NavigateToolFsmState::Tilting
|
||||
}
|
||||
ZoomCanvasBegin => {
|
||||
@@ -155,11 +155,11 @@ impl Fsm for NavigateToolFsmState {
|
||||
NavigateToolFsmState::Zooming
|
||||
}
|
||||
TransformCanvasEnd => {
|
||||
responses.add_front(NavigationMessage::TransformCanvasEnd);
|
||||
responses.add_front(NavigationMessage::TransformCanvasEnd { abort_transform: false });
|
||||
NavigateToolFsmState::Ready
|
||||
}
|
||||
Abort => {
|
||||
responses.add_front(NavigationMessage::TransformCanvasEnd);
|
||||
responses.add_front(NavigationMessage::TransformCanvasEnd { abort_transform: false });
|
||||
NavigateToolFsmState::Ready
|
||||
}
|
||||
}
|
||||
@@ -173,8 +173,12 @@ impl Fsm for NavigateToolFsmState {
|
||||
NavigateToolFsmState::Ready => HintData(vec![
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Zoom In"), HintInfo::keys([Key::Shift], "Zoom Out").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Zoom"), HintInfo::keys([Key::Control], "Snap Increments").prepend_plus()]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::MmbDrag, "Pan")]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::RmbDrag, "Tilt"), HintInfo::keys([Key::Control], "Snap 15°").prepend_plus()]),
|
||||
HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, " "),
|
||||
HintInfo::keys([Key::Space], "Or").prepend_plus(),
|
||||
HintInfo::mouse(MouseMotion::MmbDrag, "Pan"),
|
||||
]),
|
||||
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, ""), HintInfo::keys([Key::Alt], "Tilt").prepend_plus()]),
|
||||
]),
|
||||
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")])]),
|
||||
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo::keys([Key::Control], "Snap Increments")])]),
|
||||
|
||||
Reference in New Issue
Block a user