diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index 6b2d9838c8..bf655a4d91 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -45,6 +45,7 @@ impl Default for Mapping { entry! {action=SelectToolMessage::PointerMove { axis_align: KeyShift, snap_angle: KeyControl, center: KeyAlt }, message=InputMapperMessage::PointerMove}, entry! {action=SelectToolMessage::DragStart { add_to_selection: KeyShift }, key_down=Lmb}, entry! {action=SelectToolMessage::DragStop, key_up=Lmb}, + entry! {action=SelectToolMessage::DragStop, key_down=KeyEnter}, entry! {action=SelectToolMessage::EditLayer, message=InputMapperMessage::DoubleClick}, entry! {action=SelectToolMessage::Abort, key_down=Rmb}, entry! {action=SelectToolMessage::Abort, key_down=KeyEscape}, diff --git a/editor/src/input/input_preprocessor_message_handler.rs b/editor/src/input/input_preprocessor_message_handler.rs index 61811a1552..8d18412268 100644 --- a/editor/src/input/input_preprocessor_message_handler.rs +++ b/editor/src/input/input_preprocessor_message_handler.rs @@ -1,4 +1,4 @@ -use super::input_preprocessor::{KeyPosition, ModifierKeys}; +use super::input_preprocessor::ModifierKeys; use super::keyboard::{Key, KeyStates}; use super::mouse::{MouseKeys, MouseState, ViewportBounds}; use crate::message_prelude::*; @@ -83,9 +83,7 @@ impl MessageHandler for InputPreprocessorMessageHa let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); self.mouse.position = mouse_state.position; - if let Some(message) = self.translate_mouse_event(mouse_state, KeyPosition::Pressed) { - responses.push_back(message); - } + self.translate_mouse_event(mouse_state, responses); } InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => { self.handle_modifier_keys(modifier_keys, responses); @@ -94,6 +92,9 @@ impl MessageHandler for InputPreprocessorMessageHa self.mouse.position = mouse_state.position; responses.push_back(InputMapperMessage::PointerMove.into()); + + // While any pointer button is already down, additional button down events are not reported, but they are sent as `pointermove` events + self.translate_mouse_event(mouse_state, responses); } InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => { self.handle_modifier_keys(modifier_keys, responses); @@ -101,9 +102,7 @@ impl MessageHandler for InputPreprocessorMessageHa let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); self.mouse.position = mouse_state.position; - if let Some(message) = self.translate_mouse_event(mouse_state, KeyPosition::Released) { - responses.push_back(message); - } + self.translate_mouse_event(mouse_state, responses); } }; } @@ -115,27 +114,20 @@ impl MessageHandler for InputPreprocessorMessageHa } impl InputPreprocessorMessageHandler { - fn translate_mouse_event(&mut self, new_state: MouseState, position: KeyPosition) -> Option { - // Calculate the difference between the two key states (binary xor) - let difference = self.mouse.mouse_keys ^ new_state.mouse_keys; - - self.mouse = new_state; - - let key = match difference { - MouseKeys::LEFT => Key::Lmb, - MouseKeys::RIGHT => Key::Rmb, - MouseKeys::MIDDLE => Key::Mmb, - MouseKeys::NONE => return None, // self.mouse.mouse_keys was invalid, e.g. when a drag began outside the client - _ => { - log::warn!("The number of buttons modified at the same time was greater than 1. Modification: {:#010b}", difference); - Key::UnknownKey + fn translate_mouse_event(&mut self, new_state: MouseState, responses: &mut VecDeque) { + for (bit_flag, key) in [(MouseKeys::LEFT, Key::Lmb), (MouseKeys::RIGHT, Key::Rmb), (MouseKeys::MIDDLE, Key::Mmb)] { + // Calculate the intersection between the two key states + let old_down = self.mouse.mouse_keys & bit_flag == bit_flag; + let new_down = new_state.mouse_keys & bit_flag == bit_flag; + if !old_down && new_down { + responses.push_back(InputMapperMessage::KeyDown(key).into()); } - }; + if old_down && !new_down { + responses.push_back(InputMapperMessage::KeyUp(key).into()); + } + } - Some(match position { - KeyPosition::Pressed => InputMapperMessage::KeyDown(key).into(), - KeyPosition::Released => InputMapperMessage::KeyUp(key).into(), - }) + self.mouse = new_state; } fn handle_modifier_keys(&mut self, modifier_keys: ModifierKeys, responses: &mut VecDeque) { diff --git a/editor/src/viewport_tools/tools/select_tool.rs b/editor/src/viewport_tools/tools/select_tool.rs index a8fa401771..ceb6f59818 100644 --- a/editor/src/viewport_tools/tools/select_tool.rs +++ b/editor/src/viewport_tools/tools/select_tool.rs @@ -254,7 +254,6 @@ impl<'a> MessageHandler> for SelectTool { match self.fsm_state { Ready => actions!(SelectToolMessageDiscriminant; DragStart, PointerMove, EditLayer), - Dragging => actions!(SelectToolMessageDiscriminant; DragStop, PointerMove, EditLayer), _ => actions!(SelectToolMessageDiscriminant; DragStop, PointerMove, Abort, EditLayer), } } @@ -588,11 +587,27 @@ impl Fsm for SelectToolFsmState { ); Ready } + (Dragging, Abort) => { + data.snap_handler.cleanup(responses); + responses.push_back(DocumentMessage::Undo.into()); + Ready + } (_, Abort) => { if let Some(path) = data.drag_box_overlay_layer.take() { responses.push_front(DocumentMessage::Overlays(Operation::DeleteLayer { path }.into()).into()) }; - if let Some(bounding_box_overlays) = data.bounding_box_overlays.take() { + if let Some(mut bounding_box_overlays) = data.bounding_box_overlays.take() { + let selected = data.layers_dragging.iter().collect::>(); + let mut selected = Selected::new( + &mut bounding_box_overlays.original_transforms, + &mut bounding_box_overlays.pivot, + &selected, + responses, + &document.graphene_document, + ); + + selected.revert_operation(); + bounding_box_overlays.delete(responses); } diff --git a/editor/src/viewport_tools/tools/shared/transformation_cage.rs b/editor/src/viewport_tools/tools/shared/transformation_cage.rs index 490757a21b..bf49faa2b4 100644 --- a/editor/src/viewport_tools/tools/shared/transformation_cage.rs +++ b/editor/src/viewport_tools/tools/shared/transformation_cage.rs @@ -8,7 +8,7 @@ use graphene::color::Color; use graphene::layers::style::{self, Fill, Stroke}; use graphene::Operation; -use glam::{DAffine2, DVec2, Vec2Swizzles}; +use glam::{DAffine2, DVec2}; /// Contains the edges that are being dragged along with the origional bounds #[derive(Clone, Debug, Default)] @@ -18,11 +18,22 @@ pub struct SelectedEdges { bottom: bool, left: bool, right: bool, + // Aspect ratio in the form of width/height, so x:1 = width:height + aspect_ratio: f64, } impl SelectedEdges { pub fn new(top: bool, bottom: bool, left: bool, right: bool, bounds: [DVec2; 2]) -> Self { - Self { top, bottom, left, right, bounds } + let size = (bounds[0] - bounds[1]).abs(); + let aspect_ratio = size.x / size.y; + Self { + top, + bottom, + left, + right, + bounds, + aspect_ratio, + } } /// Calculate the pivot for the operation (the opposite point to the edge dragged) @@ -67,8 +78,13 @@ impl SelectedEdges { } let mut size = max - min; - if constrain && ((self.top || self.bottom) && (self.left || self.right)) { - size = size.abs().max(size.abs().yx()) * size.signum(); + if constrain { + size = match ((self.top || self.bottom), (self.left || self.right)) { + (true, true) => DVec2::new(size.x, size.x / self.aspect_ratio).abs().max(DVec2::new(size.y * self.aspect_ratio, size.y).abs()) * size.signum(), + (true, false) => DVec2::new(size.y * self.aspect_ratio, size.y), + (false, true) => DVec2::new(size.x, size.x / self.aspect_ratio), + _ => size, + }; } if center { if self.left || self.right { diff --git a/frontend/src/lifetime/input.ts b/frontend/src/lifetime/input.ts index 1ad12e0f1a..676cf72c48 100644 --- a/frontend/src/lifetime/input.ts +++ b/frontend/src/lifetime/input.ts @@ -104,6 +104,7 @@ export function createInputManager(editor: EditorState, container: HTMLElement, // Pointer events + // While any pointer button is already down, additional button down events are not reported, but they are sent as `pointermove` events and these are handled in the backend const onPointerMove = (e: PointerEvent): void => { if (!e.buttons) viewportPointerInteractionOngoing = false;