mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00:00
Improvements to the layer transform cage UX (#589)
* Allow input system to handle mousedown while dragging * Fix abort * Add apsect ratio * Make comment more explicit * Fix abort when dragging * Constrain when dragging edge
This commit is contained in:
parent
a26679b96c
commit
7c8f488ad0
5 changed files with 57 additions and 32 deletions
|
@ -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},
|
||||
|
|
|
@ -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<InputPreprocessorMessage, ()> 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<InputPreprocessorMessage, ()> 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<InputPreprocessorMessage, ()> 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<InputPreprocessorMessage, ()> for InputPreprocessorMessageHa
|
|||
}
|
||||
|
||||
impl InputPreprocessorMessageHandler {
|
||||
fn translate_mouse_event(&mut self, new_state: MouseState, position: KeyPosition) -> Option<Message> {
|
||||
// Calculate the difference between the two key states (binary xor)
|
||||
let difference = self.mouse.mouse_keys ^ new_state.mouse_keys;
|
||||
fn translate_mouse_event(&mut self, new_state: MouseState, responses: &mut VecDeque<Message>) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
Some(match position {
|
||||
KeyPosition::Pressed => InputMapperMessage::KeyDown(key).into(),
|
||||
KeyPosition::Released => InputMapperMessage::KeyUp(key).into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_modifier_keys(&mut self, modifier_keys: ModifierKeys, responses: &mut VecDeque<Message>) {
|
||||
|
|
|
@ -254,7 +254,6 @@ impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> 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::<Vec<_>>();
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue