api: overhaul pointer API

- Rename `CursorMoved` to `PointerMoved`.
- Rename `CursorEntered` to `PointerEntered`.
- Rename `CursorLeft` to `PointerLeft`.
- Rename `MouseInput` to `PointerButton`.
- Add `position` to every `PointerEvent`.
- Remove `Touch`, which is folded into the `Pointer*` events.
- New `PointerType` added to `PointerEntered` and `PointerLeft`,
  signifying which pointer type is the source of this event.
- New `PointerSource` added to `PointerMoved`, similar to `PointerType`
  but holding additional data.
- New `ButtonSource` added to `PointerButton`, similar to `PointerType`
  but holding pointer type specific buttons. Use
  `ButtonSource::mouse_button()` to easily normalize any pointer button
  type to a generic mouse button.
- In the same spirit rename `DeviceEvent::MouseMotion` to `PointerMotion`.
- Remove `Force::Calibrated::altitude_angle`.

Fixes #3833.
Fixes #883.
Fixes #336.

Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
daxpedda 2024-10-08 14:19:00 +02:00 committed by GitHub
parent 32cd1ad9a7
commit eccd9e415d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 1236 additions and 869 deletions

View file

@ -12,7 +12,7 @@ use web_sys::{
};
use super::super::cursor::CursorHandler;
use super::super::event::{DeviceId, FingerId};
use super::super::event::DeviceId;
use super::super::main_thread::MainThreadMarker;
use super::super::WindowId;
use super::animation_frame::AnimationFrameHandler;
@ -20,10 +20,12 @@ use super::event_handle::EventListenerHandle;
use super::intersection_handle::IntersectionObserverHandle;
use super::media_query_handle::MediaQueryListHandle;
use super::pointer::PointerHandler;
use super::{event, fullscreen, ButtonsState, ResizeScaleHandle};
use super::{event, fullscreen, ResizeScaleHandle};
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use crate::error::RequestError;
use crate::event::{Force, MouseButton, MouseScrollDelta, SurfaceSizeWriter};
use crate::event::{
ButtonSource, ElementState, MouseScrollDelta, PointerKind, PointerSource, SurfaceSizeWriter,
};
use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey};
use crate::platform_impl::Fullscreen;
use crate::window::{WindowAttributes, WindowId as RootWindowId};
@ -328,83 +330,62 @@ impl Canvas {
}));
}
pub fn on_cursor_leave<F>(&self, handler: F)
pub fn on_pointer_leave<F>(&self, handler: F)
where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{
self.handlers.borrow_mut().pointer_handler.on_cursor_leave(&self.common, handler)
self.handlers.borrow_mut().pointer_handler.on_pointer_leave(&self.common, handler)
}
pub fn on_cursor_enter<F>(&self, handler: F)
pub fn on_pointer_enter<F>(&self, handler: F)
where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{
self.handlers.borrow_mut().pointer_handler.on_cursor_enter(&self.common, handler)
self.handlers.borrow_mut().pointer_handler.on_pointer_enter(&self.common, handler)
}
pub fn on_mouse_release<M, T>(&self, mouse_handler: M, touch_handler: T)
pub fn on_pointer_release<C>(&self, handler: C)
where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_mouse_release(
self.handlers.borrow_mut().pointer_handler.on_pointer_release(&self.common, handler)
}
pub fn on_pointer_press<C>(&self, handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
self.handlers.borrow_mut().pointer_handler.on_pointer_press(
&self.common,
mouse_handler,
touch_handler,
)
}
pub fn on_mouse_press<M, T>(&self, mouse_handler: M, touch_handler: T)
where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_mouse_press(
&self.common,
mouse_handler,
touch_handler,
handler,
Rc::clone(&self.prevent_default),
)
}
pub fn on_cursor_move<M, T, B>(&self, mouse_handler: M, touch_handler: T, button_handler: B)
pub fn on_pointer_move<C, B>(&self, cursor_handler: C, button_handler: B)
where
M: 'static
+ FnMut(ModifiersState, Option<DeviceId>, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
T: 'static
C: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
FingerId,
&mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
),
B: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
PhysicalPosition<f64>,
ButtonsState,
MouseButton,
ElementState,
ButtonSource,
),
{
self.handlers.borrow_mut().pointer_handler.on_cursor_move(
self.handlers.borrow_mut().pointer_handler.on_pointer_move(
&self.common,
mouse_handler,
touch_handler,
cursor_handler,
button_handler,
Rc::clone(&self.prevent_default),
)
}
pub fn on_touch_cancel<F>(&self, handler: F)
where
F: 'static + FnMut(Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{
self.handlers.borrow_mut().pointer_handler.on_touch_cancel(&self.common, handler)
}
pub fn on_mouse_wheel<F>(&self, mut handler: F)
where
F: 'static + FnMut(MouseScrollDelta, ModifiersState),

