use presentar_core::{Event, Key, MouseButton as CoreMouseButton, Point};
use wasm_bindgen::prelude::*;
use web_sys::{KeyboardEvent, MouseEvent, PointerEvent, TouchEvent, WheelEvent};
pub fn mouse_event_to_presentar(event: &MouseEvent, event_type: &str) -> Event {
let position = Point::new(event.offset_x() as f32, event.offset_y() as f32);
let button = match event.button() {
0 => CoreMouseButton::Left,
1 => CoreMouseButton::Middle,
2 => CoreMouseButton::Right,
3 => CoreMouseButton::Button4,
_ => CoreMouseButton::Button5,
};
match event_type {
"mousedown" => Event::MouseDown { position, button },
"mouseup" => Event::MouseUp { position, button },
"mousemove" => Event::MouseMove { position },
"mouseenter" => Event::MouseEnter,
"mouseleave" => Event::MouseLeave,
"click" => Event::MouseUp { position, button },
_ => Event::MouseMove { position },
}
}
pub fn keyboard_event_to_presentar(event: &KeyboardEvent, event_type: &str) -> Event {
let key = code_to_key(&event.code());
match event_type {
"keydown" => Event::KeyDown { key },
"keyup" => Event::KeyUp { key },
_ => Event::KeyDown { key },
}
}
fn code_to_key(code: &str) -> Key {
match code {
"KeyA" => Key::A,
"KeyB" => Key::B,
"KeyC" => Key::C,
"KeyD" => Key::D,
"KeyE" => Key::E,
"KeyF" => Key::F,
"KeyG" => Key::G,
"KeyH" => Key::H,
"KeyI" => Key::I,
"KeyJ" => Key::J,
"KeyK" => Key::K,
"KeyL" => Key::L,
"KeyM" => Key::M,
"KeyN" => Key::N,
"KeyO" => Key::O,
"KeyP" => Key::P,
"KeyQ" => Key::Q,
"KeyR" => Key::R,
"KeyS" => Key::S,
"KeyT" => Key::T,
"KeyU" => Key::U,
"KeyV" => Key::V,
"KeyW" => Key::W,
"KeyX" => Key::X,
"KeyY" => Key::Y,
"KeyZ" => Key::Z,
"Digit0" => Key::Num0,
"Digit1" => Key::Num1,
"Digit2" => Key::Num2,
"Digit3" => Key::Num3,
"Digit4" => Key::Num4,
"Digit5" => Key::Num5,
"Digit6" => Key::Num6,
"Digit7" => Key::Num7,
"Digit8" => Key::Num8,
"Digit9" => Key::Num9,
"Enter" => Key::Enter,
"Escape" => Key::Escape,
"Backspace" => Key::Backspace,
"Tab" => Key::Tab,
"Space" => Key::Space,
"Delete" => Key::Delete,
"ArrowUp" => Key::Up,
"ArrowDown" => Key::Down,
"ArrowLeft" => Key::Left,
"ArrowRight" => Key::Right,
"ShiftLeft" => Key::ShiftLeft,
"ShiftRight" => Key::ShiftRight,
"ControlLeft" => Key::ControlLeft,
"ControlRight" => Key::ControlRight,
_ => Key::Space, }
}
pub fn text_input_event(event: &KeyboardEvent) -> Option<Event> {
let key = event.key();
if key.len() == 1 && !event.ctrl_key() && !event.alt_key() && !event.meta_key() {
Some(Event::TextInput { text: key })
} else {
None
}
}
#[derive(Debug, Clone, Copy)]
pub struct TouchPoint {
pub id: i32,
pub position: Point,
pub pressure: f32,
}
impl TouchPoint {
#[must_use]
pub const fn new(id: i32, position: Point, pressure: f32) -> Self {
Self {
id,
position,
pressure,
}
}
}
pub fn touch_event_to_points(event: &TouchEvent, canvas_offset: Point) -> Vec<TouchPoint> {
let touches = event.touches();
let mut points = Vec::with_capacity(touches.length() as usize);
for i in 0..touches.length() {
if let Some(touch) = touches.get(i) {
let x = touch.client_x() as f32 - canvas_offset.x;
let y = touch.client_y() as f32 - canvas_offset.y;
points.push(TouchPoint::new(
touch.identifier(),
Point::new(x, y),
touch.force() as f32,
));
}
}
points
}
pub fn touch_event_to_presentar(
event: &TouchEvent,
event_type: &str,
canvas_offset: Point,
) -> Event {
let touches = event.touches();
if touches.length() == 0 {
return Event::MouseLeave;
}
let touch = touches.get(0).unwrap();
let position = Point::new(
touch.client_x() as f32 - canvas_offset.x,
touch.client_y() as f32 - canvas_offset.y,
);
match event_type {
"touchstart" => Event::MouseDown {
position,
button: CoreMouseButton::Left,
},
"touchend" | "touchcancel" => Event::MouseUp {
position,
button: CoreMouseButton::Left,
},
"touchmove" => Event::MouseMove { position },
_ => Event::MouseMove { position },
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PointerType {
Mouse,
Touch,
Pen,
Unknown,
}
impl From<&str> for PointerType {
fn from(s: &str) -> Self {
match s {
"mouse" => Self::Mouse,
"touch" => Self::Touch,
"pen" => Self::Pen,
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PointerData {
pub id: i32,
pub position: Point,
pub pointer_type: PointerType,
pub pressure: f32,
pub tilt_x: f32,
pub tilt_y: f32,
pub width: f32,
pub height: f32,
pub is_primary: bool,
}
impl PointerData {
pub fn from_event(event: &PointerEvent) -> Self {
Self {
id: event.pointer_id(),
position: Point::new(event.offset_x() as f32, event.offset_y() as f32),
pointer_type: PointerType::from(event.pointer_type().as_str()),
pressure: event.pressure() as f32,
tilt_x: event.tilt_x() as f32,
tilt_y: event.tilt_y() as f32,
width: event.width() as f32,
height: event.height() as f32,
is_primary: event.is_primary(),
}
}
}
pub fn pointer_event_to_presentar(event: &PointerEvent, event_type: &str) -> Event {
let position = Point::new(event.offset_x() as f32, event.offset_y() as f32);
let button = match event.button() {
0 => CoreMouseButton::Left,
1 => CoreMouseButton::Middle,
2 => CoreMouseButton::Right,
3 => CoreMouseButton::Button4,
_ => CoreMouseButton::Button5,
};
match event_type {
"pointerdown" => Event::MouseDown { position, button },
"pointerup" => Event::MouseUp { position, button },
"pointermove" => Event::MouseMove { position },
"pointerenter" => Event::MouseEnter,
"pointerleave" => Event::MouseLeave,
"pointercancel" => Event::MouseLeave,
_ => Event::MouseMove { position },
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeltaMode {
Pixel,
Line,
Page,
}
impl From<u32> for DeltaMode {
fn from(mode: u32) -> Self {
match mode {
0 => Self::Pixel,
1 => Self::Line,
2 => Self::Page,
_ => Self::Pixel,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct WheelData {
pub delta_x: f32,
pub delta_y: f32,
pub mode: DeltaMode,
pub position: Point,
}
impl WheelData {
pub fn from_event(event: &WheelEvent) -> Self {
Self {
delta_x: event.delta_x() as f32,
delta_y: event.delta_y() as f32,
mode: DeltaMode::from(event.delta_mode()),
position: Point::new(event.offset_x() as f32, event.offset_y() as f32),
}
}
#[must_use]
pub fn normalized_delta(&self) -> (f32, f32) {
let multiplier = match self.mode {
DeltaMode::Pixel => 1.0,
DeltaMode::Line => 20.0, DeltaMode::Page => 400.0, };
(self.delta_x * multiplier, self.delta_y * multiplier)
}
}
pub fn wheel_event_to_scroll(event: &WheelEvent) -> (f32, f32) {
let data = WheelData::from_event(event);
data.normalized_delta()
}
pub struct JsonEventClosure {
_closure: Closure<dyn FnMut(web_sys::Event)>,
}
impl JsonEventClosure {
pub fn mouse<F>(mut callback: F) -> Self
where
F: FnMut(Event) + 'static,
{
let closure = Closure::new(move |e: web_sys::Event| {
if let Some(mouse_event) = e.dyn_ref::<MouseEvent>() {
let event_type = e.type_();
let presentar_event = mouse_event_to_presentar(mouse_event, &event_type);
callback(presentar_event);
}
});
Self { _closure: closure }
}
pub fn keyboard<F>(mut callback: F) -> Self
where
F: FnMut(Event) + 'static,
{
let closure = Closure::new(move |e: web_sys::Event| {
if let Some(kb_event) = e.dyn_ref::<KeyboardEvent>() {
let event_type = e.type_();
let presentar_event = keyboard_event_to_presentar(kb_event, &event_type);
callback(presentar_event);
}
});
Self { _closure: closure }
}
pub fn as_function(&self) -> &js_sys::Function {
self._closure.as_ref().unchecked_ref()
}
pub fn touch<F>(mut callback: F, canvas_offset: Point) -> Self
where
F: FnMut(Event, Vec<TouchPoint>) + 'static,
{
let closure = Closure::new(move |e: web_sys::Event| {
if let Some(touch_event) = e.dyn_ref::<TouchEvent>() {
let event_type = e.type_();
let presentar_event =
touch_event_to_presentar(touch_event, &event_type, canvas_offset);
let points = touch_event_to_points(touch_event, canvas_offset);
callback(presentar_event, points);
}
});
Self { _closure: closure }
}
pub fn pointer<F>(mut callback: F) -> Self
where
F: FnMut(Event, PointerData) + 'static,
{
let closure = Closure::new(move |e: web_sys::Event| {
if let Some(pointer_event) = e.dyn_ref::<PointerEvent>() {
let event_type = e.type_();
let presentar_event = pointer_event_to_presentar(pointer_event, &event_type);
let data = PointerData::from_event(pointer_event);
callback(presentar_event, data);
}
});
Self { _closure: closure }
}
pub fn wheel<F>(mut callback: F) -> Self
where
F: FnMut(f32, f32, Point) + 'static,
{
let closure = Closure::new(move |e: web_sys::Event| {
if let Some(wheel_event) = e.dyn_ref::<WheelEvent>() {
let data = WheelData::from_event(wheel_event);
let (dx, dy) = data.normalized_delta();
callback(dx, dy, data.position);
}
});
Self { _closure: closure }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pointer_type_from_str() {
assert_eq!(PointerType::from("mouse"), PointerType::Mouse);
assert_eq!(PointerType::from("touch"), PointerType::Touch);
assert_eq!(PointerType::from("pen"), PointerType::Pen);
assert_eq!(PointerType::from("unknown"), PointerType::Unknown);
}
#[test]
fn test_delta_mode_from_u32() {
assert_eq!(DeltaMode::from(0), DeltaMode::Pixel);
assert_eq!(DeltaMode::from(1), DeltaMode::Line);
assert_eq!(DeltaMode::from(2), DeltaMode::Page);
assert_eq!(DeltaMode::from(99), DeltaMode::Pixel);
}
#[test]
fn test_touch_point_new() {
let point = TouchPoint::new(1, Point::new(100.0, 200.0), 0.5);
assert_eq!(point.id, 1);
assert_eq!(point.position.x, 100.0);
assert_eq!(point.position.y, 200.0);
assert_eq!(point.pressure, 0.5);
}
#[test]
fn test_code_to_key_letters() {
assert!(matches!(code_to_key("KeyA"), Key::A));
assert!(matches!(code_to_key("KeyZ"), Key::Z));
}
#[test]
fn test_code_to_key_digits() {
assert!(matches!(code_to_key("Digit0"), Key::Num0));
assert!(matches!(code_to_key("Digit9"), Key::Num9));
}
#[test]
fn test_code_to_key_special() {
assert!(matches!(code_to_key("Enter"), Key::Enter));
assert!(matches!(code_to_key("Escape"), Key::Escape));
assert!(matches!(code_to_key("Tab"), Key::Tab));
assert!(matches!(code_to_key("Space"), Key::Space));
assert!(matches!(code_to_key("ArrowUp"), Key::Up));
assert!(matches!(code_to_key("ArrowDown"), Key::Down));
}
}