use crate::traits::StaticEvent; use wasm_bindgen::JsCast; use web_sys::{EventTarget, HtmlInputElement, HtmlTextAreaElement, TouchList, Touch}; #[cfg(feature = "nightly")] pub struct Event { event: T, } #[cfg(feature = "nightly")] impl StaticEvent for Event where T: JsCast { const EVENT_TYPE: &'static str = NAME; #[inline] fn unchecked_from_event(event: web_sys::Event) -> Self { Self { // TODO use unchecked_into in release mode ? event: event.dyn_into().unwrap(), } } } // TODO code duplication // TODO implement the rest of the methods #[cfg(feature = "nightly")] impl Event where T: AsRef { #[inline] pub fn prevent_default(&self) { self.event.as_ref().prevent_default(); } #[inline] pub fn target(&self) -> Option { self.event.as_ref().target() } #[inline] pub fn dyn_target(&self) -> Option where A: JsCast { self.target()?.dyn_into().ok() } } macro_rules! make_event { ($name:ident, $type:literal => $event:path) => { #[derive(Debug)] pub struct $name { event: $event, } impl StaticEvent for $name { const EVENT_TYPE: &'static str = $type; #[inline] fn unchecked_from_event(event: web_sys::Event) -> Self { Self { event: event.unchecked_into(), } } } impl $name { #[inline] pub fn prevent_default(&self) { self.event.prevent_default(); } #[inline] pub fn stop_propagation(&self) { self.event.stop_propagation(); } #[inline] pub fn stop_immediate_propagation(&self) { self.event.stop_immediate_propagation(); } #[inline] pub fn target(&self) -> Option { self.event.target() } #[inline] pub fn dyn_target(&self) -> Option where A: JsCast { self.target()?.dyn_into().ok() } } }; } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum MouseButton { Left, Middle, Right, Button4, Button5, } macro_rules! make_mouse_event { ($name:ident, $type:literal => $event:path) => { make_event!($name, $type => $event); impl $name { #[inline] pub fn x(&self) -> i32 { self.event.client_x() } #[inline] pub fn y(&self) -> i32 { self.event.client_y() } #[inline] pub fn movement_x(&self) -> i32 { self.event.movement_x() } #[inline] pub fn movement_y(&self) -> i32 { self.event.movement_y() } #[inline] pub fn offset_x(&self) -> i32 { self.event.offset_x() } #[inline] pub fn offset_y(&self) -> i32 { self.event.offset_y() } #[inline] pub fn page_x(&self) -> i32 { self.event.page_x() } #[inline] pub fn page_y(&self) -> i32 { self.event.page_y() } #[inline] pub fn screen_x(&self) -> i32 { self.event.screen_x() } #[inline] pub fn screen_y(&self) -> i32 { self.event.screen_y() } #[inline] pub fn ctrl_key(&self) -> bool { self.event.ctrl_key() || self.event.meta_key() } #[inline] pub fn shift_key(&self) -> bool { self.event.shift_key() } #[inline] pub fn alt_key(&self) -> bool { self.event.alt_key() } // TODO maybe deprecate these ? #[inline] pub fn mouse_x(&self) -> i32 { self.event.client_x() } #[inline] pub fn mouse_y(&self) -> i32 { self.event.client_y() } pub fn button(&self) -> MouseButton { match self.event.button() { 0 => MouseButton::Left, 1 => MouseButton::Middle, 2 => MouseButton::Right, 3 => MouseButton::Button4, 4 => MouseButton::Button5, _ => unreachable!("Unexpected MouseEvent.button value"), } } } }; } macro_rules! make_pointer_event { ($name:ident, $type:literal) => { make_mouse_event!($name, $type => web_sys::PointerEvent); impl $name { #[inline] pub fn pointer_id(&self) -> i32 { self.event.pointer_id() } #[inline] pub fn pointer_width(&self) -> i32 { self.event.width() } #[inline] pub fn pointer_height(&self) -> i32 { self.event.height() } #[inline] pub fn pressure(&self) -> f32 { self.event.pressure() } #[inline] pub fn tangential_pressure(&self) -> f32 { self.event.tangential_pressure() } #[inline] pub fn tilt_x(&self) -> i32 { self.event.tilt_x() } #[inline] pub fn tilt_y(&self) -> i32 { self.event.tilt_y() } #[inline] pub fn twist(&self) -> i32 { self.event.twist() } #[inline] pub fn is_primary(&self) -> bool { self.event.is_primary() } } }; } macro_rules! make_touch_event { ($name:ident, $type:literal) => { make_event!($name, $type => web_sys::TouchEvent); impl $name { #[inline] pub fn ctrl_key(&self) -> bool { self.event.ctrl_key() || self.event.meta_key() } #[inline] pub fn shift_key(&self) -> bool { self.event.shift_key() } #[inline] pub fn alt_key(&self) -> bool { self.event.alt_key() } #[inline] pub fn changed_touches(&self) -> impl Iterator { TouchListIter::new(self.event.changed_touches()) } #[inline] pub fn target_touches(&self) -> impl Iterator { TouchListIter::new(self.event.target_touches()) } #[inline] pub fn touches(&self) -> impl Iterator { TouchListIter::new(self.event.touches()) } } }; } macro_rules! make_keyboard_event { ($name:ident, $type:literal) => { make_event!($name, $type => web_sys::KeyboardEvent); impl $name { // TODO return enum or something #[inline] pub fn key(&self) -> String { self.event.key() } #[inline] pub fn ctrl_key(&self) -> bool { self.event.ctrl_key() || self.event.meta_key() } #[inline] pub fn shift_key(&self) -> bool { self.event.shift_key() } #[inline] pub fn alt_key(&self) -> bool { self.event.alt_key() } #[inline] pub fn repeat(&self) -> bool { self.event.repeat() } } }; } macro_rules! make_focus_event { ($name:ident, $type:literal) => { make_event!($name, $type => web_sys::FocusEvent); impl $name { #[inline] pub fn related_target(&self) -> Option { self.event.related_target() } } }; } macro_rules! make_drag_event { ($name:ident, $type:literal) => { make_mouse_event!($name, $type => web_sys::DragEvent); impl $name { #[inline] pub fn data_transfer(&self) -> Option { self.event.data_transfer() } } }; } macro_rules! make_input_event { ($name:ident, $type:literal) => { make_event!($name, $type => web_sys::InputEvent); impl $name { #[inline] pub fn data(&self) -> Option { self.event.data() } } }; } macro_rules! make_animation_event { ($name:ident, $type:literal) => { make_event!($name, $type => web_sys::AnimationEvent); impl $name { #[inline] pub fn animation_name(&self) -> String { self.event.animation_name() } #[inline] pub fn elapsed_time(&self) -> f32 { self.event.elapsed_time() } #[inline] pub fn pseudo_element(&self) -> String { self.event.pseudo_element() } } }; } macro_rules! make_wheel_event { ($name:ident, $type:literal) => { make_mouse_event!($name, $type => web_sys::WheelEvent); impl $name { #[inline] pub fn delta_x(&self) -> f64 { self.event.delta_x() } #[inline] pub fn delta_y(&self) -> f64 { self.event.delta_y() } #[inline] pub fn delta_z(&self) -> f64 { self.event.delta_z() } } }; } make_mouse_event!(Click, "click" => web_sys::MouseEvent); make_mouse_event!(MouseDown, "mousedown" => web_sys::MouseEvent); make_mouse_event!(MouseUp, "mouseup" => web_sys::MouseEvent); make_mouse_event!(MouseMove, "mousemove" => web_sys::MouseEvent); make_mouse_event!(MouseEnter, "mouseenter" => web_sys::MouseEvent); make_mouse_event!(MouseLeave, "mouseleave" => web_sys::MouseEvent); make_mouse_event!(DoubleClick, "dblclick" => web_sys::MouseEvent); make_mouse_event!(ContextMenu, "contextmenu" => web_sys::MouseEvent); make_pointer_event!(PointerOver, "pointerover"); make_pointer_event!(PointerEnter, "pointerenter"); make_pointer_event!(PointerDown, "pointerdown"); make_pointer_event!(PointerMove, "pointermove"); make_pointer_event!(PointerUp, "pointerup"); make_pointer_event!(PointerCancel, "pointercancel"); make_pointer_event!(PointerOut, "pointerout"); make_pointer_event!(PointerLeave, "pointerleave"); make_pointer_event!(GotPointerCapture, "gotpointercapture"); make_pointer_event!(LostPointerCapture, "lostpointercapture"); make_keyboard_event!(KeyDown, "keydown"); make_keyboard_event!(KeyUp, "keyup"); make_focus_event!(Focus, "focus"); make_focus_event!(Blur, "blur"); make_focus_event!(FocusIn, "focusin"); make_focus_event!(FocusOut, "focusout"); make_drag_event!(DragStart, "dragstart"); make_drag_event!(Drag, "drag"); make_drag_event!(DragEnd, "dragend"); make_drag_event!(DragOver, "dragover"); make_drag_event!(DragEnter, "dragenter"); make_drag_event!(DragLeave, "dragleave"); make_drag_event!(Drop, "drop"); make_input_event!(Input, "input"); make_input_event!(BeforeInput, "beforeinput"); make_animation_event!(AnimationStart, "animationstart"); make_animation_event!(AnimationIteration, "animationiteration"); make_animation_event!(AnimationCancel, "animationcancel"); make_animation_event!(AnimationEnd, "animationend"); make_wheel_event!(Wheel, "wheel"); make_event!(Load, "load" => web_sys::Event); make_event!(Scroll, "scroll" => web_sys::Event); make_event!(Submit, "submit" => web_sys::Event); make_event!(Resize, "resize" => web_sys::UiEvent); impl Input { // TODO should this work on other types as well ? #[deprecated(since = "0.5.19", note = "Use with_node instead")] pub fn value(&self) -> Option { let target = self.target()?; if let Some(target) = target.dyn_ref::() { // TODO check the element's type ? Some(target.value()) } else if let Some(target) = target.dyn_ref::() { Some(target.value()) } else { None } } } make_event!(Change, "change" => web_sys::Event); // TODO add in a value method as well, the same as Input::value impl Change { // https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement pub fn checked(&self) -> Option { let target = self.dyn_target::()?; match target.type_().as_str() { "checkbox" | "radio" => Some(target.checked()), _ => None, } } } make_touch_event!(TouchCancel, "touchcancel"); make_touch_event!(TouchEnd, "touchend"); make_touch_event!(TouchMove, "touchmove"); make_touch_event!(TouchStart, "touchstart"); #[derive(Debug)] struct TouchListIter { list: TouchList, index: u32, length: u32, } impl TouchListIter { fn new(list: TouchList) -> Self { Self { index: 0, length: list.length(), list, } } } impl Iterator for TouchListIter { type Item = Touch; fn next(&mut self) -> Option { let index = self.index; if index < self.length { self.index += 1; self.list.get(index) } else { None } } }