View file

@ -6,8 +6,9 @@ use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent};
use super::super::FingerId;
use super::Engine;
use crate::event::{MouseButton, MouseScrollDelta};
use crate::event::{MouseButton, MouseScrollDelta, PointerKind};
use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey};
bitflags::bitflags! {
@ -68,14 +69,14 @@ pub fn mouse_button(event: &MouseEvent) -> Option<MouseButton> {
}
impl MouseButton {
pub fn to_id(self) -> u32 {
pub fn to_id(self) -> u16 {
match self {
MouseButton::Left => 0,
MouseButton::Right => 1,
MouseButton::Middle => 2,
MouseButton::Back => 3,
MouseButton::Forward => 4,
MouseButton::Other(value) => value.into(),
MouseButton::Other(value) => value,
}
}
}
@ -160,6 +161,14 @@ pub fn mouse_scroll_delta(
}
}
pub fn pointer_type(event: &PointerEvent, pointer_id: i32) -> PointerKind {
match event.pointer_type().as_str() {
"mouse" => PointerKind::Mouse,
"touch" => PointerKind::Touch(FingerId::new(pointer_id, event.is_primary()).into()),
_ => PointerKind::Unknown,
}
}
pub fn key_code(event: &KeyboardEvent) -> PhysicalKey {
let code = event.code();
PhysicalKey::from_key_code_attribute_value(&code)

View file

@ -18,7 +18,6 @@ use wasm_bindgen::JsCast;
use web_sys::{Document, HtmlCanvasElement, Navigator, PageTransitionEvent, VisibilityState};
pub use self::canvas::{Canvas, Style};
pub use self::event::ButtonsState;
pub use self::event_handle::EventListenerHandle;
pub use self::resize_scaling::ResizeScaleHandle;
pub use self::schedule::Schedule;

View file

@ -1,15 +1,14 @@
use std::cell::Cell;
use std::rc::Rc;
use event::ButtonsState;
use web_sys::PointerEvent;
use super::super::event::{DeviceId, FingerId};
use super::super::event::DeviceId;
use super::canvas::Common;
use super::event;
use super::event_handle::EventListenerHandle;
use crate::dpi::PhysicalPosition;
use crate::event::{Force, MouseButton};
use crate::event::{ButtonSource, ElementState, Force, PointerKind, PointerSource};
use crate::keyboard::ModifiersState;
#[allow(dead_code)]
@ -34,88 +33,78 @@ impl PointerHandler {
}
}
pub fn on_cursor_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
pub fn on_pointer_leave<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{
let window = canvas_common.window.clone();
self.on_cursor_leave =
Some(canvas_common.add_event("pointerout", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
// touch events are handled separately
// handling them here would produce duplicate mouse events, inconsistent with
// other platforms.
let device_id =
(event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id()));
handler(modifiers, device_id);
let pointer_id = event.pointer_id();
let device_id = DeviceId::new(pointer_id);
let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id);
handler(modifiers, device_id, position, kind);
}));
}
pub fn on_cursor_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
pub fn on_pointer_enter<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(ModifiersState, Option<Option<DeviceId>>),
F: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, PointerKind),
{
let window = canvas_common.window.clone();
self.on_cursor_enter =
Some(canvas_common.add_event("pointerover", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
// touch events are handled separately
// handling them here would produce duplicate mouse events, inconsistent with
// other platforms.
let device_id =
(event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id()));
handler(modifiers, device_id);
let pointer_id = event.pointer_id();
let device_id = DeviceId::new(pointer_id);
let position =
event::mouse_position(&event).to_physical(super::scale_factor(&window));
let kind = event::pointer_type(&event, pointer_id);
handler(modifiers, device_id, position, kind);
}));
}
pub fn on_mouse_release<M, T>(
&mut self,
canvas_common: &Common,
mut mouse_handler: M,
mut touch_handler: T,
) where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
pub fn on_pointer_release<C>(&mut self, canvas_common: &Common, mut handler: C)
where
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
let window = canvas_common.window.clone();
self.on_pointer_release =
Some(canvas_common.add_event("pointerup", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let kind = event::pointer_type(&event, pointer_id);
match event.pointer_type().as_str() {
"touch" => {
let pointer_id = event.pointer_id();
touch_handler(
modifiers,
DeviceId::new(pointer_id),
FingerId::new(pointer_id, event.is_primary()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
)
let button = event::mouse_button(&event).expect("no mouse button pressed");
let source = match kind {
PointerKind::Mouse => ButtonSource::Mouse(button),
PointerKind::Touch(finger_id) => ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
},
_ => mouse_handler(
modifiers,
DeviceId::new(event.pointer_id()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_button(&event).expect("no mouse button released"),
),
}
PointerKind::Unknown => ButtonSource::Unknown(button.to_id()),
};
handler(
modifiers,
DeviceId::new(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
}));
}
pub fn on_mouse_press<M, T>(
pub fn on_pointer_press<C>(
&mut self,
canvas_common: &Common,
mut mouse_handler: M,
mut touch_handler: T,
mut handler: C,
prevent_default: Rc<Cell<bool>>,
) where
M: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, MouseButton),
T: 'static
+ FnMut(ModifiersState, Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
C: 'static + FnMut(ModifiersState, Option<DeviceId>, PhysicalPosition<f64>, ButtonSource),
{
let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone();
@ -129,75 +118,65 @@ impl PointerHandler {
}
let modifiers = event::mouse_modifiers(&event);
let pointer_type = &event.pointer_type();
let pointer_id = event.pointer_id();
let kind = event::pointer_type(&event, pointer_id);
let button = event::mouse_button(&event).expect("no mouse button pressed");
match pointer_type.as_str() {
"touch" => {
let pointer_id = event.pointer_id();
touch_handler(
modifiers,
DeviceId::new(pointer_id),
FingerId::new(pointer_id, event.is_primary()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
);
let source = match kind {
PointerKind::Mouse => {
// Error is swallowed here since the error would occur every time the
// mouse is clicked when the cursor is
// grabbed, and there is probably not a
// situation where this could fail, that we
// care if it fails.
let _e = canvas.set_pointer_capture(pointer_id);
ButtonSource::Mouse(button)
},
_ => {
let pointer_id = event.pointer_id();
mouse_handler(
modifiers,
DeviceId::new(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_button(&event).expect("no mouse button pressed"),
);
if pointer_type == "mouse" {
// Error is swallowed here since the error would occur every time the
// mouse is clicked when the cursor is
// grabbed, and there is probably not a
// situation where this could fail, that we
// care if it fails.
let _e = canvas.set_pointer_capture(pointer_id);
}
PointerKind::Touch(finger_id) => ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
},
}
PointerKind::Unknown => ButtonSource::Unknown(button.to_id()),
};
handler(
modifiers,
DeviceId::new(pointer_id),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
source,
)
}));
}
pub fn on_cursor_move<M, T, B>(
pub fn on_pointer_move<C, B>(
&mut self,
canvas_common: &Common,
mut mouse_handler: M,
mut touch_handler: T,
mut cursor_handler: C,
mut button_handler: B,
prevent_default: Rc<Cell<bool>>,
) where
M: 'static
+ FnMut(ModifiersState, Option<DeviceId>, &mut dyn Iterator<Item = PhysicalPosition<f64>>),
T: 'static
C: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
FingerId,
&mut dyn Iterator<Item = (PhysicalPosition<f64>, Force)>,
&mut dyn Iterator<Item = (ModifiersState, PhysicalPosition<f64>, PointerSource)>,
),
B: 'static
+ FnMut(
ModifiersState,
Option<DeviceId>,
PhysicalPosition<f64>,
ButtonsState,
MouseButton,
ElementState,
ButtonSource,
),
{
let window = canvas_common.window.clone();
let canvas = canvas_common.raw().clone();
self.on_cursor_move =
Some(canvas_common.add_event("pointermove", move |event: PointerEvent| {
let modifiers = event::mouse_modifiers(&event);
let pointer_id = event.pointer_id();
let device_id = DeviceId::new(pointer_id);
let kind = event::pointer_type(&event, pointer_id);
// chorded button event
if let Some(button) = event::mouse_button(&event) {
@ -208,11 +187,34 @@ impl PointerHandler {
let _ = canvas.focus();
}
let state = if event::mouse_buttons(&event).contains(button.into()) {
ElementState::Pressed
} else {
ElementState::Released
};
let button = match kind {
PointerKind::Mouse => ButtonSource::Mouse(button),
PointerKind::Touch(finger_id) => {
let button_id = button.to_id();
if button_id != 1 {
tracing::error!("unexpected touch button id: {button_id}");
}
ButtonSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
}
},
PointerKind::Unknown => todo!(),
};
button_handler(
modifiers,
event::mouse_modifiers(&event),
device_id,
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
event::mouse_buttons(&event),
state,
button,
);
@ -221,44 +223,24 @@ impl PointerHandler {
// pointer move event
let scale = super::scale_factor(&window);
match event.pointer_type().as_str() {
"touch" => touch_handler(
modifiers,
device_id,
FingerId::new(pointer_id, event.is_primary()),
&mut event::pointer_move_event(event).map(|event| {
(
event::mouse_position(&event).to_physical(scale),
Force::Normalized(event.pressure() as f64),
)
}),
),
_ => mouse_handler(
modifiers,
device_id,
&mut event::pointer_move_event(event)
.map(|event| event::mouse_position(&event).to_physical(scale)),
),
};
}));
}
pub fn on_touch_cancel<F>(&mut self, canvas_common: &Common, mut handler: F)
where
F: 'static + FnMut(Option<DeviceId>, FingerId, PhysicalPosition<f64>, Force),
{
let window = canvas_common.window.clone();
self.on_touch_cancel =
Some(canvas_common.add_event("pointercancel", move |event: PointerEvent| {
if event.pointer_type() == "touch" {
let pointer_id = event.pointer_id();
handler(
DeviceId::new(pointer_id),
FingerId::new(pointer_id, event.is_primary()),
event::mouse_position(&event).to_physical(super::scale_factor(&window)),
Force::Normalized(event.pressure() as f64),
);
}
cursor_handler(
device_id,
&mut event::pointer_move_event(event).map(|event| {
(
event::mouse_modifiers(&event),
event::mouse_position(&event).to_physical(scale),
match kind {
PointerKind::Mouse => PointerSource::Mouse,
PointerKind::Touch(finger_id) => PointerSource::Touch {
finger_id,
force: Some(Force::Normalized(event.pressure().into())),
},
PointerKind::Unknown => PointerSource::Unknown,
},
)
}),
);
}));
}