Viewport canvas navigation with modifier keys and zoom widget (#229)

* Add rotation around the center

* Document transform centred

* Fix drawing hexagon on rotated document

* Format

* Fix translation on rotated document

* Remove logging

* Rotate around centre of viewport

* Rotate with shift + MMB drag

* Zoom with +/- keys

* Rotation input field

* Implement frontend zoom buttons

* Zoom with ctrl + MMB

* Format

* Update number inputs

* Require Ctrl + Plus / Minus key

* Ctrl scroll

* Update zoom -> Multiply Zoom

* Fix typo

* More fixing typo

* Remove :v-on

* Add mouse scroll X

* Scrolling on document

* Refactor

* Format

* Fix ctrl + plus/minus to zoom

* Reduce zoom sensitivity

* Ctrl + shift + mmb drag = snap rotate

* Further reduce zoom speed

* Add ctrl + number key to change zoom

* Switch Ctrl and Shift for zoom and rotate

* Fix compile errors

* Format JS

* Add increment to snap angle

* Edit getting layerdata functions

* Pass viewport size directily into create_document_transform_from_layerdata

* Add to_dvec2()

* Refactor get_transform

* Get -> Calculate

* Add consts

* Use to_radians

* Remove get from function names

* Use .entry when getting layerdata that does not exist

* Fix distance scroll calculations

* Fix zooming.

* Remove 'Violation' in chrome

* Fix compile errors
This commit is contained in:
0HyperCube
2021-07-13 07:50:10 +01:00
committed by GitHub
parent 8ff545f01c
commit 39e6893630
22 changed files with 442 additions and 73 deletions

View File

@@ -27,6 +27,8 @@ impl Dispatcher {
| Message::InputMapper(_)
| Message::Document(DocumentMessage::RenderDocument)
| Message::Frontend(FrontendMessage::UpdateCanvas { .. })
| Message::Frontend(FrontendMessage::SetZoom { .. })
| Message::Frontend(FrontendMessage::SetRotation { .. })
| Message::Document(DocumentMessage::DispatchOperation { .. })
) || MessageDiscriminant::from(&message).local_name().ends_with("MouseMove"))
{

View File

@@ -0,0 +1,7 @@
pub const PLUS_KEY_MULTIPLIER: f64 = 1.25;
pub const MINUS_KEY_MULTIPLIER: f64 = 0.8;
pub const WHEEL_ZOOM_DIVISOR: f64 = 500.;
pub const MOUSE_ZOOM_DIVISOR: f64 = 400.;
pub const ROTATE_SNAP_INTERVAL: f64 = 15.;

View File

@@ -1,5 +1,6 @@
use crate::{frontend::layer_panel::*, EditorError};
use crate::{consts::ROTATE_SNAP_INTERVAL, frontend::layer_panel::*, EditorError};
use document_core::{document::Document as InteralDocument, layers::Layer, LayerId};
use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -15,7 +16,7 @@ impl Default for Document {
Self {
document: InteralDocument::default(),
name: String::from("Untitled Document"),
layer_data: vec![(vec![], LayerData { selected: false, expanded: true })].into_iter().collect(),
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
}
}
}
@@ -25,14 +26,14 @@ impl Document {
Self {
document: InteralDocument::default(),
name,
layer_data: vec![(vec![], LayerData { selected: false, expanded: true })].into_iter().collect(),
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
}
}
}
fn layer_data<'a>(layer_data: &'a mut HashMap<Vec<LayerId>, LayerData>, path: &[LayerId]) -> &'a mut LayerData {
if !layer_data.contains_key(path) {
layer_data.insert(path.to_vec(), LayerData::default());
layer_data.insert(path.to_vec(), LayerData::new(false));
}
layer_data.get_mut(path).unwrap()
}
@@ -74,8 +75,43 @@ impl Document {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy, Default)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Copy)]
pub struct LayerData {
pub selected: bool,
pub expanded: bool,
pub translation: DVec2,
pub rotation: f64,
pub snap_rotate: bool,
pub scale: f64,
}
impl LayerData {
pub fn new(expanded: bool) -> LayerData {
LayerData {
selected: false,
expanded,
translation: DVec2::ZERO,
rotation: 0.,
snap_rotate: false,
scale: 1.,
}
}
pub fn snapped_angle(&self) -> f64 {
let increment_radians: f64 = ROTATE_SNAP_INTERVAL.to_radians();
if self.snap_rotate {
(self.rotation / increment_radians).round() * increment_radians
} else {
self.rotation
}
}
pub fn calculate_offset_transform(&self, offset: DVec2) -> DAffine2 {
let offset_transform = DAffine2::from_translation(offset);
let scale_transform = DAffine2::from_scale(DVec2::new(self.scale, self.scale));
let angle_transform = DAffine2::from_angle(self.snapped_angle());
let translation_transform = DAffine2::from_translation(self.translation.into());
scale_transform * offset_transform * angle_transform * scale_transform * translation_transform
}
pub fn calculate_transform(&self) -> DAffine2 {
self.calculate_offset_transform(DVec2::ZERO)
}
}

