#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MouseButton {
Left,
Right,
Middle,
Other(u16),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Modifiers {
pub ctrl: bool,
pub alt: bool,
pub shift: bool,
pub meta: bool,
}
impl Modifiers {
pub const NONE: Modifiers = Modifiers {
ctrl: false,
alt: false,
shift: false,
meta: false,
};
pub fn is_empty(self) -> bool {
!self.ctrl && !self.alt && !self.shift && !self.meta
}
pub fn command(self) -> bool {
self.ctrl || self.meta
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Key {
Character(String),
Enter,
Tab,
Space,
Backspace,
Delete,
Escape,
ArrowLeft,
ArrowRight,
ArrowUp,
ArrowDown,
Home,
End,
PageUp,
PageDown,
Function(u8),
Named(String),
}
impl Key {
pub fn as_text(&self) -> Option<&str> {
match self {
Key::Character(s) => Some(s.as_str()),
Key::Space => Some(" "),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ScrollDelta {
Lines {
x: f32,
y: f32,
},
Pixels {
x: f32,
y: f32,
},
}
use crate::geometry::Point;
#[derive(Clone, Debug, PartialEq)]
pub enum MouseEvent {
Down {
pos: Point,
button: MouseButton,
modifiers: Modifiers,
},
Up {
pos: Point,
button: MouseButton,
modifiers: Modifiers,
},
Move {
pos: Point,
modifiers: Modifiers,
},
Enter {
pos: Point,
},
Leave {
pos: Point,
},
DoubleClick {
pos: Point,
button: MouseButton,
modifiers: Modifiers,
},
TripleClick {
pos: Point,
button: MouseButton,
modifiers: Modifiers,
},
Scroll {
pos: Point,
delta: ScrollDelta,
modifiers: Modifiers,
},
DragStart {
pos: Point,
button: MouseButton,
},
DragMove {
pos: Point,
delta: Point,
},
DragEnd {
pos: Point,
button: MouseButton,
},
}
impl MouseEvent {
pub fn position(&self) -> Point {
match self {
MouseEvent::Down { pos, .. }
| MouseEvent::Up { pos, .. }
| MouseEvent::Move { pos, .. }
| MouseEvent::Enter { pos }
| MouseEvent::Leave { pos }
| MouseEvent::DoubleClick { pos, .. }
| MouseEvent::TripleClick { pos, .. }
| MouseEvent::Scroll { pos, .. }
| MouseEvent::DragStart { pos, .. }
| MouseEvent::DragMove { pos, .. }
| MouseEvent::DragEnd { pos, .. } => *pos,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PhysicalKey(pub String);
impl PhysicalKey {
pub fn new(code: impl Into<String>) -> Self {
Self(code.into())
}
pub fn code(&self) -> &str {
&self.0
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum KeyboardEvent {
Down {
key: Key,
physical: Option<PhysicalKey>,
modifiers: Modifiers,
repeat: bool,
},
Up {
key: Key,
physical: Option<PhysicalKey>,
modifiers: Modifiers,
},
CharInput {
text: String,
},
}
impl KeyboardEvent {
pub fn is_repeat(&self) -> bool {
matches!(self, KeyboardEvent::Down { repeat: true, .. })
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum GestureKind {
Pinch {
scale: f32,
},
Rotate {
radians: f32,
},
Swipe {
delta: Point,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum TouchEvent {
Start {
id: u64,
pos: Point,
},
Move {
id: u64,
pos: Point,
},
End {
id: u64,
pos: Point,
},
Cancel {
id: u64,
},
Gesture {
kind: GestureKind,
center: Point,
},
}
impl TouchEvent {
pub fn touch_id(&self) -> Option<u64> {
match self {
TouchEvent::Start { id, .. }
| TouchEvent::Move { id, .. }
| TouchEvent::End { id, .. }
| TouchEvent::Cancel { id } => Some(*id),
TouchEvent::Gesture { .. } => None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Propagation {
pub stop_propagation: bool,
pub prevent_default: bool,
}
impl Propagation {
pub const CONTINUE: Propagation = Propagation {
stop_propagation: false,
prevent_default: false,
};
pub const fn stop() -> Self {
Self {
stop_propagation: true,
prevent_default: false,
}
}
pub const fn prevent() -> Self {
Self {
stop_propagation: false,
prevent_default: true,
}
}
pub fn merge(self, other: Propagation) -> Propagation {
Propagation {
stop_propagation: self.stop_propagation || other.stop_propagation,
prevent_default: self.prevent_default || other.prevent_default,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn modifiers_command() {
assert!(Modifiers::NONE.is_empty());
let ctrl = Modifiers {
ctrl: true,
..Modifiers::NONE
};
assert!(ctrl.command());
let meta = Modifiers {
meta: true,
..Modifiers::NONE
};
assert!(meta.command());
assert!(!Modifiers::NONE.command());
}
#[test]
fn key_as_text() {
assert_eq!(Key::Character("ä".into()).as_text(), Some("ä"));
assert_eq!(Key::Space.as_text(), Some(" "));
assert_eq!(Key::Enter.as_text(), None);
assert_eq!(Key::Function(5), Key::Function(5));
}
#[test]
fn mouse_button_eq() {
assert_eq!(MouseButton::Left, MouseButton::Left);
assert_ne!(MouseButton::Left, MouseButton::Right);
assert_eq!(MouseButton::Other(7), MouseButton::Other(7));
}
#[test]
fn mouse_event_position() {
let e = MouseEvent::Down {
pos: Point::new(3.0, 4.0),
button: MouseButton::Left,
modifiers: Modifiers::NONE,
};
assert_eq!(e.position(), Point::new(3.0, 4.0));
let drag = MouseEvent::DragMove {
pos: Point::new(9.0, 9.0),
delta: Point::new(1.0, 1.0),
};
assert_eq!(drag.position(), Point::new(9.0, 9.0));
}
#[test]
fn keyboard_event_repeat_and_char() {
let down = KeyboardEvent::Down {
key: Key::Character("a".into()),
physical: Some(PhysicalKey::new("KeyA")),
modifiers: Modifiers::NONE,
repeat: true,
};
assert!(down.is_repeat());
let ci = KeyboardEvent::CharInput { text: "x".into() };
assert!(!ci.is_repeat());
if let KeyboardEvent::Down {
physical: Some(p), ..
} = &down
{
assert_eq!(p.code(), "KeyA");
} else {
panic!("expected Down with physical key");
}
}
#[test]
fn touch_event_id_and_gesture() {
assert_eq!(
TouchEvent::Start {
id: 7,
pos: Point::ZERO
}
.touch_id(),
Some(7)
);
let g = TouchEvent::Gesture {
kind: GestureKind::Pinch { scale: 1.5 },
center: Point::new(10.0, 10.0),
};
assert_eq!(g.touch_id(), None);
assert_eq!(g, g);
}
#[test]
fn propagation_merge_is_sticky() {
let a = Propagation::stop();
let b = Propagation::prevent();
let m = a.merge(b);
assert!(m.stop_propagation);
assert!(m.prevent_default);
assert!(
Propagation::CONTINUE
.merge(Propagation::stop())
.stop_propagation
);
assert_eq!(Propagation::default(), Propagation::CONTINUE);
}
}