mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 06:28:12 +08:00
Fix crash and clean up frontend -> backend input handling code (#1770)
This commit is contained in:
@@ -3,7 +3,6 @@ use crate::messages::prelude::*;
|
|||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
// Origin is top left
|
// Origin is top left
|
||||||
@@ -37,16 +36,35 @@ impl ViewportBounds {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct ScrollDelta {
|
pub struct ScrollDelta {
|
||||||
// TODO: Switch these to `f64` values (not trivial because floats don't provide PartialEq, Eq, and Hash)
|
pub x: f64,
|
||||||
pub x: i32,
|
pub y: f64,
|
||||||
pub y: i32,
|
pub z: f64,
|
||||||
pub z: i32,
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ScrollDelta {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.x == other.x && self.y == other.y && self.z == other.z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ScrollDelta {}
|
||||||
|
|
||||||
|
impl Hash for ScrollDelta {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
let no_negative_zero = |value: f64| if value == 0. { 0. } else { value };
|
||||||
|
|
||||||
|
no_negative_zero(self.x).to_bits().hash(state);
|
||||||
|
no_negative_zero(self.y).to_bits().hash(state);
|
||||||
|
no_negative_zero(self.z).to_bits().hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollDelta {
|
impl ScrollDelta {
|
||||||
pub fn new(x: i32, y: i32, z: i32) -> Self {
|
pub fn new(x: f64, y: f64, z: f64) -> Self {
|
||||||
Self { x, y, z }
|
Self { x, y, z }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +74,11 @@ impl ScrollDelta {
|
|||||||
|
|
||||||
pub fn scroll_delta(&self) -> f64 {
|
pub fn scroll_delta(&self) -> f64 {
|
||||||
let (dx, dy) = (self.x, self.y);
|
let (dx, dy) = (self.x, self.y);
|
||||||
dy.signum() as f64 * ((dy * dy + i32::min(dy.abs(), dx.abs()).pow(2)) as f64).sqrt()
|
dy.signum() as f64 * ((dy * dy + f64::min(dy.abs(), dx.abs()).powi(2)) as f64).sqrt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Document the difference between this and EditorMouseState
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct MouseState {
|
pub struct MouseState {
|
||||||
pub position: ViewportPosition,
|
pub position: ViewportPosition,
|
||||||
@@ -68,28 +87,6 @@ pub struct MouseState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MouseState {
|
impl MouseState {
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_position(x: f64, y: f64) -> Self {
|
|
||||||
Self {
|
|
||||||
position: (x, y).into(),
|
|
||||||
mouse_keys: MouseKeys::default(),
|
|
||||||
scroll_delta: ScrollDelta::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_keys_and_editor_position(keys: u8, position: ViewportPosition) -> Self {
|
|
||||||
let mouse_keys = MouseKeys::from_bits(keys).expect("Invalid modifier keys");
|
|
||||||
|
|
||||||
Self {
|
|
||||||
position,
|
|
||||||
mouse_keys,
|
|
||||||
scroll_delta: ScrollDelta::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish_transaction(&self, drag_start: DVec2, responses: &mut VecDeque<Message>) {
|
pub fn finish_transaction(&self, drag_start: DVec2, responses: &mut VecDeque<Message>) {
|
||||||
match drag_start.distance(self.position) <= DRAG_THRESHOLD {
|
match drag_start.distance(self.position) <= DRAG_THRESHOLD {
|
||||||
true => responses.add(DocumentMessage::AbortTransaction),
|
true => responses.add(DocumentMessage::AbortTransaction),
|
||||||
@@ -98,6 +95,7 @@ impl MouseState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Document the difference between this and MouseState
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct EditorMouseState {
|
pub struct EditorMouseState {
|
||||||
pub editor_position: EditorPosition,
|
pub editor_position: EditorPosition,
|
||||||
@@ -106,20 +104,8 @@ pub struct EditorMouseState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EditorMouseState {
|
impl EditorMouseState {
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_editor_position(x: f64, y: f64) -> Self {
|
|
||||||
Self {
|
|
||||||
editor_position: (x, y).into(),
|
|
||||||
mouse_keys: MouseKeys::default(),
|
|
||||||
scroll_delta: ScrollDelta::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_keys_and_editor_position(keys: u8, editor_position: EditorPosition) -> Self {
|
pub fn from_keys_and_editor_position(keys: u8, editor_position: EditorPosition) -> Self {
|
||||||
let mouse_keys = MouseKeys::from_bits(keys).expect("Invalid modifier keys");
|
let mouse_keys = MouseKeys::from_bits(keys).expect("Invalid decoding of MouseKeys");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
editor_position,
|
editor_position,
|
||||||
@@ -138,12 +124,16 @@ impl EditorMouseState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// Based on <https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons#value>.
|
||||||
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct MouseKeys: u8 {
|
pub struct MouseKeys: u8 {
|
||||||
const LEFT = 0b0000_0001;
|
const NONE = 0b0000_0000;
|
||||||
const RIGHT = 0b0000_0010;
|
const LEFT = 0b0000_0001;
|
||||||
const MIDDLE = 0b0000_0100;
|
const RIGHT = 0b0000_0010;
|
||||||
|
const MIDDLE = 0b0000_0100;
|
||||||
|
const BACK = 0b0000_1000;
|
||||||
|
const FORWARD = 0b0001_0000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ impl InputPreprocessorMessageHandler {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
|
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, ModifierKeys};
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::EditorMouseState;
|
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta};
|
||||||
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
@@ -189,7 +189,11 @@ mod test {
|
|||||||
fn process_action_mouse_move_handle_modifier_keys() {
|
fn process_action_mouse_move_handle_modifier_keys() {
|
||||||
let mut input_preprocessor = InputPreprocessorMessageHandler::default();
|
let mut input_preprocessor = InputPreprocessorMessageHandler::default();
|
||||||
|
|
||||||
let editor_mouse_state = EditorMouseState::from_editor_position(4., 809.);
|
let editor_mouse_state = EditorMouseState {
|
||||||
|
editor_position: (4., 809.).into(),
|
||||||
|
mouse_keys: MouseKeys::default(),
|
||||||
|
scroll_delta: ScrollDelta::default(),
|
||||||
|
};
|
||||||
let modifier_keys = ModifierKeys::ALT;
|
let modifier_keys = ModifierKeys::ALT;
|
||||||
let message = InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys };
|
let message = InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys };
|
||||||
|
|
||||||
@@ -208,7 +212,7 @@ mod test {
|
|||||||
fn process_action_mouse_down_handle_modifier_keys() {
|
fn process_action_mouse_down_handle_modifier_keys() {
|
||||||
let mut input_preprocessor = InputPreprocessorMessageHandler::default();
|
let mut input_preprocessor = InputPreprocessorMessageHandler::default();
|
||||||
|
|
||||||
let editor_mouse_state = EditorMouseState::new();
|
let editor_mouse_state = EditorMouseState::default();
|
||||||
let modifier_keys = ModifierKeys::CONTROL;
|
let modifier_keys = ModifierKeys::CONTROL;
|
||||||
let message = InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys };
|
let message = InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys };
|
||||||
|
|
||||||
@@ -227,7 +231,7 @@ mod test {
|
|||||||
fn process_action_mouse_up_handle_modifier_keys() {
|
fn process_action_mouse_up_handle_modifier_keys() {
|
||||||
let mut input_preprocessor = InputPreprocessorMessageHandler::default();
|
let mut input_preprocessor = InputPreprocessorMessageHandler::default();
|
||||||
|
|
||||||
let editor_mouse_state = EditorMouseState::new();
|
let editor_mouse_state = EditorMouseState::default();
|
||||||
let modifier_keys = ModifierKeys::SHIFT;
|
let modifier_keys = ModifierKeys::SHIFT;
|
||||||
let message = InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys };
|
let message = InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys };
|
||||||
|
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||||||
NavigationMessage::CanvasZoomMouseWheel => {
|
NavigationMessage::CanvasZoomMouseWheel => {
|
||||||
let scroll = ipp.mouse.scroll_delta.scroll_delta();
|
let scroll = ipp.mouse.scroll_delta.scroll_delta();
|
||||||
let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE;
|
let mut zoom_factor = 1. + scroll.abs() * VIEWPORT_ZOOM_WHEEL_RATE;
|
||||||
if ipp.mouse.scroll_delta.y > 0 {
|
if ipp.mouse.scroll_delta.y > 0. {
|
||||||
zoom_factor = 1. / zoom_factor
|
zoom_factor = 1. / zoom_factor
|
||||||
}
|
}
|
||||||
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
zoom_factor *= Self::clamp_zoom(ptz.zoom * zoom_factor, document_bounds, old_zoom, ipp);
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ impl EditorTestUtils for Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn move_mouse(&mut self, x: f64, y: f64) {
|
fn move_mouse(&mut self, x: f64, y: f64) {
|
||||||
let mut editor_mouse_state = EditorMouseState::new();
|
let mut editor_mouse_state = EditorMouseState::default();
|
||||||
editor_mouse_state.editor_position = ViewportPosition::new(x, y);
|
editor_mouse_state.editor_position = ViewportPosition::new(x, y);
|
||||||
let modifier_keys = ModifierKeys::default();
|
let modifier_keys = ModifierKeys::default();
|
||||||
self.input(InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys });
|
self.input(InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys });
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ impl EditorHandle {
|
|||||||
|
|
||||||
/// Mouse scrolling within the screenspace bounds of the viewport
|
/// Mouse scrolling within the screenspace bounds of the viewport
|
||||||
#[wasm_bindgen(js_name = onWheelScroll)]
|
#[wasm_bindgen(js_name = onWheelScroll)]
|
||||||
pub fn on_wheel_scroll(&self, x: f64, y: f64, mouse_keys: u8, wheel_delta_x: i32, wheel_delta_y: i32, wheel_delta_z: i32, modifiers: u8) {
|
pub fn on_wheel_scroll(&self, x: f64, y: f64, mouse_keys: u8, wheel_delta_x: f64, wheel_delta_y: f64, wheel_delta_z: f64, modifiers: u8) {
|
||||||
let mut editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
let mut editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
||||||
editor_mouse_state.scroll_delta = ScrollDelta::new(wheel_delta_x, wheel_delta_y, wheel_delta_z);
|
editor_mouse_state.scroll_delta = ScrollDelta::new(wheel_delta_x, wheel_delta_y, wheel_delta_z);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user