View File

@@ -1,5 +1,8 @@
use crate::input::{mouse::ViewportPosition, InputPreprocessor};
use crate::message_prelude::*;
use crate::{
consts::{MOUSE_ZOOM_DIVISOR, WHEEL_ZOOM_DIVISOR},
input::{mouse::ViewportPosition, InputPreprocessor},
};
use document_core::layers::Layer;
use document_core::{DocumentResponse, LayerId, Operation as DocumentOperation};
use glam::{DAffine2, DVec2};
@@ -8,6 +11,8 @@ use log::warn;
use crate::document::Document;
use std::collections::VecDeque;
use super::LayerData;
#[impl_message(Message, Document)]
#[derive(PartialEq, Clone, Debug)]
pub enum DocumentMessage {
@@ -36,6 +41,14 @@ pub enum DocumentMessage {
MouseMove,
TranslateDown,
TranslateUp,
WheelTranslate,
RotateDown { snap: bool },
ZoomDown,
TransformUp,
SetZoom(f64),
MultiplyZoom(f64),
WheelZoom,
SetRotation(f64),
NudgeSelectedLayers(f64, f64),
}
@@ -54,7 +67,9 @@ impl From<DocumentOperation> for Message {
pub struct DocumentMessageHandler {
documents: Vec<Document>,
active_document: usize,
mmb_down: bool,
translating: bool,
rotating: bool,
zooming: bool,
mouse_pos: ViewportPosition,
copy_buffer: Vec<Layer>,
}
@@ -86,6 +101,35 @@ impl DocumentMessageHandler {
// TODO: Add deduplication
(!path.is_empty()).then(|| self.handle_folder_changed(path[..path.len() - 1].to_vec())).flatten()
}
fn layerdata(&self, path: &[LayerId]) -> &LayerData {
self.active_document().layer_data.get(path).expect("Layerdata does not exist")
}
fn layerdata_mut(&mut self, path: &[LayerId]) -> &mut LayerData {
self.active_document_mut().layer_data.entry(path.to_vec()).or_insert(LayerData::new(true))
}
#[allow(dead_code)]
fn create_transform_from_layerdata(&self, path: Vec<u64>, responses: &mut VecDeque<Message>) {
let layerdata = self.layerdata(&path);
responses.push_back(
DocumentOperation::SetLayerTransform {
path: path,
transform: layerdata.calculate_transform().to_cols_array(),
}
.into(),
);
}
fn create_document_transform_from_layerdata(&self, viewport_size: &ViewportPosition, responses: &mut VecDeque<Message>) {
let half_viewport = viewport_size.to_dvec2() / 2.;
let layerdata = self.layerdata(&vec![]);
let scaled_half_viewport = half_viewport / layerdata.scale;
responses.push_back(
DocumentOperation::SetLayerTransform {
path: vec![],
transform: layerdata.calculate_offset_transform(scaled_half_viewport).to_cols_array(),
}
.into(),
);
}
/// Returns the paths to the selected layers in order
fn selected_layers_sorted(&self) -> Vec<Vec<LayerId>> {
@@ -119,7 +163,9 @@ impl Default for DocumentMessageHandler {
Self {
documents: vec![Document::default()],
active_document: 0,
mmb_down: false,
translating: false,
rotating: false,
zooming: false,
mouse_pos: ViewportPosition::default(),
copy_buffer: vec![],
}
@@ -335,22 +381,100 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
.into(),
),
TranslateDown => {
self.mmb_down = true;
self.translating = true;
self.mouse_pos = ipp.mouse.position;
}
TranslateUp => {
self.mmb_down = false;
RotateDown { snap } => {
self.rotating = true;
let layerdata = self.layerdata_mut(&vec![]);
layerdata.snap_rotate = snap;
self.mouse_pos = ipp.mouse.position;
}
ZoomDown => {
self.zooming = true;
self.mouse_pos = ipp.mouse.position;
}
TransformUp => {
let layerdata = self.layerdata_mut(&vec![]);
layerdata.rotation = layerdata.snapped_angle();
layerdata.snap_rotate = false;
self.translating = false;
self.rotating = false;
self.zooming = false;
}
MouseMove => {
if self.mmb_down {
let delta = DVec2::new(ipp.mouse.position.x as f64 - self.mouse_pos.x as f64, ipp.mouse.position.y as f64 - self.mouse_pos.y as f64);
let operation = DocumentOperation::TransformLayer {
path: vec![],
transform: DAffine2::from_translation(delta).to_cols_array(),
};
responses.push_back(operation.into());
self.mouse_pos = ipp.mouse.position;
if self.translating {
let delta = ipp.mouse.position.to_dvec2() - self.mouse_pos.to_dvec2();
let transformed_delta = self.active_document().document.root.transform.inverse().transform_vector2(delta);
let layerdata = self.layerdata_mut(&vec![]);
layerdata.translation = layerdata.translation + transformed_delta;
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
if self.rotating {
let half_viewport = ipp.viewport_size.to_dvec2() / 2.;
let rotation = {
let start_vec = self.mouse_pos.to_dvec2() - half_viewport;
let end_vec = ipp.mouse.position.to_dvec2() - half_viewport;
start_vec.angle_between(end_vec)
};
let layerdata = self.layerdata_mut(&vec![]);
layerdata.rotation += rotation;
responses.push_back(
FrontendMessage::SetRotation {
new_radians: layerdata.snapped_angle(),
}
.into(),
);
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
if self.zooming {
let difference = self.mouse_pos.y as f64 - ipp.mouse.position.y as f64;
let amount = 1. + difference / MOUSE_ZOOM_DIVISOR;
let layerdata = self.layerdata_mut(&vec![]);
layerdata.scale *= amount;
responses.push_back(FrontendMessage::SetZoom { new_zoom: layerdata.scale }.into());
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
self.mouse_pos = ipp.mouse.position;
}
SetZoom(new) => {
let layerdata = self.layerdata_mut(&vec![]);
layerdata.scale = new;
responses.push_back(FrontendMessage::SetZoom { new_zoom: layerdata.scale }.into());
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
MultiplyZoom(mult) => {
let layerdata = self.layerdata_mut(&vec![]);
layerdata.scale *= mult;
responses.push_back(FrontendMessage::SetZoom { new_zoom: layerdata.scale }.into());
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
WheelZoom => {
let scroll = ipp.mouse.scroll_delta.y as f64;
let amount = if ipp.mouse.scroll_delta.y > 0 {
1. + scroll / -WHEEL_ZOOM_DIVISOR
} else {
1. / (1. + scroll / WHEEL_ZOOM_DIVISOR)
};
let layerdata = self.layerdata_mut(&vec![]);
layerdata.scale *= amount;
responses.push_back(FrontendMessage::SetZoom { new_zoom: layerdata.scale }.into());
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
WheelTranslate => {
let delta = -ipp.mouse.scroll_delta.to_dvec2();
let transformed_delta = self.active_document().document.root.transform.inverse().transform_vector2(delta);
let layerdata = self.layerdata_mut(&vec![]);
layerdata.translation += transformed_delta;
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
}
SetRotation(new) => {
let layerdata = self.layerdata_mut(&vec![]);
layerdata.rotation = new;
self.create_document_transform_from_layerdata(&ipp.viewport_size, responses);
responses.push_back(FrontendMessage::SetRotation { new_radians: new }.into());
}
NudgeSelectedLayers(x, y) => {
let paths: Vec<Vec<LayerId>> = self.selected_layers_sorted();
@@ -367,9 +491,9 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
fn actions(&self) -> ActionList {
if self.active_document().layer_data.values().any(|data| data.selected) {
actions!(DocumentMessageDiscriminant; Undo, SelectAllLayers, DeselectAllLayers, DeleteSelectedLayers, DuplicateSelectedLayers, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument, MouseMove, TranslateUp, TranslateDown, CopySelectedLayers, PasteLayers, NudgeSelectedLayers)
actions!(DocumentMessageDiscriminant; Undo, SelectAllLayers, DeselectAllLayers, DeleteSelectedLayers, DuplicateSelectedLayers, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument, MouseMove, TransformUp, TranslateDown, CopySelectedLayers, PasteLayers, NudgeSelectedLayers, RotateDown, ZoomDown, SetZoom, MultiplyZoom, SetRotation, WheelZoom, WheelTranslate)
} else {
actions!(DocumentMessageDiscriminant; Undo, SelectAllLayers, DeselectAllLayers, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument, MouseMove, TranslateUp, TranslateDown, PasteLayers)
actions!(DocumentMessageDiscriminant; Undo, SelectAllLayers, DeselectAllLayers, RenderDocument, ExportDocument, NewDocument, CloseActiveDocument, NextDocument, PrevDocument, MouseMove, TransformUp, TranslateDown, PasteLayers, RotateDown, ZoomDown, SetZoom, MultiplyZoom, SetRotation, WheelZoom, WheelTranslate)
}
}
}

View File

@@ -20,6 +20,8 @@ pub enum FrontendMessage {
DisableTextInput,
UpdateWorkingColors { primary: Color, secondary: Color },
PromptCloseConfirmationModal,
SetZoom { new_zoom: f64 },
SetRotation { new_radians: f64 },
}
pub struct FrontendMessageHandler {
@@ -46,5 +48,7 @@ impl MessageHandler<FrontendMessage, ()> for FrontendMessageHandler {
UpdateCanvas,
EnableTextInput,
DisableTextInput,
SetZoom,
SetRotation,
);
}

View File

@@ -3,7 +3,7 @@ use document_core::{layers::LayerDataTypes, LayerId};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct LayerPanelEntry {
pub name: String,
pub visible: bool,

View File

@@ -1,3 +1,4 @@
use crate::consts::{MINUS_KEY_MULTIPLIER, PLUS_KEY_MULTIPLIER};
use crate::message_prelude::*;
use crate::tool::ToolType;
@@ -13,6 +14,7 @@ const SHIFT_NUDGE_AMOUNT: f64 = 10.;
#[derive(PartialEq, Clone, Debug)]
pub enum InputMapperMessage {
PointerMove,
MouseScroll,
KeyUp(Key),
KeyDown(Key),
}
@@ -62,6 +64,7 @@ struct Mapping {
up: [KeyMappingEntries; NUMBER_OF_KEYS],
down: [KeyMappingEntries; NUMBER_OF_KEYS],
pointer_move: KeyMappingEntries,
mouse_scroll: KeyMappingEntries,
}
macro_rules! modifiers {
@@ -91,21 +94,23 @@ macro_rules! mapping {
let mut up = KeyMappingEntries::key_array();
let mut down = KeyMappingEntries::key_array();
let mut pointer_move: KeyMappingEntries = Default::default();
let mut mouse_scroll: KeyMappingEntries = Default::default();
$(
let arr = match $entry.trigger {
InputMapperMessage::KeyDown(key) => &mut down[key as usize],
InputMapperMessage::KeyUp(key) => &mut up[key as usize],
InputMapperMessage::PointerMove => &mut pointer_move,
InputMapperMessage::MouseScroll => &mut mouse_scroll,
};
arr.push($entry);
)*
(up, down, pointer_move)
(up, down, pointer_move, mouse_scroll)
}};
}
impl Default for Mapping {
fn default() -> Self {
let (up, down, pointer_move) = mapping![
let (up, down, pointer_move, mouse_scroll) = mapping![
entry! {action=DocumentMessage::PasteLayers, key_down=KeyV, modifiers=[KeyControl]},
// Select
entry! {action=SelectMessage::MouseMove, message=InputMapperMessage::PointerMove},
@@ -181,8 +186,18 @@ impl Default for Mapping {
entry! {action=DocumentMessage::ExportDocument, key_down=KeyS, modifiers=[KeyControl, KeyShift]},
entry! {action=DocumentMessage::ExportDocument, key_down=KeyE, modifiers=[KeyControl]},
entry! {action=DocumentMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=DocumentMessage::RotateDown{snap:true}, key_down=Mmb, modifiers=[KeyControl, KeyShift]},
entry! {action=DocumentMessage::RotateDown{snap:false}, key_down=Mmb, modifiers=[KeyControl]},
entry! {action=DocumentMessage::ZoomDown, key_down=Mmb, modifiers=[KeyShift]},
entry! {action=DocumentMessage::TranslateDown, key_down=Mmb},
entry! {action=DocumentMessage::TranslateUp, key_up=Mmb},
entry! {action=DocumentMessage::TransformUp, key_up=Mmb},
entry! {action=DocumentMessage::MultiplyZoom(PLUS_KEY_MULTIPLIER), key_down=KeyPlus, modifiers=[KeyControl]},
entry! {action=DocumentMessage::MultiplyZoom(PLUS_KEY_MULTIPLIER), key_down=KeyEquals, modifiers=[KeyControl]},
entry! {action=DocumentMessage::MultiplyZoom(MINUS_KEY_MULTIPLIER), key_down=KeyMinus, modifiers=[KeyControl]},
entry! {action=DocumentMessage::SetZoom(1.), key_down=Key1, modifiers=[KeyControl]},
entry! {action=DocumentMessage::SetZoom(2.), key_down=Key2, modifiers=[KeyControl]},
entry! {action=DocumentMessage::WheelZoom, message=InputMapperMessage::MouseScroll, modifiers=[KeyControl]},
entry! {action=DocumentMessage::WheelTranslate, message=InputMapperMessage::MouseScroll},
entry! {action=DocumentMessage::NewDocument, key_down=KeyN, modifiers=[KeyShift]},
entry! {action=DocumentMessage::NextDocument, key_down=KeyTab, modifiers=[KeyShift]},
entry! {action=DocumentMessage::CloseActiveDocument, key_down=KeyW, modifiers=[KeyShift]},
@@ -217,7 +232,7 @@ impl Default for Mapping {
entry! {action=GlobalMessage::LogDebug, key_down=Key2},
entry! {action=GlobalMessage::LogTrace, key_down=Key3},
];
Self { up, down, pointer_move }
Self { up, down, pointer_move, mouse_scroll }
}
}
@@ -228,6 +243,7 @@ impl Mapping {
KeyDown(key) => &self.down[key as usize],
KeyUp(key) => &self.up[key as usize],
PointerMove => &self.pointer_move,
MouseScroll => &self.mouse_scroll,
};
list.match_mapping(keys, actions)
}

View File

@@ -1,12 +1,13 @@
use std::usize;
use super::keyboard::{Key, KeyStates};
use super::mouse::{MouseKeys, MouseState, ViewportPosition};
use super::mouse::{MouseKeys, MouseState, ScrollDelta, ViewportPosition};
use crate::message_prelude::*;
use bitflags::bitflags;
#[doc(inline)]
pub use document_core::DocumentResponse;
use glam::DVec2;
#[impl_message(Message, InputPreprocessor)]
#[derive(PartialEq, Clone, Debug)]
@@ -14,8 +15,10 @@ pub enum InputPreprocessorMessage {
MouseDown(MouseState, ModifierKeys),
MouseUp(MouseState, ModifierKeys),
MouseMove(ViewportPosition, ModifierKeys),
MouseScroll(ScrollDelta, ModifierKeys),
KeyUp(Key, ModifierKeys),
KeyDown(Key, ModifierKeys),
ViewportResize(ViewportPosition),
}
bitflags! {
@@ -32,6 +35,7 @@ bitflags! {
pub struct InputPreprocessor {
pub keyboard: KeyStates,
pub mouse: MouseState,
pub viewport_size: ViewportPosition,
}
enum KeyPosition {
@@ -41,32 +45,46 @@ enum KeyPosition {
impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessor {
fn process_action(&mut self, message: InputPreprocessorMessage, _data: (), responses: &mut VecDeque<Message>) {
let response = match message {
match message {
InputPreprocessorMessage::MouseMove(pos, modifier_keys) => {
self.handle_modifier_keys(modifier_keys, responses);
self.mouse.position = pos;
InputMapperMessage::PointerMove.into()
responses.push_back(InputMapperMessage::PointerMove.into());
}
InputPreprocessorMessage::MouseScroll(delta, modifier_keys) => {
self.handle_modifier_keys(modifier_keys, responses);
self.mouse.scroll_delta = delta;
responses.push_back(InputMapperMessage::MouseScroll.into());
}
InputPreprocessorMessage::MouseDown(state, modifier_keys) => {
self.handle_modifier_keys(modifier_keys, responses);
self.translate_mouse_event(state, KeyPosition::Pressed)
responses.push_back(self.translate_mouse_event(state, KeyPosition::Pressed));
}
InputPreprocessorMessage::MouseUp(state, modifier_keys) => {
self.handle_modifier_keys(modifier_keys, responses);
self.translate_mouse_event(state, KeyPosition::Released)
responses.push_back(self.translate_mouse_event(state, KeyPosition::Released));
}
InputPreprocessorMessage::KeyDown(key, modifier_keys) => {
self.handle_modifier_keys(modifier_keys, responses);
self.keyboard.set(key as usize);
InputMapperMessage::KeyDown(key).into()
responses.push_back(InputMapperMessage::KeyDown(key).into());
}
InputPreprocessorMessage::KeyUp(key, modifier_keys) => {
self.handle_modifier_keys(modifier_keys, responses);
self.keyboard.unset(key as usize);
InputMapperMessage::KeyUp(key).into()
responses.push_back(InputMapperMessage::KeyUp(key).into());
}
InputPreprocessorMessage::ViewportResize(size) => {
responses.push_back(
document_core::Operation::TransformLayer {
path: vec![],
transform: glam::DAffine2::from_translation(DVec2::new((size.x as f64 - self.viewport_size.x as f64) / 2., (size.y as f64 - self.viewport_size.y as f64) / 2.)).to_cols_array(),
}
.into(),
);
self.viewport_size = size;
}
};
responses.push_back(response)
}
// clean user input and if possible reconstruct it
// store the changes in the keyboard if it is a key event

View File

@@ -55,6 +55,9 @@ pub enum Key {
Key8,
Key9,
KeyEnter,
KeyEquals,
KeyMinus,
KeyPlus,
KeyShift,
KeyControl,
KeyDelete,

View File

@@ -1,4 +1,5 @@
use bitflags::bitflags;
use glam::DVec2;
// origin is top left
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
@@ -13,12 +14,31 @@ impl ViewportPosition {
let y_diff = other.y as i64 - self.y as i64;
f64::sqrt((x_diff * x_diff + y_diff * y_diff) as f64)
}
pub fn to_dvec2(&self) -> DVec2 {
DVec2::new(self.x as f64, self.y as f64)
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct ScrollDelta {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl ScrollDelta {
pub fn new(x: i32, y: i32, z: i32) -> ScrollDelta {
ScrollDelta { x, y, z }
}
pub fn to_dvec2(&self) -> DVec2 {
DVec2::new(self.x as f64, self.y as f64)
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
pub struct MouseState {
pub position: ViewportPosition,
pub mouse_keys: MouseKeys,
pub scroll_delta: ScrollDelta,
}
impl MouseState {
@@ -30,11 +50,16 @@ impl MouseState {
MouseState {
position: ViewportPosition { x, y },
mouse_keys: MouseKeys::default(),
scroll_delta: ScrollDelta::default(),
}
}
pub fn from_u8_pos(keys: u8, position: ViewportPosition) -> Self {
let mouse_keys = MouseKeys::from_bits(keys).expect("invalid modifier keys");
Self { position, mouse_keys }
Self {
position,
mouse_keys,
scroll_delta: ScrollDelta::default(),
}
}
}
bitflags! {

View File

@@ -12,6 +12,8 @@ mod global;
pub mod input;
pub mod tool;
pub mod consts;
#[doc(inline)]
pub use misc::EditorError;

View File

@@ -1,6 +1,6 @@
use crate::{
input::{
mouse::{MouseKeys, MouseState, ViewportPosition},
mouse::{MouseKeys, MouseState, ScrollDelta, ViewportPosition},
InputPreprocessorMessage, ModifierKeys,
},
message_prelude::{Message, ToolMessage},
@@ -47,6 +47,7 @@ impl EditorTestUtils for Editor {
self.mouseup(MouseState {
position: ViewportPosition { x: x2, y: y2 },
mouse_keys: MouseKeys::empty(),
scroll_delta: ScrollDelta::default(),
});
}
@@ -66,6 +67,7 @@ impl EditorTestUtils for Editor {
self.mousedown(MouseState {
position: ViewportPosition { x, y },
mouse_keys: MouseKeys::LEFT,
scroll_delta: ScrollDelta::default(),
})
}

View File

@@ -2,7 +2,7 @@ use crate::input::InputPreprocessor;
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
use crate::{message_prelude::*, SvgDocument};
use document_core::{layers::style, Operation};
use glam::{DAffine2, DVec2};
use glam::DAffine2;
#[derive(Default)]
pub struct Pen {
@@ -56,7 +56,7 @@ impl Fsm for PenToolFsmState {
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
let transform = document.root.transform;
let pos = transform.inverse() * DAffine2::from_translation(DVec2::new(input.mouse.position.x as f64, input.mouse.position.y as f64));
let pos = transform.inverse() * DAffine2::from_translation(input.mouse.position.to_dvec2());
use PenMessage::*;
use PenToolFsmState::*;