[yew-devel] [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent`
Dominik Csapak
d.csapak at proxmox.com
Tue Jun 24 14:19:19 CEST 2025
this is an abstracted event for either a PointerEvent or a Touch.
This prepares the interfaces to deal with touch only input.
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
Cargo.toml | 1 +
src/touch/gesture_detector.rs | 87 ++++++++++++++++++++++-------------
src/touch/mod.rs | 4 +-
src/touch/side_dialog.rs | 14 +++---
src/touch/slidable/mod.rs | 8 ++--
5 files changed, 67 insertions(+), 47 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 0e919e0..2561436 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -50,6 +50,7 @@ web-sys = { version = "0.3", features = [
"IntersectionObserver",
"IntersectionObserverEntry",
"KeyboardEventInit",
+ "Touch",
] }
js-sys = "0.3"
log = "0.4.6"
diff --git a/src/touch/gesture_detector.rs b/src/touch/gesture_detector.rs
index 2e50e54..0929e01 100644
--- a/src/touch/gesture_detector.rs
+++ b/src/touch/gesture_detector.rs
@@ -3,6 +3,7 @@ use std::collections::HashMap;
use std::rc::Rc;
use gloo_timers::callback::Timeout;
+use web_sys::Touch;
use yew::html::IntoEventCallback;
use yew::prelude::*;
use yew::virtual_dom::{Key, VComp, VNode};
@@ -11,39 +12,64 @@ use crate::impl_to_html;
use crate::props::{ContainerBuilder, EventSubscriber, WidgetBuilder, WidgetStyleBuilder};
use crate::widget::Container;
-/// Like [PointerEvent](web_sys::PointerEvent) (currently no additional features)
-pub struct GestureDragEvent {
- event: PointerEvent,
+/// An event that can happen from a [`PointerEvent`] or a [`Touch`]
+///
+/// For convenience, expose the most important values from the underlying events
+pub enum InputEvent {
+ PointerEvent(PointerEvent),
+ Touch(Touch),
}
-impl GestureDragEvent {
- fn new(event: PointerEvent) -> Self {
- Self { event }
+impl InputEvent {
+ pub fn x(&self) -> i32 {
+ match self {
+ InputEvent::PointerEvent(pointer_event) => pointer_event.client_x(),
+ InputEvent::Touch(touch) => touch.client_x(),
+ }
+ }
+
+ pub fn y(&self) -> i32 {
+ match self {
+ InputEvent::PointerEvent(pointer_event) => pointer_event.client_y(),
+ InputEvent::Touch(touch) => touch.client_y(),
+ }
+ }
+
+ pub fn id(&self) -> i32 {
+ match self {
+ InputEvent::PointerEvent(pointer_event) => pointer_event.pointer_id(),
+ InputEvent::Touch(touch) => touch.identifier(),
+ }
}
}
-impl Deref for GestureDragEvent {
- type Target = PointerEvent;
- fn deref(&self) -> &Self::Target {
- &self.event
+impl From<PointerEvent> for InputEvent {
+ fn from(event: PointerEvent) -> Self {
+ Self::PointerEvent(event)
+ }
+}
+
+impl From<Touch> for InputEvent {
+ fn from(touch: Touch) -> Self {
+ Self::Touch(touch)
}
}
/// Like [PointerEvent](web_sys::PointerEvent), but includes the swipe direction
pub struct GestureSwipeEvent {
- event: PointerEvent,
+ event: InputEvent,
/// Direction angle (from -180 to +180 degree)
pub direction: f64,
}
impl GestureSwipeEvent {
- fn new(event: PointerEvent, direction: f64) -> Self {
+ fn new(event: InputEvent, direction: f64) -> Self {
Self { event, direction }
}
}
impl Deref for GestureSwipeEvent {
- type Target = PointerEvent;
+ type Target = InputEvent;
fn deref(&self) -> &Self::Target {
&self.event
}
@@ -98,20 +124,20 @@ pub struct GestureDetector {
/// Callback for tap events.
#[prop_or_default]
- pub on_tap: Option<Callback<PointerEvent>>,
+ pub on_tap: Option<Callback<InputEvent>>,
/// Callback for long-tap events.
#[prop_or_default]
pub on_long_press: Option<Callback<()>>,
/// Callback for drag-start events.
#[prop_or_default]
- pub on_drag_start: Option<Callback<GestureDragEvent>>,
+ pub on_drag_start: Option<Callback<InputEvent>>,
/// Callback for drag-start events.
#[prop_or_default]
- pub on_drag_update: Option<Callback<GestureDragEvent>>,
+ pub on_drag_update: Option<Callback<InputEvent>>,
/// Callback for drag-start events.
#[prop_or_default]
- pub on_drag_end: Option<Callback<GestureDragEvent>>,
+ pub on_drag_end: Option<Callback<InputEvent>>,
#[prop_or_default]
pub on_swipe: Option<Callback<GestureSwipeEvent>>,
@@ -132,7 +158,7 @@ impl GestureDetector {
}
/// Builder style method to set the on_tap callback
- pub fn on_tap(mut self, cb: impl IntoEventCallback<PointerEvent>) -> Self {
+ pub fn on_tap(mut self, cb: impl IntoEventCallback<InputEvent>) -> Self {
self.on_tap = cb.into_event_callback();
self
}
@@ -144,19 +170,19 @@ impl GestureDetector {
}
/// Builder style method to set the on_drag_start callback
- pub fn on_drag_start(mut self, cb: impl IntoEventCallback<GestureDragEvent>) -> Self {
+ pub fn on_drag_start(mut self, cb: impl IntoEventCallback<InputEvent>) -> Self {
self.on_drag_start = cb.into_event_callback();
self
}
/// Builder style method to set the on_drag_update callback
- pub fn on_drag_update(mut self, cb: impl IntoEventCallback<GestureDragEvent>) -> Self {
+ pub fn on_drag_update(mut self, cb: impl IntoEventCallback<InputEvent>) -> Self {
self.on_drag_update = cb.into_event_callback();
self
}
/// Builder style method to set the on_drag_end callback
- pub fn on_drag_end(mut self, cb: impl IntoEventCallback<GestureDragEvent>) -> Self {
+ pub fn on_drag_end(mut self, cb: impl IntoEventCallback<InputEvent>) -> Self {
self.on_drag_end = cb.into_event_callback();
self
}
@@ -365,7 +391,7 @@ impl PwtGestureDetector {
if !pointer_state.got_tap_timeout && distance < props.tap_tolerance {
if let Some(on_tap) = &props.on_tap {
//log::info!("tap {} {}", event.x(), event.y());
- on_tap.emit(event);
+ on_tap.emit(event.into());
}
}
}
@@ -387,8 +413,7 @@ impl PwtGestureDetector {
self.state = DetectionState::Drag;
self.capture_pointer(event.pointer_id());
if let Some(on_drag_start) = &props.on_drag_start {
- let event = GestureDragEvent::new(event);
- on_drag_start.emit(event);
+ on_drag_start.emit(event.into());
}
}
}
@@ -417,8 +442,7 @@ impl PwtGestureDetector {
self.state = DetectionState::Double;
//log::info!("DRAG END");
if let Some(on_drag_end) = &props.on_drag_end {
- let event = GestureDragEvent::new(event);
- on_drag_end.emit(event);
+ on_drag_end.emit(event.into());
}
}
Msg::PointerUp(event) => {
@@ -437,8 +461,7 @@ impl PwtGestureDetector {
let speed = distance / time_diff;
//log::info!("DRAG END {time_diff} {speed}");
if let Some(on_drag_end) = &props.on_drag_end {
- let event = GestureDragEvent::new(event.clone());
- on_drag_end.emit(event);
+ on_drag_end.emit(event.clone().into());
}
if let Some(on_swipe) = &props.on_swipe {
@@ -453,7 +476,7 @@ impl PwtGestureDetector {
event.y(),
);
- let event = GestureSwipeEvent::new(event, direction);
+ let event = GestureSwipeEvent::new(event.into(), direction);
on_swipe.emit(event)
}
}
@@ -473,8 +496,7 @@ impl PwtGestureDetector {
if distance >= props.tap_tolerance || pointer_state.got_tap_timeout {
//log::info!("DRAG TO {} {}", event.x(), event.y());
if let Some(on_drag_update) = &props.on_drag_update {
- let event = GestureDragEvent::new(event);
- on_drag_update.emit(event);
+ on_drag_update.emit(event.into());
}
}
}
@@ -486,8 +508,7 @@ impl PwtGestureDetector {
self.state = DetectionState::Initial;
//log::info!("DRAG END");
if let Some(on_drag_end) = &props.on_drag_end {
- let event = GestureDragEvent::new(event);
- on_drag_end.emit(event);
+ on_drag_end.emit(event.into());
}
}
}
diff --git a/src/touch/mod.rs b/src/touch/mod.rs
index c33f1f0..87fcc85 100644
--- a/src/touch/mod.rs
+++ b/src/touch/mod.rs
@@ -5,9 +5,7 @@ mod application_bar;
pub use application_bar::{ApplicationBar, PwtApplicationBar};
mod gesture_detector;
-pub use gesture_detector::{
- GestureDetector, GestureDragEvent, GestureSwipeEvent, PwtGestureDetector,
-};
+pub use gesture_detector::{GestureDetector, GestureSwipeEvent, InputEvent, PwtGestureDetector};
mod fab;
pub use fab::{Fab, PwtFab};
diff --git a/src/touch/side_dialog.rs b/src/touch/side_dialog.rs
index 2bd45f8..01abcd9 100644
--- a/src/touch/side_dialog.rs
+++ b/src/touch/side_dialog.rs
@@ -12,7 +12,7 @@ use crate::prelude::*;
use crate::state::{SharedState, SharedStateObserver};
use crate::widget::Container;
-use super::{GestureDetector, GestureDragEvent, GestureSwipeEvent};
+use super::{GestureDetector, GestureSwipeEvent, InputEvent};
// Messages sent from the [SideDialogController].
pub enum SideDialogControllerMsg {
@@ -125,9 +125,9 @@ pub enum Msg {
Close,
Dismiss, // Slide out, then close
SliderAnimationEnd,
- DragStart(GestureDragEvent),
- DragEnd(GestureDragEvent),
- Drag(GestureDragEvent),
+ DragStart(InputEvent),
+ DragEnd(InputEvent),
+ Drag(InputEvent),
Swipe(GestureSwipeEvent),
Controller,
}
@@ -390,11 +390,11 @@ impl Component for PwtSideDialog {
.on_tap({
let slider_ref = self.slider_ref.clone();
let link = ctx.link().clone();
- move |event: PointerEvent| {
+ move |event: InputEvent| {
if let Some(element) = slider_ref.clone().into_html_element() {
let rect = element.get_bounding_client_rect();
- let x = event.client_x() as f64;
- let y = event.client_y() as f64;
+ let x = event.x() as f64;
+ let y = event.y() as f64;
if (rect.left() < x)
&& (x < rect.right())
diff --git a/src/touch/slidable/mod.rs b/src/touch/slidable/mod.rs
index c81d2a9..ffcf8c8 100644
--- a/src/touch/slidable/mod.rs
+++ b/src/touch/slidable/mod.rs
@@ -15,7 +15,7 @@ use yew::virtual_dom::VNode;
use crate::dom::DomSizeObserver;
use crate::prelude::*;
use crate::props::CssLength;
-use crate::touch::{GestureDetector, GestureDragEvent, GestureSwipeEvent};
+use crate::touch::{GestureDetector, GestureSwipeEvent, InputEvent};
use crate::widget::{Container, Row};
use pwt_macros::widget;
@@ -116,9 +116,9 @@ pub struct PwtSlidable {
pub enum Msg {
StartDismissTransition,
- Drag(GestureDragEvent),
- DragStart(GestureDragEvent),
- DragEnd(GestureDragEvent),
+ Drag(InputEvent),
+ DragStart(InputEvent),
+ DragEnd(InputEvent),
Swipe(GestureSwipeEvent),
LeftResize(f64),
RightResize(f64),
--
2.39.5
More information about the yew-devel
mailing list