ui_events_web/
lib.rs

1// Copyright 2025 the UI Events Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! This crate bridges [`web_sys`] DOM input events — Pointer Events (mouse, touch, pen),
5//! Wheel, and Keyboard — into the [`ui-events`] model.
6//!
7//! It provides lightweight helpers to convert browser events into portable
8//! `ui-events` types you can feed into your input handling. It supports
9//! Pointer Events (mouse, touch, pen) and keyboard.
10//!
11//! ## Keyboard
12//!
13//! - [`keyboard::from_web_keyboard_event`]
14//! - Optional helpers: [`keyboard::from_web_keydown_event`], [`keyboard::from_web_keyup_event`]
15//!
16//! ## Pointer (Pointer Events)
17//!
18//! - One‑shot DOM conversion: [`pointer::pointer_event_from_dom_event`]
19//! - Multi-touch aware DOM conversion (may return multiple events):
20//!   [`pointer::pointer_events_from_dom_event`]
21//! - Per‑event helpers (preferred):
22//!   [`pointer::down_from_pointer_event`], [`pointer::up_from_pointer_event`],
23//!   [`pointer::move_from_pointer_event`], [`pointer::enter_from_pointer_event`],
24//!   [`pointer::leave_from_pointer_event`], [`pointer::cancel_from_pointer_event`]
25//! - Mouse‑only helpers (legacy and less portable):
26//!   [`pointer::down_from_mouse_event`], [`pointer::up_from_mouse_event`],
27//!   [`pointer::move_from_mouse_event`], [`pointer::enter_from_mouse_event`],
28//!   [`pointer::leave_from_mouse_event`], [`pointer::scroll_from_wheel_event`]
29//! - Conversion options: [`pointer::Options`] (controls scale/coalesced/predicted)
30//! - Pointer capture helpers: [`pointer::set_pointer_capture`],
31//!   [`pointer::release_pointer_capture`], [`pointer::has_pointer_capture`]
32//!
33//! ## Notes
34//!
35//! - Positions use `clientX` / `clientY` scaled by `Options::scale_factor`. Pass the
36//!   current device-pixel-ratio for physical pixels.
37//! - **Element-local coordinates (recipe):** DOM pointer coordinates are reported in viewport
38//!   space even when you register the listener on an element. For canvas/SVG apps you often
39//!   want coordinates relative to a specific element’s top-left corner. A common recipe is:
40//!
41//!   ```no_run
42//!   # use web_sys::{window, Element, PointerEvent};
43//!   # let (el, e): (Element, PointerEvent) = unimplemented!();
44//!   let rect = el.get_bounding_client_rect();
45//!   let dpr = window().unwrap().device_pixel_ratio();
46//!   let x = (e.client_x() as f64 - rect.left()) * dpr;
47//!   let y = (e.client_y() as f64 - rect.top()) * dpr;
48//!   ```
49//!
50//!   This does *not* undo arbitrary CSS transforms, and for SVG you may want a true transform
51//!   inversion (e.g. `getScreenCTM()`).
52//! - Coalesced and predicted move samples are opt‑in via `Options`.
53//! - Touch events (`touchstart`/`touchmove`/`touchend`/`touchcancel`) may correspond to multiple
54//!   changed touches; use `pointer_events_from_dom_event` to receive all of them.
55//! - Stylus fields:
56//!   - `pressure`, `tangential_pressure`, `contact_geometry`, and `orientation` are populated
57//!     from Pointer Events data when available (preferring `altitudeAngle`/`azimuthAngle`,
58//!     otherwise falling back to `tiltX`/`tiltY`).
59//!   - Stylus rotation/twist (Pointer Events `twist`) is not currently exposed by `ui-events`,
60//!     so it is not mapped.
61//! - Keyboard: unknown `key`/`code` map to `Unidentified`; `is_composing` reflects the DOM flag.
62//!
63//! ## Example
64//!
65//! ```no_run
66//! use web_sys::wasm_bindgen::JsCast;
67//! use web_sys::{window, Event, KeyboardEvent};
68//! use ui_events_web::{keyboard, pointer};
69//!
70//! // Inside an event listener closure…
71//! # {
72//! let ev: Event = /* from DOM */
73//! # unimplemented!();
74//! let win = window().unwrap();
75//! let opts = pointer::Options::default()
76//!     .with_scale(win.device_pixel_ratio())
77//!     .with_coalesced(true)
78//!     .with_predicted(true);
79//!
80//! if let Some(pe) = pointer::pointer_event_from_dom_event(&ev, &opts) {
81//!     match pe {
82//!         ui_events::pointer::PointerEvent::Move(update) => {
83//!             // Use update.current; update.coalesced / update.predicted may be empty
84//!         }
85//!         ui_events::pointer::PointerEvent::Down(_) => {}
86//!         ui_events::pointer::PointerEvent::Up(_) => {}
87//!         _ => {}
88//!     }
89//! }
90//!
91//! if let Some(ke) = ev.dyn_ref::<KeyboardEvent>() {
92//!     let k = keyboard::from_web_keyboard_event(ke);
93//!     // Use k.state, k.code, k.key, k.modifiers …
94//! }
95//! # }
96//! ```
97//!
98//! [`ui-events`]: https://docs.rs/ui-events/
99
100// LINEBENDER LINT SET - lib.rs - v3
101// See https://linebender.org/wiki/canonical-lints/
102// These lints shouldn't apply to examples or tests.
103#![cfg_attr(not(test), warn(unused_crate_dependencies))]
104// These lints shouldn't apply to examples.
105#![warn(clippy::print_stdout, clippy::print_stderr)]
106// Targeting e.g. 32-bit means structs containing usize can give false positives for 64-bit.
107#![cfg_attr(target_pointer_width = "64", warn(clippy::trivially_copy_pass_by_ref))]
108// END LINEBENDER LINT SET
109#![no_std]
110
111extern crate alloc;
112
113pub mod keyboard;
114pub mod pointer;