mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 18:38:05 +08:00
Major overhaul of input and communication systems
* Add input manager * WIP lifetime hell * Hell yeah, dark lifetime magic * Replace events with actions in tools * Fix borrow in GlobalEventHandler * Fix typo in response-handler * Distribute dispatch structure * Add translation from events to actions * Port default key actions to input_mapper * Split actions macro * Add handling for Ambiguous Mouse events * Fix warnings and clippy lints * Add actions macro * WIP rework * Add AsMessage macro * Add implementation for derived enums * Add macro implementation for top level message * Add #[child] attribute to indicate derivation * Replace some mentions of Actions and Responses with Message * It compiles !!! * Add functionality to some message handlers * Add document rendering * ICE * Rework the previous code while keeping basic functionality * Reduce parent-top-level macro args to only two * Add workaround for ICE * Fix cyclic reference in document.rs * Make derive_transitive_child a bit more powerful This addresses the todo that was left, enabling arbitrary expressions to be passed as the last parameter of a #[parent] attribute * Adapt frontend message format * Make responses use VecDeque Our responses are a queue so we should use a queue type for them * Move traits to sensible location * Are we rectangle yet? * Simplify, improve & document `derive_discriminant` * Change `child` to `sub_discriminant` This only applies to `ToDiscriminant`. Code using `#[impl_message]` continues to work. * Add docs for `derive_transitive_child` * Finish docs and improve macros The improvements are that impl_message now uses trait resolution to obtain the parent's discriminant and that derive_as_message now allows for non-unit variants (which we don't use but it's nice to have, just in case) * Remove logging call * Move files around and cleanup structure * Fix proc macro doc tests * Improve actions_fn!() macro * Add ellipse tool * Pass populated actions list to the input mapper * Add KeyState bitvector * Merge mouse buttons into "keyboard" * Add macro for initialization of key mapper table * Add syntactic sugar for the macro * Implement mapping function * Translate the remaining tools * Fix shape tool * Add keybindings for line and pen tool * Fix modifiers * Cleanup * Add doc comments for the actions macro * Fix formatting * Rename MouseMove to PointerMove * Add keybinds for tools * Apply review suggestions * Rename KeyMappings -> KeyMappingEntries * Apply review changes Co-authored-by: T0mstone <realt0mstone@gmail.com> Co-authored-by: Paul Kupper <kupper.pa@gmail.com>
This commit is contained in:
committed by
Keavon Chambers
parent
c08b2d4b31
commit
4b19a459b7
191
core/editor/src/input/input_mapper.rs
Normal file
191
core/editor/src/input/input_mapper.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use crate::message_prelude::*;
|
||||
use crate::tool::ToolType;
|
||||
|
||||
use super::{
|
||||
keyboard::{Key, KeyStates, NUMBER_OF_KEYS},
|
||||
InputPreprocessor,
|
||||
};
|
||||
|
||||
#[impl_message(Message, InputMapper)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum InputMapperMessage {
|
||||
PointerMove,
|
||||
KeyUp(Key),
|
||||
KeyDown(Key),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
struct MappingEntry {
|
||||
trigger: InputMapperMessage,
|
||||
modifiers: KeyStates,
|
||||
action: Message,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct KeyMappingEntries(Vec<MappingEntry>);
|
||||
|
||||
impl KeyMappingEntries {
|
||||
fn match_mapping(&self, keys: &KeyStates, actions: ActionList) -> Option<Message> {
|
||||
for entry in self.0.iter() {
|
||||
let all_required_modifiers_pressed = ((*keys & entry.modifiers) ^ entry.modifiers).is_empty();
|
||||
if all_required_modifiers_pressed && actions.iter().flatten().any(|action| entry.action.to_discriminant() == *action) {
|
||||
return Some(entry.action.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn push(&mut self, entry: MappingEntry) {
|
||||
self.0.push(entry)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Mapping {
|
||||
up: [KeyMappingEntries; NUMBER_OF_KEYS],
|
||||
down: [KeyMappingEntries; NUMBER_OF_KEYS],
|
||||
pointer_move: KeyMappingEntries,
|
||||
}
|
||||
|
||||
macro_rules! modifiers {
|
||||
($($m:ident),*) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut state = KeyStates::new();
|
||||
$(
|
||||
state.set(Key::$m as usize);
|
||||
),*
|
||||
state
|
||||
}};
|
||||
}
|
||||
macro_rules! entry {
|
||||
{action=$action:expr, key_down=$key:ident $(, modifiers=[$($m:ident),* $(,)?])?} => {{
|
||||
entry!{action=$action, message=InputMapperMessage::KeyDown(Key::$key) $(, modifiers=[$($m),*])?}
|
||||
}};
|
||||
{action=$action:expr, key_up=$key:ident $(, modifiers=[$($m:ident),* $(,)?])?} => {{
|
||||
entry!{action=$action, message=InputMapperMessage::KeyUp(Key::$key) $(, modifiers=[$($m),* ])?}
|
||||
}};
|
||||
{action=$action:expr, message=$message:expr $(, modifiers=[$($m:ident),* $(,)?])?} => {{
|
||||
MappingEntry {trigger: $message, modifiers: modifiers!($($($m),*)?), action: $action.into()}
|
||||
}};
|
||||
}
|
||||
macro_rules! mapping {
|
||||
//[$(<action=$action:expr; message=$key:expr; $(modifiers=[$($m:ident),* $(,)?];)?>)*] => {{
|
||||
[$($entry:expr),* $(,)?] => {{
|
||||
let mut up: [KeyMappingEntries; NUMBER_OF_KEYS] = Default::default();
|
||||
let mut down: [KeyMappingEntries; NUMBER_OF_KEYS] = Default::default();
|
||||
let mut pointer_move: 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,
|
||||
};
|
||||
arr.push($entry);
|
||||
)*
|
||||
(up, down, pointer_move)
|
||||
}};
|
||||
}
|
||||
|
||||
impl Default for Mapping {
|
||||
fn default() -> Self {
|
||||
let (up, down, pointer_move) = mapping![
|
||||
// Rectangle
|
||||
entry! {action=RectangleMessage::Center, key_down=KeyAlt},
|
||||
entry! {action=RectangleMessage::UnCenter, key_up=KeyAlt},
|
||||
entry! {action=RectangleMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=RectangleMessage::DragStart, key_down=Lmb},
|
||||
entry! {action=RectangleMessage::DragStop, key_up=Lmb},
|
||||
entry! {action=RectangleMessage::Abort, key_down=Rmb},
|
||||
entry! {action=RectangleMessage::Abort, key_down=KeyEscape},
|
||||
entry! {action=RectangleMessage::LockAspectRatio, key_down=KeyShift},
|
||||
entry! {action=RectangleMessage::UnlockAspectRatio, key_up=KeyShift},
|
||||
entry! {action=RectangleMessage::LockAspectRatio, key_down=KeyCaps},
|
||||
entry! {action=RectangleMessage::UnlockAspectRatio, key_up=KeyCaps},
|
||||
// Ellipse
|
||||
entry! {action=EllipseMessage::Center, key_down=KeyAlt},
|
||||
entry! {action=EllipseMessage::UnCenter, key_up=KeyAlt},
|
||||
entry! {action=EllipseMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=EllipseMessage::DragStart, key_down=Lmb},
|
||||
entry! {action=EllipseMessage::DragStop, key_up=Lmb},
|
||||
entry! {action=EllipseMessage::Abort, key_down=Rmb},
|
||||
entry! {action=EllipseMessage::Abort, key_down=KeyEscape},
|
||||
entry! {action=EllipseMessage::LockAspectRatio, key_down=KeyShift},
|
||||
entry! {action=EllipseMessage::UnlockAspectRatio, key_up=KeyShift},
|
||||
entry! {action=EllipseMessage::LockAspectRatio, key_down=KeyCaps},
|
||||
entry! {action=EllipseMessage::UnlockAspectRatio, key_up=KeyCaps},
|
||||
// Shape
|
||||
entry! {action=ShapeMessage::Center, key_down=KeyAlt},
|
||||
entry! {action=ShapeMessage::UnCenter, key_up=KeyAlt},
|
||||
entry! {action=ShapeMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=ShapeMessage::DragStart, key_down=Lmb},
|
||||
entry! {action=ShapeMessage::DragStop, key_up=Lmb},
|
||||
entry! {action=ShapeMessage::Abort, key_down=Rmb},
|
||||
entry! {action=ShapeMessage::Abort, key_down=KeyEscape},
|
||||
entry! {action=ShapeMessage::LockAspectRatio, key_down=KeyShift},
|
||||
entry! {action=ShapeMessage::UnlockAspectRatio, key_up=KeyShift},
|
||||
entry! {action=ShapeMessage::LockAspectRatio, key_down=KeyCaps},
|
||||
entry! {action=ShapeMessage::UnlockAspectRatio, key_up=KeyCaps},
|
||||
// Line
|
||||
entry! {action=LineMessage::Center, key_down=KeyAlt},
|
||||
entry! {action=LineMessage::UnCenter, key_up=KeyAlt},
|
||||
entry! {action=LineMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=LineMessage::DragStart, key_down=Lmb},
|
||||
entry! {action=LineMessage::DragStop, key_up=Lmb},
|
||||
entry! {action=LineMessage::Abort, key_down=Rmb},
|
||||
entry! {action=LineMessage::Abort, key_down=KeyEscape},
|
||||
entry! {action=LineMessage::LockAngle, key_down=KeyControl},
|
||||
entry! {action=LineMessage::UnlockAngle, key_up=KeyControl},
|
||||
entry! {action=LineMessage::SnapToAngle, key_down=KeyShift},
|
||||
entry! {action=LineMessage::UnSnapToAngle, key_up=KeyShift},
|
||||
entry! {action=LineMessage::SnapToAngle, key_down=KeyCaps},
|
||||
entry! {action=LineMessage::UnSnapToAngle, key_up=KeyCaps},
|
||||
// Pen
|
||||
entry! {action=PenMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=PenMessage::DragStart, key_down=Lmb},
|
||||
entry! {action=PenMessage::DragStop, key_up=Lmb},
|
||||
entry! {action=PenMessage::Confirm, key_down=Rmb},
|
||||
entry! {action=PenMessage::Confirm, key_down=KeyEscape},
|
||||
entry! {action=PenMessage::Confirm, key_down=KeyEnter},
|
||||
// Document Actions
|
||||
entry! {action=DocumentMessage::Undo, key_down=KeyZ, modifiers=[KeyControl]},
|
||||
// Tool Actions
|
||||
entry! {action=ToolMessage::SelectTool(ToolType::Rectangle), key_down=KeyM},
|
||||
entry! {action=ToolMessage::SelectTool(ToolType::Ellipse), key_down=KeyE},
|
||||
entry! {action=ToolMessage::SelectTool(ToolType::Select), key_down=KeyV},
|
||||
entry! {action=ToolMessage::SelectTool(ToolType::Line), key_down=KeyL},
|
||||
entry! {action=ToolMessage::SelectTool(ToolType::Shape), key_down=KeyY},
|
||||
entry! {action=ToolMessage::SwapColors, key_down=KeyX, modifiers=[KeyShift]},
|
||||
// Global Actions
|
||||
entry! {action=GlobalMessage::LogInfo, key_down=Key1},
|
||||
entry! {action=GlobalMessage::LogDebug, key_down=Key2},
|
||||
entry! {action=GlobalMessage::LogTrace, key_down=Key3},
|
||||
];
|
||||
Self { up, down, pointer_move }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mapping {
|
||||
fn match_message(&self, message: InputMapperMessage, keys: &KeyStates, actions: ActionList) -> Option<Message> {
|
||||
use InputMapperMessage::*;
|
||||
let list = match message {
|
||||
KeyDown(key) => &self.down[key as usize],
|
||||
KeyUp(key) => &self.up[key as usize],
|
||||
PointerMove => &self.pointer_move,
|
||||
};
|
||||
list.match_mapping(keys, actions)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InputMapper {
|
||||
mapping: Mapping,
|
||||
}
|
||||
|
||||
impl MessageHandler<InputMapperMessage, (&InputPreprocessor, ActionList)> for InputMapper {
|
||||
fn process_action(&mut self, message: InputMapperMessage, data: (&InputPreprocessor, ActionList), responses: &mut VecDeque<Message>) {
|
||||
let (input, actions) = data;
|
||||
if let Some(message) = self.mapping.match_message(message, &input.keyboard, actions) {
|
||||
responses.push_back(message);
|
||||
}
|
||||
}
|
||||
advertise_actions!();
|
||||
}
|
||||
74
core/editor/src/input/input_preprocessor.rs
Normal file
74
core/editor/src/input/input_preprocessor.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use super::keyboard::{Key, KeyStates};
|
||||
use super::mouse::{MouseKeys, MouseState, ViewportPosition};
|
||||
use crate::message_prelude::*;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use document_core::DocumentResponse;
|
||||
|
||||
#[impl_message(Message, InputPreprocessor)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum InputPreprocessorMessage {
|
||||
MouseDown(MouseState),
|
||||
MouseUp(MouseState),
|
||||
MouseMove(ViewportPosition),
|
||||
KeyUp(Key),
|
||||
KeyDown(Key),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InputPreprocessor {
|
||||
pub keyboard: KeyStates,
|
||||
pub mouse: MouseState,
|
||||
}
|
||||
|
||||
enum KeyPosition {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessor {
|
||||
fn process_action(&mut self, message: InputPreprocessorMessage, _data: (), responses: &mut VecDeque<Message>) {
|
||||
let response = match message {
|
||||
InputPreprocessorMessage::MouseMove(pos) => {
|
||||
self.mouse.position = pos;
|
||||
InputMapperMessage::PointerMove.into()
|
||||
}
|
||||
InputPreprocessorMessage::MouseDown(state) => self.translate_mouse_event(state, KeyPosition::Pressed),
|
||||
InputPreprocessorMessage::MouseUp(state) => self.translate_mouse_event(state, KeyPosition::Released),
|
||||
InputPreprocessorMessage::KeyDown(key) => {
|
||||
self.keyboard.set(key as usize);
|
||||
InputMapperMessage::KeyDown(key).into()
|
||||
}
|
||||
InputPreprocessorMessage::KeyUp(key) => {
|
||||
self.keyboard.unset(key as usize);
|
||||
InputMapperMessage::KeyUp(key).into()
|
||||
}
|
||||
};
|
||||
responses.push_back(response)
|
||||
}
|
||||
// clean user input and if possible reconstruct it
|
||||
// store the changes in the keyboard if it is a key event
|
||||
// transform canvas coordinates to document coordinates
|
||||
advertise_actions!();
|
||||
}
|
||||
|
||||
impl InputPreprocessor {
|
||||
fn translate_mouse_event(&mut self, new_state: MouseState, position: KeyPosition) -> Message {
|
||||
// Calculate the difference between the two key states (binary xor)
|
||||
let diff = self.mouse.mouse_keys ^ new_state.mouse_keys;
|
||||
self.mouse = new_state;
|
||||
let key = match diff {
|
||||
MouseKeys::LEFT => Key::Lmb,
|
||||
MouseKeys::RIGHT => Key::Rmb,
|
||||
MouseKeys::MIDDLE => Key::Mmb,
|
||||
_ => {
|
||||
log::warn!("The number of buttons modified at the same time was not equal to 1. Modification: {:#010b}", diff);
|
||||
Key::UnknownKey
|
||||
}
|
||||
};
|
||||
match position {
|
||||
KeyPosition::Pressed => InputMapperMessage::KeyDown(key).into(),
|
||||
KeyPosition::Released => InputMapperMessage::KeyUp(key).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
143
core/editor/src/input/keyboard.rs
Normal file
143
core/editor/src/input/keyboard.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
|
||||
// Edit this to specify the storage type used
|
||||
// TODO: Increase size of type
|
||||
pub type StorageType = u8;
|
||||
const STORAGE_SIZE: u32 = std::mem::size_of::<usize>() as u32 * 8 + 2 - std::mem::size_of::<StorageType>().leading_zeros();
|
||||
const STORAGE_SIZE_BITS: usize = 1 << STORAGE_SIZE;
|
||||
const KEY_MASK_STORAGE_LENGTH: usize = (NUMBER_OF_KEYS + STORAGE_SIZE_BITS - 1) >> STORAGE_SIZE;
|
||||
pub type KeyStates = BitVector<KEY_MASK_STORAGE_LENGTH>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Key {
|
||||
UnknownKey,
|
||||
// MouseKeys
|
||||
Lmb,
|
||||
Rmb,
|
||||
Mmb,
|
||||
|
||||
// Keyboard keys
|
||||
KeyR,
|
||||
KeyM,
|
||||
KeyE,
|
||||
KeyL,
|
||||
KeyP,
|
||||
KeyV,
|
||||
KeyX,
|
||||
KeyZ,
|
||||
KeyY,
|
||||
KeyEnter,
|
||||
Key0,
|
||||
Key1,
|
||||
Key2,
|
||||
Key3,
|
||||
Key4,
|
||||
Key5,
|
||||
Key6,
|
||||
Key7,
|
||||
Key8,
|
||||
Key9,
|
||||
KeyShift,
|
||||
KeyCaps,
|
||||
KeyControl,
|
||||
KeyAlt,
|
||||
KeyEscape,
|
||||
|
||||
// This has to be the last element in the enum.
|
||||
NumKeys,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct BitVector<const LENGTH: usize>([StorageType; LENGTH]);
|
||||
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign},
|
||||
usize,
|
||||
};
|
||||
|
||||
impl<const LENGTH: usize> BitVector<LENGTH> {
|
||||
#[inline]
|
||||
fn convert_index(bitvector_index: usize) -> (usize, StorageType) {
|
||||
let bit = 1 << (bitvector_index & (STORAGE_SIZE_BITS as StorageType - 1) as usize);
|
||||
let offset = bitvector_index >> STORAGE_SIZE;
|
||||
(offset, bit)
|
||||
}
|
||||
pub const fn new() -> Self {
|
||||
Self([0; LENGTH])
|
||||
}
|
||||
pub fn set(&mut self, bitvector_index: usize) {
|
||||
let (offset, bit) = Self::convert_index(bitvector_index);
|
||||
self.0[offset] |= bit;
|
||||
}
|
||||
pub fn unset(&mut self, bitvector_index: usize) {
|
||||
let (offset, bit) = Self::convert_index(bitvector_index);
|
||||
self.0[offset] &= !bit;
|
||||
}
|
||||
pub fn toggle(&mut self, bitvector_index: usize) {
|
||||
let (offset, bit) = Self::convert_index(bitvector_index);
|
||||
self.0[offset] ^= bit;
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let mut result = 0;
|
||||
for storage in self.0.iter() {
|
||||
result |= storage;
|
||||
}
|
||||
result == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize> Default for BitVector<LENGTH> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LENGTH: usize> Display for BitVector<LENGTH> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
for storage in self.0.iter().rev() {
|
||||
write!(f, "{:0width$b}", storage, width = STORAGE_SIZE_BITS)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! bit_ops {
|
||||
($(($op:ident, $func:ident)),* $(,)?) => {
|
||||
$(
|
||||
impl<const LENGTH: usize> $op for BitVector<LENGTH> {
|
||||
type Output = Self;
|
||||
fn $func(self, right: Self) -> Self::Output {
|
||||
let mut result = Self::new();
|
||||
for ((left, right), new) in self.0.iter().zip(right.0.iter()).zip(result.0.iter_mut()) {
|
||||
*new = $op::$func(left, right);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
impl<const LENGTH: usize> $op for &BitVector<LENGTH> {
|
||||
type Output = BitVector<LENGTH>;
|
||||
fn $func(self, right: Self) -> Self::Output {
|
||||
let mut result = BitVector::<LENGTH>::new();
|
||||
for ((left, right), new) in self.0.iter().zip(right.0.iter()).zip(result.0.iter_mut()) {
|
||||
*new = $op::$func(left, right);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
macro_rules! bit_ops_assign {
|
||||
($(($op:ident, $func:ident)),* $(,)?) => {
|
||||
$(impl<const LENGTH: usize> $op for BitVector<LENGTH> {
|
||||
fn $func(&mut self, right: Self) {
|
||||
for (left, right) in self.0.iter_mut().zip(right.0.iter()) {
|
||||
$op::$func(left, right);
|
||||
}
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
bit_ops!((BitAnd, bitand), (BitOr, bitor), (BitXor, bitxor));
|
||||
bit_ops_assign!((BitAndAssign, bitand_assign), (BitOrAssign, bitor_assign), (BitXorAssign, bitxor_assign));
|
||||
9
core/editor/src/input/mod.rs
Normal file
9
core/editor/src/input/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod input_mapper;
|
||||
pub mod input_preprocessor;
|
||||
pub mod keyboard;
|
||||
pub mod mouse;
|
||||
|
||||
pub use {
|
||||
input_mapper::{InputMapper, InputMapperMessage, InputMapperMessageDiscriminant},
|
||||
input_preprocessor::{InputPreprocessor, InputPreprocessorMessage, InputPreprocessorMessageDiscriminant},
|
||||
};
|
||||
48
core/editor/src/input/mouse.rs
Normal file
48
core/editor/src/input/mouse.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
// origin is top left
|
||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
|
||||
pub struct ViewportPosition {
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
}
|
||||
|
||||
impl ViewportPosition {
|
||||
pub fn distance(&self, other: &Self) -> f64 {
|
||||
let x_diff = other.x as i64 - self.x as i64;
|
||||
let y_diff = other.y as i64 - self.y as i64;
|
||||
f64::sqrt((x_diff * x_diff + y_diff * y_diff) as f64)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
|
||||
pub struct MouseState {
|
||||
pub position: ViewportPosition,
|
||||
pub mouse_keys: MouseKeys,
|
||||
}
|
||||
|
||||
impl MouseState {
|
||||
pub fn new() -> MouseState {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn from_pos(x: u32, y: u32) -> MouseState {
|
||||
MouseState {
|
||||
position: ViewportPosition { x, y },
|
||||
mouse_keys: MouseKeys::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 }
|
||||
}
|
||||
}
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct MouseKeys: u8 {
|
||||
const LEFT = 0b0000_0001;
|
||||
const RIGHT = 0b0000_0010;
|
||||
const MIDDLE = 0b0000_0100;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user