Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 17 additions & 23 deletions editor/src/document/movement_handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::consts::VIEWPORT_ROTATE_SNAP_INTERVAL;
pub use crate::document::layer_panel::*;
use crate::document::DocumentMessage;
use crate::input::keyboard::Key;
use crate::message_prelude::*;
use crate::{
consts::{VIEWPORT_SCROLL_RATE, VIEWPORT_ZOOM_LEVELS, VIEWPORT_ZOOM_MOUSE_RATE, VIEWPORT_ZOOM_SCALE_MAX, VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_WHEEL_RATE},
Expand All @@ -16,12 +17,10 @@ use std::collections::VecDeque;
#[impl_message(Message, DocumentMessage, Movement)]
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum MovementMessage {
MouseMove,
MouseMove { snap_angle: Key },
TranslateCanvasBegin,
WheelCanvasTranslate { use_y_as_x: bool },
RotateCanvasBegin { snap: bool },
EnableSnapping,
DisableSnapping,
RotateCanvasBegin,
ZoomCanvasBegin,
TransformCanvasEnd,
SetCanvasRotation(f64),
Expand Down Expand Up @@ -101,16 +100,10 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessor)> for Moveme
self.translating = true;
self.mouse_pos = ipp.mouse.position;
}
RotateCanvasBegin { snap } => {
RotateCanvasBegin => {
self.rotating = true;
self.snap_rotate = snap;
self.mouse_pos = ipp.mouse.position;
}
EnableSnapping => self.snap_rotate = true,
DisableSnapping => {
self.rotation = self.snapped_angle();
self.snap_rotate = false
}
ZoomCanvasBegin => {
self.zooming = true;
self.mouse_pos = ipp.mouse.position;
Expand All @@ -123,7 +116,7 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessor)> for Moveme
self.rotating = false;
self.zooming = false;
}
MouseMove => {
MouseMove { snap_angle } => {
if self.translating {
let delta = ipp.mouse.position - self.mouse_pos;
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
Expand All @@ -132,7 +125,15 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessor)> for Moveme
responses.push_back(ToolMessage::DocumentIsDirty.into());
self.create_document_transform(&ipp.viewport_bounds, responses);
}

if self.rotating {
let new_snap = ipp.keyboard.get(snap_angle as usize);
// When disabling snap, keep the viewed rotation as it was previously.
if !new_snap && self.snap_rotate {
self.rotation = self.snapped_angle();
}
self.snap_rotate = new_snap;

let half_viewport = ipp.viewport_bounds.size() / 2.;
let rotation = {
let start_vec = self.mouse_pos - half_viewport;
Expand All @@ -151,8 +152,8 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessor)> for Moveme

let new = (self.scale * amount).clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
self.scale = new;
responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into());
responses.push_back(ToolMessage::DocumentIsDirty.into());
responses.push_back(FrontendMessage::SetCanvasZoom { new_zoom: self.scale }.into());
self.create_document_transform(&ipp.viewport_bounds, responses);
}
self.mouse_pos = ipp.mouse.position;
Expand Down Expand Up @@ -213,11 +214,11 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessor)> for Moveme
responses.push_back(ToolMessage::DocumentIsDirty.into());
self.create_document_transform(&ipp.viewport_bounds, responses);
}
SetCanvasRotation(new) => {
self.rotation = new;
SetCanvasRotation(new_radians) => {
self.rotation = new_radians;
self.create_document_transform(&ipp.viewport_bounds, responses);
responses.push_back(ToolMessage::DocumentIsDirty.into());
responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: new }.into());
responses.push_back(FrontendMessage::SetCanvasRotation { new_radians }.into());
}
ZoomCanvasToFitAll => {
if let Some([pos1, pos2]) = document.visible_layers_bounding_box() {
Expand Down Expand Up @@ -272,13 +273,6 @@ impl MessageHandler<MovementMessage, (&Document, &InputPreprocessor)> for Moveme
TranslateCanvasByViewportFraction,
);

if self.rotating {
let snapping = actions!(MovementMessageDiscriminant;
EnableSnapping,
DisableSnapping,
);
common.extend(snapping);
}
if self.translating || self.rotating || self.zooming {
let transforming = actions!(MovementMessageDiscriminant;
TransformCanvasEnd,
Expand Down
16 changes: 10 additions & 6 deletions editor/src/input/input_mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,6 @@ impl Default for Mapping {
let mappings = mapping![
// Higher priority than entries in sections below
entry! {action=DocumentsMessage::Paste(User), key_down=KeyV, modifiers=[KeyControl]},
entry! {action=MovementMessage::EnableSnapping, key_down=KeyShift},
entry! {action=MovementMessage::DisableSnapping, key_up=KeyShift},
// Transform layers
entry! {action=TransformLayerMessage::ApplyOperation, key_down=KeyEnter},
entry! {action=TransformLayerMessage::ApplyOperation, key_down=Lmb},
Expand All @@ -155,6 +153,12 @@ impl Default for Mapping {
entry! {action=SelectMessage::DragStop, key_up=Lmb},
entry! {action=SelectMessage::Abort, key_down=Rmb},
entry! {action=SelectMessage::Abort, key_down=KeyEscape},
// Navigate
entry! {action=NavigateMessage::MouseMove{snap_angle: KeyControl}, message=InputMapperMessage::PointerMove},
entry! {action=NavigateMessage::RotateCanvasBegin, key_down=Rmb},
entry! {action=NavigateMessage::ZoomCanvasBegin, key_down=Lmb},
entry! {action=NavigateMessage::TransformCanvasEnd, key_up=Rmb},
entry! {action=NavigateMessage::TransformCanvasEnd, key_up=Lmb},
// Eyedropper
entry! {action=EyedropperMessage::LeftMouseDown, key_down=Lmb},
entry! {action=EyedropperMessage::RightMouseDown, key_down=Rmb},
Expand Down Expand Up @@ -194,6 +198,7 @@ impl Default for Mapping {
entry! {action=FillMessage::RightMouseDown, key_down=Rmb},
// Tool Actions
entry! {action=ToolMessage::ActivateTool(ToolType::Select), key_down=KeyV},
entry! {action=ToolMessage::ActivateTool(ToolType::Navigate), key_down=KeyZ},
entry! {action=ToolMessage::ActivateTool(ToolType::Eyedropper), key_down=KeyI},
entry! {action=ToolMessage::ActivateTool(ToolType::Fill), key_down=KeyF},
entry! {action=ToolMessage::ActivateTool(ToolType::Path), key_down=KeyA},
Expand Down Expand Up @@ -226,11 +231,9 @@ impl Default for Mapping {
entry! {action=TransformLayerMessage::BeginRotate, key_down=KeyR},
entry! {action=TransformLayerMessage::BeginScale, key_down=KeyS},
// Document movement
entry! {action=MovementMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=MovementMessage::RotateCanvasBegin{snap:false}, key_down=Mmb, modifiers=[KeyControl]},
entry! {action=MovementMessage::RotateCanvasBegin{snap:true}, key_down=Mmb, modifiers=[KeyControl, KeyShift]},
entry! {action=MovementMessage::MouseMove{snap_angle: KeyShift}, message=InputMapperMessage::PointerMove},
entry! {action=MovementMessage::RotateCanvasBegin, key_down=Mmb, modifiers=[KeyControl]},
entry! {action=MovementMessage::ZoomCanvasBegin, key_down=Mmb, modifiers=[KeyShift]},
entry! {action=MovementMessage::ZoomCanvasToFitAll, key_down=Key0, modifiers=[KeyControl]},
entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Mmb},
entry! {action=MovementMessage::TransformCanvasEnd, key_up=Mmb},
entry! {action=MovementMessage::TranslateCanvasBegin, key_down=Lmb, modifiers=[KeySpace]},
Expand All @@ -240,6 +243,7 @@ impl Default for Mapping {
entry! {action=MovementMessage::DecreaseCanvasZoom, key_down=KeyMinus, modifiers=[KeyControl]},
entry! {action=MovementMessage::SetCanvasZoom(1.), key_down=Key1, modifiers=[KeyControl]},
entry! {action=MovementMessage::SetCanvasZoom(2.), key_down=Key2, modifiers=[KeyControl]},
entry! {action=MovementMessage::ZoomCanvasToFitAll, key_down=Key0, modifiers=[KeyControl]},
entry! {action=MovementMessage::WheelCanvasZoom, message=InputMapperMessage::MouseScroll, modifiers=[KeyControl]},
entry! {action=MovementMessage::WheelCanvasTranslate{use_y_as_x: true}, message=InputMapperMessage::MouseScroll, modifiers=[KeyShift]},
entry! {action=MovementMessage::WheelCanvasTranslate{use_y_as_x: false}, message=InputMapperMessage::MouseScroll},
Expand Down
2 changes: 2 additions & 0 deletions editor/src/tool/tool_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ fn standard_tool_message(tool: ToolType, message_type: StandardToolMessageType)
StandardToolMessageType::DocumentIsDirty => match tool {
ToolType::Select => Some(SelectMessage::DocumentIsDirty.into()),
ToolType::Path => Some(PathMessage::DocumentIsDirty.into()),
//ToolType::Navigate => Some(NavigateMessage::DocumentIsDirty.into())
// ToolType::Pen => Some(PenMessage::DocumentIsDirty.into()),
// ToolType::Line => Some(LineMessage::DocumentIsDirty.into()),
// ToolType::Rectangle => Some(RectangleMessage::DocumentIsDirty.into()),
Expand All @@ -178,6 +179,7 @@ fn standard_tool_message(tool: ToolType, message_type: StandardToolMessageType)
StandardToolMessageType::Abort => match tool {
ToolType::Select => Some(SelectMessage::Abort.into()),
ToolType::Path => Some(PathMessage::Abort.into()),
ToolType::Navigate => Some(NavigateMessage::Abort.into()),
ToolType::Pen => Some(PenMessage::Abort.into()),
ToolType::Line => Some(LineMessage::Abort.into()),
ToolType::Rectangle => Some(RectangleMessage::Abort.into()),
Expand Down
139 changes: 133 additions & 6 deletions editor/src/tool/tools/navigate.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,147 @@
use crate::message_prelude::*;
use crate::tool::ToolActionHandlerData;
use crate::input::keyboard::MouseMotion;
use crate::misc::{HintData, HintGroup, HintInfo, KeysGroup};
use crate::tool::{Fsm, ToolActionHandlerData};
use crate::{input::keyboard::Key, message_prelude::*};
use serde::{Deserialize, Serialize};

#[derive(Default)]
pub struct Navigate;
pub struct Navigate {
fsm_state: NavigateToolFsmState,
}

#[impl_message(Message, ToolMessage, Navigate)]
#[derive(PartialEq, Clone, Debug, Hash, Serialize, Deserialize)]
pub enum NavigateMessage {
MouseMove,
MouseMove { snap_angle: Key },
TranslateCanvasBegin,
RotateCanvasBegin,
ZoomCanvasBegin,
TransformCanvasEnd,
Abort,
}

impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Navigate {
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
todo!("{}::handle_input {:?} {:?} {:?} ", module_path!(), action, data, responses);
if action == ToolMessage::UpdateHints {
self.fsm_state.update_hints(responses);
return;
}

let new_state = self.fsm_state.transition(action, data.0, data.1, &mut (), data.2, responses);

if self.fsm_state != new_state {
self.fsm_state = new_state;
self.fsm_state.update_hints(responses);
}
}

fn actions(&self) -> ActionList {
use NavigateToolFsmState::*;
match self.fsm_state {
Ready => actions!(NavigateMessageDiscriminant; TranslateCanvasBegin, RotateCanvasBegin, ZoomCanvasBegin),
_ => actions!(NavigateMessageDiscriminant; MouseMove, TransformCanvasEnd),
}
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum NavigateToolFsmState {
Ready,
Translating,
Rotating,
Zooming,
}

impl Default for NavigateToolFsmState {
fn default() -> Self {
NavigateToolFsmState::Ready
}
}

impl Fsm for NavigateToolFsmState {
type ToolData = ();

fn transition(
self,
message: ToolMessage,
_document: &crate::document::DocumentMessageHandler,
_tool_data: &crate::tool::DocumentToolData,
_data: &mut Self::ToolData,
_input: &crate::input::InputPreprocessor,
messages: &mut VecDeque<Message>,
) -> Self {
if let ToolMessage::Navigate(navigate) = message {
use NavigateMessage::*;
match navigate {
MouseMove { snap_angle } => {
messages.push_front(MovementMessage::MouseMove { snap_angle }.into());
self
}
TranslateCanvasBegin => {
messages.push_front(MovementMessage::TranslateCanvasBegin.into());
NavigateToolFsmState::Translating
}
RotateCanvasBegin => {
messages.push_front(MovementMessage::RotateCanvasBegin.into());
NavigateToolFsmState::Rotating
}
ZoomCanvasBegin => {
messages.push_front(MovementMessage::ZoomCanvasBegin.into());
NavigateToolFsmState::Zooming
}
TransformCanvasEnd => {
messages.push_front(MovementMessage::TransformCanvasEnd.into());
NavigateToolFsmState::Ready
}
Abort => {
messages.push_front(MovementMessage::TransformCanvasEnd.into());
NavigateToolFsmState::Ready
}
}
} else {
self
}
}

advertise_actions!();
fn update_hints(&self, responses: &mut VecDeque<Message>) {
let hint_data = match self {
NavigateToolFsmState::Ready => HintData(vec![
HintGroup(vec![HintInfo {
key_groups: vec![],
mouse: Some(MouseMotion::MmbDrag),
label: String::from("Translate"),
plus: false,
}]),
HintGroup(vec![
HintInfo {
key_groups: vec![],
mouse: Some(MouseMotion::RmbDrag),
label: String::from("Rotate (drag around centre)"),
plus: false,
},
HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyControl])],
mouse: None,
label: String::from("Snap rotation to 15° increments"),
plus: true,
},
]),
HintGroup(vec![HintInfo {
key_groups: vec![],
mouse: Some(MouseMotion::LmbDrag),
label: String::from("Zoom in and out (drag up and down)"),
plus: false,
}]),
]),
NavigateToolFsmState::Rotating => HintData(vec![HintGroup(vec![HintInfo {
key_groups: vec![KeysGroup(vec![Key::KeyControl])],
mouse: None,
label: String::from("Snap to 15° increments"),
plus: false,
}])]),
_ => HintData(Vec::new()),
};

responses.push_back(FrontendMessage::UpdateInputHints { hint_data }.into());
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/panels/Document.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
<div class="tools scrollable-y">
<ShelfItemInput icon="LayoutSelectTool" title="Select Tool (V)" :active="activeTool === 'Select'" :action="() => selectTool('Select')" />
<ShelfItemInput icon="LayoutCropTool" title="Crop Tool" :active="activeTool === 'Crop'" :action="() => dialog.comingSoon(289) && selectTool('Crop')" />
<ShelfItemInput icon="LayoutNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => dialog.comingSoon(155) && selectTool('Navigate')" />
<ShelfItemInput icon="LayoutNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => selectTool('Navigate')" />
<ShelfItemInput icon="LayoutEyedropperTool" title="Eyedropper Tool (I)" :active="activeTool === 'Eyedropper'" :action="() => selectTool('Eyedropper')" />

<Separator :type="SeparatorType.Section" :direction="SeparatorDirection.Vertical" />
Expand Down