#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MouseButton {
Left,
Middle,
Right,
Other(i16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct KeyboardModifiers {
pub ctrl: bool,
pub shift: bool,
pub alt: bool,
pub meta: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InputType {
InsertText,
InsertReplacementText,
InsertLineBreak,
InsertParagraph,
InsertFromPaste,
InsertFromDrop,
DeleteContentBackward,
DeleteContentForward,
DeleteByCut,
DeleteWordBackward,
DeleteWordForward,
HistoryUndo,
HistoryRedo,
Other(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ToggleState {
Open,
Closed,
}
use crate::node_id::NodeId;
#[derive(Debug, Clone, PartialEq, Default)]
pub enum EventDetail {
#[default]
None,
String(String),
Transition(Box<TransitionDetail>),
Input(Box<InputDetail>),
Submit(Box<SubmitDetail>),
Toggle(Box<ToggleDetail>),
Mouse(MouseDetail),
Keyboard(Box<KeyboardDetail>),
}
#[cfg(target_pointer_width = "64")]
const _: () = assert!(std::mem::size_of::<EventDetail>() <= 32);
impl EventDetail {
pub fn as_string(&self) -> Option<&str> {
match self {
EventDetail::String(s) => Some(s),
_ => None,
}
}
pub fn as_transition(&self) -> Option<&TransitionDetail> {
match self {
EventDetail::Transition(t) => Some(t),
_ => None,
}
}
pub fn as_input(&self) -> Option<&InputDetail> {
match self {
EventDetail::Input(i) => Some(i),
_ => None,
}
}
pub fn as_submit(&self) -> Option<&SubmitDetail> {
match self {
EventDetail::Submit(s) => Some(s),
_ => None,
}
}
pub fn as_toggle(&self) -> Option<&ToggleDetail> {
match self {
EventDetail::Toggle(t) => Some(t),
_ => None,
}
}
pub fn as_mouse(&self) -> Option<&MouseDetail> {
match self {
EventDetail::Mouse(m) => Some(m),
_ => None,
}
}
pub fn as_keyboard(&self) -> Option<&KeyboardDetail> {
match self {
EventDetail::Keyboard(k) => Some(k),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TransitionDetail {
pub property_name: String,
pub elapsed: f64,
pub pseudo_element: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InputDetail {
pub input_type: InputType,
pub data: Option<String>,
pub is_composing: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SubmitDetail {
pub submitter: Option<NodeId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ToggleDetail {
pub old_state: ToggleState,
pub new_state: ToggleState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MouseDetail {
pub button: MouseButton,
pub buttons: u8,
pub client_x: i32,
pub client_y: i32,
pub delta_x: i32,
pub delta_y: i32,
pub modifiers: KeyboardModifiers,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeyboardDetail {
pub key: String,
pub modifiers: KeyboardModifiers,
pub repeat: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mouse_button_left_middle_right_are_unit_variants() {
assert!(matches!(MouseButton::Left, MouseButton::Left));
assert!(matches!(MouseButton::Middle, MouseButton::Middle));
assert!(matches!(MouseButton::Right, MouseButton::Right));
}
#[test]
fn mouse_button_other_carries_i16() {
let back = MouseButton::Other(3);
match back {
MouseButton::Other(n) => assert_eq!(n, 3),
_ => panic!("Other(3) didn't match Other"),
}
}
#[test]
fn mouse_button_is_copy_and_eq() {
let b = MouseButton::Left;
let c = b; assert_eq!(b, c);
assert_ne!(MouseButton::Left, MouseButton::Right);
assert_ne!(MouseButton::Other(3), MouseButton::Other(4));
}
#[test]
fn keyboard_modifiers_default_is_all_false() {
let m = KeyboardModifiers::default();
assert!(!m.ctrl);
assert!(!m.shift);
assert!(!m.alt);
assert!(!m.meta);
}
#[test]
fn keyboard_modifiers_field_struct_round_trips() {
let m = KeyboardModifiers {
ctrl: true,
shift: false,
alt: true,
meta: false,
};
assert!(m.ctrl);
assert!(!m.shift);
assert!(m.alt);
assert!(!m.meta);
}
#[test]
fn keyboard_modifiers_is_copy_and_eq() {
let a = KeyboardModifiers {
ctrl: true,
..Default::default()
};
let b = a; assert_eq!(a, b);
let c = KeyboardModifiers {
shift: true,
..Default::default()
};
assert_ne!(a, c);
}
#[test]
fn input_type_named_variants_exist() {
let named = [
InputType::InsertText,
InputType::InsertReplacementText,
InputType::InsertLineBreak,
InputType::InsertParagraph,
InputType::InsertFromPaste,
InputType::InsertFromDrop,
InputType::DeleteContentBackward,
InputType::DeleteContentForward,
InputType::DeleteByCut,
InputType::DeleteWordBackward,
InputType::DeleteWordForward,
InputType::HistoryUndo,
InputType::HistoryRedo,
];
for (i, a) in named.iter().enumerate() {
for (j, b) in named.iter().enumerate() {
if i == j {
assert_eq!(a, b);
} else {
assert_ne!(a, b);
}
}
}
}
#[test]
fn input_type_other_carries_string() {
let it = InputType::Other("formatBold".into());
match &it {
InputType::Other(s) => assert_eq!(s, "formatBold"),
_ => panic!("Other didn't match Other"),
}
}
#[test]
fn input_type_other_differs_from_named_with_same_label() {
assert_ne!(InputType::Other("insertText".into()), InputType::InsertText);
}
#[test]
fn toggle_state_variants_exist_and_differ() {
assert!(matches!(ToggleState::Open, ToggleState::Open));
assert!(matches!(ToggleState::Closed, ToggleState::Closed));
assert_ne!(ToggleState::Open, ToggleState::Closed);
}
#[test]
fn toggle_state_is_copy() {
let s = ToggleState::Open;
let t = s; assert_eq!(s, t);
}
#[test]
fn event_detail_default_is_none() {
let d: EventDetail = Default::default();
assert!(matches!(d, EventDetail::None));
}
#[test]
fn event_detail_string_round_trip_via_as_string() {
let d = EventDetail::String("payload".into());
assert_eq!(d.as_string(), Some("payload"));
}
#[test]
fn event_detail_as_string_returns_none_for_other_variants() {
assert_eq!(EventDetail::None.as_string(), None);
assert_eq!(
EventDetail::Mouse(MouseDetail {
button: MouseButton::Left,
buttons: 0,
client_x: 0,
client_y: 0,
delta_x: 0,
delta_y: 0,
modifiers: KeyboardModifiers::default(),
})
.as_string(),
None
);
}
#[test]
fn event_detail_as_transition_round_trips() {
let d = EventDetail::Transition(Box::new(TransitionDetail {
property_name: "color".into(),
elapsed: 0.25,
pseudo_element: None,
}));
let t = d.as_transition().expect("variant matches");
assert_eq!(t.property_name, "color");
assert!((t.elapsed - 0.25).abs() < f64::EPSILON);
assert!(t.pseudo_element.is_none());
assert_eq!(d.as_string(), None);
}
#[test]
fn event_detail_as_input_round_trips() {
let d = EventDetail::Input(Box::new(InputDetail {
input_type: InputType::InsertText,
data: Some("a".into()),
is_composing: false,
}));
let i = d.as_input().expect("variant matches");
assert_eq!(i.input_type, InputType::InsertText);
assert_eq!(i.data.as_deref(), Some("a"));
assert!(!i.is_composing);
}
#[test]
fn event_detail_as_submit_round_trips_with_none_submitter() {
let d = EventDetail::Submit(Box::new(SubmitDetail { submitter: None }));
let s = d.as_submit().expect("variant matches");
assert!(s.submitter.is_none());
}
#[test]
fn event_detail_as_toggle_round_trips() {
let d = EventDetail::Toggle(Box::new(ToggleDetail {
old_state: ToggleState::Closed,
new_state: ToggleState::Open,
}));
let t = d.as_toggle().expect("variant matches");
assert_eq!(t.old_state, ToggleState::Closed);
assert_eq!(t.new_state, ToggleState::Open);
}
#[test]
fn event_detail_as_mouse_round_trips() {
let d = EventDetail::Mouse(MouseDetail {
button: MouseButton::Right,
buttons: 0b010,
client_x: 12,
client_y: 7,
delta_x: 0,
delta_y: -1,
modifiers: KeyboardModifiers {
ctrl: true,
..Default::default()
},
});
let m = d.as_mouse().expect("variant matches");
assert_eq!(m.button, MouseButton::Right);
assert_eq!(m.buttons, 0b010);
assert_eq!(m.client_x, 12);
assert_eq!(m.client_y, 7);
assert_eq!(m.delta_y, -1);
assert!(m.modifiers.ctrl);
assert!(!m.modifiers.shift);
}
#[test]
fn event_detail_as_keyboard_round_trips() {
let d = EventDetail::Keyboard(Box::new(KeyboardDetail {
key: "Enter".into(),
modifiers: KeyboardModifiers::default(),
repeat: false,
}));
let k = d.as_keyboard().expect("variant matches");
assert_eq!(k.key, "Enter");
assert!(!k.repeat);
}
#[test]
fn event_detail_accessor_cross_check() {
let s = EventDetail::String("hello".into());
assert!(s.as_transition().is_none());
assert!(s.as_input().is_none());
assert!(s.as_submit().is_none());
assert!(s.as_toggle().is_none());
assert!(s.as_mouse().is_none());
assert!(s.as_keyboard().is_none());
}
}