public inbox for yew-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes
@ 2025-06-24 12:19 Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 1/4] slidable: don't add padding on top and bottom to actions Dominik Csapak
                   ` (11 more replies)
  0 siblings, 12 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

various fixes for touch components & scss. They're intended to be
independent from each other, aside from maybe touching nearby lines.

proxmox-yew-widget-toolkit-assets:

Dominik Csapak (4):
  slidable: don't add padding on top and bottom to actions
  slidable: set background-color to pwt-color-background for actions
  color scheme: add `pwt-default-colors` helper
  material: add rounded corners to bottom sheet

 scss/_slidable.scss                     | 3 ++-
 scss/introduction.md                    | 4 ++++
 scss/material/_theme_post_material.scss | 7 +++++++
 scss/mixins/_color_scheme.scss          | 5 +++++
 4 files changed, 18 insertions(+), 1 deletion(-)


proxmox-yew-widget-toolkit:

Dominik Csapak (7):
  touch: gesture detector: introduce `InputEvent`
  touch: gesture detector: implement a touch only mode
  touch: fab: rename on_click to on_activate
  touch: slidable action: rename on_click to on_activate
  touch: slidable action: add css classes property
  touch: slidable: add on_tap callback
  touch: slidable: cleanup unnecessary macro use

 Cargo.toml                            |   2 +
 src/touch/fab.rs                      |  18 +-
 src/touch/fab_menu.rs                 |  25 +-
 src/touch/gesture_detector.rs         | 350 ++++++++++++++++++++++----
 src/touch/mod.rs                      |   4 +-
 src/touch/side_dialog.rs              |  14 +-
 src/touch/slidable/mod.rs             |  51 ++--
 src/touch/slidable/slidable_action.rs |  34 ++-
 8 files changed, 396 insertions(+), 102 deletions(-)


Summary over all repositories:
  12 files changed, 414 insertions(+), 103 deletions(-)

-- 
Generated by git-murpp 0.8.1


_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit-assets 1/4] slidable: don't add padding on top and bottom to actions
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 2/4] slidable: set background-color to pwt-color-background for actions Dominik Csapak
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

otherwise they are taller than the main slidable component, resulting in
graphical glitches.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 scss/_slidable.scss | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scss/_slidable.scss b/scss/_slidable.scss
index 784b500..1fd4d74 100644
--- a/scss/_slidable.scss
+++ b/scss/_slidable.scss
@@ -17,7 +17,7 @@
 
     height: 100%;
     box-sizing: border-box;
-    padding: var(--pwt-spacer-2);
+    padding: 0 var(--pwt-spacer-2);
 
     color: var(--pwt-color);
 
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit-assets 2/4] slidable: set background-color to pwt-color-background for actions
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 1/4] slidable: don't add padding on top and bottom to actions Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 3/4] color scheme: add `pwt-default-colors` helper Dominik Csapak
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

this way, an action can be colored in simply with the correct color
scheme class.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 scss/_slidable.scss | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scss/_slidable.scss b/scss/_slidable.scss
index 1fd4d74..821d554 100644
--- a/scss/_slidable.scss
+++ b/scss/_slidable.scss
@@ -20,6 +20,7 @@
     padding: 0 var(--pwt-spacer-2);
 
     color: var(--pwt-color);
+    background-color: var(--pwt-color-background);
 
     justify-content: center;
     align-items: center;
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit-assets 3/4] color scheme: add `pwt-default-colors` helper
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 1/4] slidable: don't add padding on top and bottom to actions Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 2/4] slidable: set background-color to pwt-color-background for actions Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 4/4] material: add rounded corners to bottom sheet Dominik Csapak
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

this helper makes it more easier to apply the color scheme settings to a
custom component. Otherwise one would have to manually set color and
background-color.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 scss/introduction.md           | 4 ++++
 scss/mixins/_color_scheme.scss | 5 +++++
 2 files changed, 9 insertions(+)

diff --git a/scss/introduction.md b/scss/introduction.md
index 9a2d795..9baa240 100644
--- a/scss/introduction.md
+++ b/scss/introduction.md
@@ -108,6 +108,10 @@ changes the colors of the widget. The `pwt-scheme-<name>` and
 `pwt-scheme-<name>-container` helper classes can be used to set
 those variables.
 
+There is also the `pwt-default-colors` class to set the `color` and
+`background-color` properties directly to the `--pwt-color` and
+`--pwt-color-background` respectively.
+
 A similar scheme is used for widgets with an associated accent color, using
 the following CSS variables:
 
diff --git a/scss/mixins/_color_scheme.scss b/scss/mixins/_color_scheme.scss
index 630ddce..36b26ca 100644
--- a/scss/mixins/_color_scheme.scss
+++ b/scss/mixins/_color_scheme.scss
@@ -34,6 +34,11 @@
     //    @include outline-button-color-scheme-vars($name, $important: true);
     //}
 
+    .pwt-default-colors {
+        color: var(--pwt-color);
+        background-color: var(--pwt-color-background);
+    }
+
     .pwt-color-#{$name} {
         color: var(--pwt-color-#{$name});
     }
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit-assets 4/4] material: add rounded corners to bottom sheet
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (2 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 3/4] color scheme: add `pwt-default-colors` helper Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent` Dominik Csapak
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 scss/material/_theme_post_material.scss | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/scss/material/_theme_post_material.scss b/scss/material/_theme_post_material.scss
index 0e53940..0f448cb 100644
--- a/scss/material/_theme_post_material.scss
+++ b/scss/material/_theme_post_material.scss
@@ -67,3 +67,10 @@ ul.pwt-menubar {
         @include elevation-box-shadow(1);
     }
 }
+
+/// `pwt-side-dialog-bottom`
+/// : overwrite corner radius
+.pwt-side-dialog-bottom {
+        border-top-left-radius: var(--pwt-shape-corner-large);
+        border-top-right-radius: var(--pwt-shape-corner-large);
+}
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent`
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (3 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 4/4] material: add rounded corners to bottom sheet Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 16:27   ` [yew-devel] applied: " Dietmar Maurer
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 2/7] touch: gesture detector: implement a touch only mode Dominik Csapak
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

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@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



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 2/7] touch: gesture detector: implement a touch only mode
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (4 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent` Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 3/7] touch: fab: rename on_click to on_activate Dominik Csapak
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

when dealing with touch devices, using pointer events is not practical,
since those will be canceled by touch events. While doing

```
event.prevent_default();
```

inside the 'touchstart' event would allow us to use pointer events,
click events on touch enabled devices would not anymore (since those are
generated from the touch events when touching).

As a slightly less broken workaround, detect if the browser is touch
capable, and set the gesture detector to a touch only mode, so that it
only uses the touchstart/end/move/cancel events.

This makes it both work on a touch enabled device with touch, and a
non-touch enabled device with the mouse. One downside is that it does
not work with a mouse on touch enabled devices, but this should not that
big of a problem, since the gesture detector is intended to be used with
touch interfaces in the first place.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 Cargo.toml                    |   1 +
 src/touch/gesture_detector.rs | 263 ++++++++++++++++++++++++++++++++--
 2 files changed, 251 insertions(+), 13 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 2561436..0a8d147 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -51,6 +51,7 @@ web-sys = { version = "0.3", features = [
   "IntersectionObserverEntry",
   "KeyboardEventInit",
   "Touch",
+  "TouchList",
 ] }
 js-sys = "0.3"
 log = "0.4.6"
diff --git a/src/touch/gesture_detector.rs b/src/touch/gesture_detector.rs
index 0929e01..6984722 100644
--- a/src/touch/gesture_detector.rs
+++ b/src/touch/gesture_detector.rs
@@ -3,6 +3,8 @@ use std::collections::HashMap;
 use std::rc::Rc;
 
 use gloo_timers::callback::Timeout;
+use gloo_utils::window;
+use wasm_bindgen::JsValue;
 use web_sys::Touch;
 use yew::html::IntoEventCallback;
 use yew::prelude::*;
@@ -203,6 +205,11 @@ pub enum Msg {
 
     LongPressTimeout(i32),
     TapTimeout(i32),
+
+    TouchStart(TouchEvent),
+    TouchMove(TouchEvent),
+    TouchCancel(TouchEvent),
+    TouchEnd(TouchEvent),
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -231,6 +238,7 @@ struct PointerState {
 
 #[doc(hidden)]
 pub struct PwtGestureDetector {
+    touch_only: bool,
     node_ref: NodeRef,
     state: DetectionState,
     pointers: HashMap<i32, PointerState>,
@@ -241,13 +249,9 @@ fn now() -> f64 {
 }
 
 impl PwtGestureDetector {
-    fn register_pointer(&mut self, ctx: &Context<Self>, event: &PointerEvent) {
+    fn register_pointer_state(&mut self, ctx: &Context<Self>, id: i32, start_x: i32, start_y: i32) {
         let props = ctx.props();
 
-        let id = event.pointer_id();
-        let start_x = event.x();
-        let start_y = event.y();
-
         let link = ctx.link().clone();
         let _long_press_timeout = Timeout::new(props.long_press_delay, move || {
             link.send_message(Msg::LongPressTimeout(id))
@@ -279,6 +283,36 @@ impl PwtGestureDetector {
         );
     }
 
+    fn register_pointer(&mut self, ctx: &Context<Self>, event: &PointerEvent) {
+        let id = event.pointer_id();
+        let start_x = event.x();
+        let start_y = event.y();
+
+        self.register_pointer_state(ctx, id, start_x, start_y);
+    }
+
+    fn register_touches(&mut self, ctx: &Context<Self>, event: &TouchEvent) {
+        for_each_changed_touch(event, |touch: Touch| {
+            let id = touch.identifier();
+            let x = touch.client_x();
+            let y = touch.client_y();
+            self.register_pointer_state(ctx, id, x, y);
+        });
+    }
+
+    fn unregister_touches<F: FnMut(i32, Touch, PointerState)>(
+        &mut self,
+        event: &TouchEvent,
+        mut func: F,
+    ) {
+        for_each_changed_touch(event, |touch: Touch| {
+            let id = touch.identifier();
+            if let Some(state) = self.pointers.remove(&id) {
+                func(id, touch, state);
+            }
+        });
+    }
+
     fn unregister_pointer(&mut self, id: i32) -> Option<PointerState> {
         self.pointers.remove(&id)
     }
@@ -332,10 +366,24 @@ impl PwtGestureDetector {
                 self.register_pointer(ctx, &event);
                 self.state = DetectionState::Single;
             }
+            Msg::TouchStart(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 0);
+                self.register_touches(ctx, &event);
+                self.state = match self.pointers.len() {
+                    0 => DetectionState::Initial,
+                    1 => DetectionState::Single,
+                    // TODO implement more touches
+                    _ => DetectionState::Double,
+                };
+            }
             Msg::PointerUp(_event) => { /* ignore */ }
             Msg::PointerMove(_event) => { /* ignore */ }
             Msg::PointerCancel(_event) => { /* ignore */ }
             Msg::PointerLeave(_event) => { /* ignore */ }
+            Msg::TouchMove(_event) => { /* ignore */ }
+            Msg::TouchCancel(_event) => { /* ignore */ }
+            Msg::TouchEnd(_event) => { /* ignore */ }
         }
         true
     }
@@ -376,6 +424,17 @@ impl PwtGestureDetector {
                 self.register_pointer(ctx, &event);
                 self.state = DetectionState::Double;
             }
+            Msg::TouchStart(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 1);
+                self.register_touches(ctx, &event);
+                self.state = match self.pointers.len() {
+                    0 => DetectionState::Initial,
+                    1 => DetectionState::Single,
+                    // TODO implement more touches
+                    _ => DetectionState::Double,
+                };
+            }
             Msg::PointerUp(event) => {
                 event.prevent_default();
                 let pointer_count = self.pointers.len();
@@ -396,6 +455,25 @@ impl PwtGestureDetector {
                     }
                 }
             }
+            Msg::TouchEnd(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 1);
+                self.unregister_touches(&event, |_id, touch, pointer_state| {
+                    let distance = compute_distance(
+                        pointer_state.start_x,
+                        pointer_state.start_y,
+                        touch.client_x(),
+                        touch.client_y(),
+                    );
+                    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(touch.into());
+                        }
+                    }
+                });
+                self.state = DetectionState::Initial;
+            }
             Msg::PointerMove(event) => {
                 event.prevent_default();
                 if let Some(pointer_state) =
@@ -418,6 +496,29 @@ impl PwtGestureDetector {
                     }
                 }
             }
+            Msg::TouchMove(event) => {
+                for_each_changed_touch(&event, |touch| {
+                    if let Some(pointer_state) = self.update_pointer_position(
+                        touch.identifier(),
+                        touch.client_x(),
+                        touch.client_y(),
+                    ) {
+                        let distance = compute_distance(
+                            pointer_state.start_x,
+                            pointer_state.start_y,
+                            touch.client_x(),
+                            touch.client_y(),
+                        );
+                        // Make sure it cannot be a TAP or LONG PRESS event
+                        if distance >= props.tap_tolerance {
+                            self.state = DetectionState::Drag;
+                            if let Some(on_drag_start) = &props.on_drag_start {
+                                on_drag_start.emit(touch.into());
+                            }
+                        }
+                    }
+                });
+            }
             Msg::PointerCancel(event) | Msg::PointerLeave(event) => {
                 let pointer_count = self.pointers.len();
                 assert!(pointer_count == 1);
@@ -425,6 +526,12 @@ impl PwtGestureDetector {
                     self.state = DetectionState::Initial;
                 }
             }
+            Msg::TouchCancel(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 1);
+                self.unregister_touches(&event, |_, _, _| {});
+                self.state = DetectionState::Initial;
+            }
         }
         true
     }
@@ -445,6 +552,20 @@ impl PwtGestureDetector {
                     on_drag_end.emit(event.into());
                 }
             }
+            Msg::TouchStart(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 1);
+                // Abort current drags
+                self.register_touches(ctx, &event);
+                self.state = DetectionState::Double;
+                for_each_active_touch(&event, |touch| {
+                    if self.pointers.contains_key(&touch.identifier()) {
+                        if let Some(on_drag_end) = &props.on_drag_end {
+                            on_drag_end.emit(touch.into());
+                        }
+                    }
+                });
+            }
             Msg::PointerUp(event) => {
                 event.prevent_default();
                 let pointer_count = self.pointers.len();
@@ -482,6 +603,44 @@ impl PwtGestureDetector {
                     }
                 }
             }
+            Msg::TouchEnd(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 1);
+                for_each_changed_touch(&event, |touch| {
+                    if let Some(pointer_state) = self.unregister_pointer(touch.identifier()) {
+                        let distance = compute_distance(
+                            pointer_state.start_x,
+                            pointer_state.start_y,
+                            touch.client_x(),
+                            touch.client_y(),
+                        );
+                        let time_diff = now() - pointer_state.start_ctime;
+                        let speed = distance / time_diff;
+                        //log::info!("DRAG END {time_diff} {speed}");
+                        if let Some(on_drag_end) = &props.on_drag_end {
+                            on_drag_end.emit(touch.clone().into());
+                        }
+
+                        if let Some(on_swipe) = &props.on_swipe {
+                            if distance > props.swipe_min_distance
+                                && time_diff < props.swipe_max_duration
+                                && speed > props.swipe_min_velocity
+                            {
+                                let direction = compute_direction(
+                                    pointer_state.start_x,
+                                    pointer_state.start_y,
+                                    touch.client_x(),
+                                    touch.client_y(),
+                                );
+
+                                let event = GestureSwipeEvent::new(touch.into(), direction);
+                                on_swipe.emit(event)
+                            }
+                        }
+                    }
+                });
+                self.state = DetectionState::Initial;
+            }
             Msg::PointerMove(event) => {
                 event.prevent_default();
                 if let Some(pointer_state) =
@@ -501,6 +660,28 @@ impl PwtGestureDetector {
                     }
                 }
             }
+            Msg::TouchMove(event) => {
+                for_each_changed_touch(&event, |touch| {
+                    if let Some(pointer_state) = self.update_pointer_position(
+                        touch.identifier(),
+                        touch.client_x(),
+                        touch.client_y(),
+                    ) {
+                        let distance = compute_distance(
+                            pointer_state.start_x,
+                            pointer_state.start_y,
+                            touch.client_x(),
+                            touch.client_y(),
+                        );
+                        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 {
+                                on_drag_update.emit(touch.into());
+                            }
+                        }
+                    }
+                });
+            }
             Msg::PointerCancel(event) | Msg::PointerLeave(event) => {
                 let pointer_count = self.pointers.len();
                 assert!(pointer_count == 1);
@@ -512,6 +693,17 @@ impl PwtGestureDetector {
                     }
                 }
             }
+            Msg::TouchCancel(event) => {
+                let pointer_count = self.pointers.len();
+                assert!(pointer_count == 1);
+                self.unregister_touches(&event, |_id, touch, _pointer_state| {
+                    //log::info!("DRAG END");
+                    if let Some(on_drag_end) = &props.on_drag_end {
+                        on_drag_end.emit(touch.into());
+                    }
+                });
+                self.state = DetectionState::Initial;
+            }
         }
         true
     }
@@ -524,19 +716,35 @@ impl PwtGestureDetector {
             Msg::PointerDown(event) => {
                 self.register_pointer(ctx, &event);
             }
+            Msg::TouchStart(event) => {
+                self.register_touches(ctx, &event);
+            }
             Msg::PointerUp(event) => {
                 self.unregister_pointer(event.pointer_id());
                 if self.pointers.is_empty() {
                     self.state = DetectionState::Initial;
                 }
             }
+            Msg::TouchEnd(event) => {
+                self.unregister_touches(&event, |_, _, _| {});
+                if self.pointers.is_empty() {
+                    self.state = DetectionState::Initial;
+                }
+            }
             Msg::PointerMove(_event) => { /* ignore */ }
+            Msg::TouchMove(_event) => { /* ignore */ }
             Msg::PointerCancel(event) => {
                 self.unregister_pointer(event.pointer_id());
                 if self.pointers.is_empty() {
                     self.state = DetectionState::Initial;
                 }
             }
+            Msg::TouchCancel(event) => {
+                self.unregister_touches(&event, |_, _, _| {});
+                if self.pointers.is_empty() {
+                    self.state = DetectionState::Initial;
+                }
+            }
             Msg::PointerLeave(event) => {
                 self.unregister_pointer(event.pointer_id());
                 if self.pointers.is_empty() {
@@ -553,7 +761,10 @@ impl Component for PwtGestureDetector {
     type Properties = GestureDetector;
 
     fn create(_ctx: &Context<Self>) -> Self {
+        let touch_only = window().has_own_property(&JsValue::from_str("ontouchstart"));
+
         Self {
+            touch_only,
             state: DetectionState::Initial,
             pointers: HashMap::new(),
             node_ref: NodeRef::default(),
@@ -575,17 +786,25 @@ impl Component for PwtGestureDetector {
     fn view(&self, ctx: &Context<Self>) -> Html {
         let props = ctx.props();
 
-        Container::new()
+        let mut container = Container::new()
             .node_ref(self.node_ref.clone())
             .class("pwt-d-contents")
             .style("touch-action", "none")
-            .onpointerdown(ctx.link().callback(Msg::PointerDown))
-            .onpointerup(ctx.link().callback(Msg::PointerUp))
-            .onpointermove(ctx.link().callback(Msg::PointerMove))
-            .onpointercancel(ctx.link().callback(Msg::PointerCancel))
-            .onpointerleave(ctx.link().callback(Msg::PointerLeave))
-            .with_child(props.content.clone())
-            .into()
+            .with_child(props.content.clone());
+
+        if self.touch_only {
+            container.add_ontouchstart(ctx.link().callback(Msg::TouchStart));
+            container.add_ontouchmove(ctx.link().callback(Msg::TouchMove));
+            container.add_ontouchcancel(ctx.link().callback(Msg::TouchCancel));
+            container.add_ontouchend(ctx.link().callback(Msg::TouchEnd));
+        } else {
+            container.add_onpointerdown(ctx.link().callback(Msg::PointerDown));
+            container.add_onpointerup(ctx.link().callback(Msg::PointerUp));
+            container.add_onpointermove(ctx.link().callback(Msg::PointerMove));
+            container.add_onpointercancel(ctx.link().callback(Msg::PointerCancel));
+            container.add_onpointerleave(ctx.link().callback(Msg::PointerLeave));
+        }
+        container.into()
     }
 }
 
@@ -612,3 +831,21 @@ fn compute_distance(x1: i32, y1: i32, x2: i32, y2: i32) -> f64 {
 
     (dx * dx + dy * dy).sqrt()
 }
+
+fn for_each_changed_touch<F: FnMut(Touch)>(event: &TouchEvent, mut func: F) {
+    let touch_list = event.changed_touches();
+    for i in 0..touch_list.length() {
+        if let Some(touch) = touch_list.get(i) {
+            func(touch);
+        }
+    }
+}
+
+fn for_each_active_touch<F: FnMut(Touch)>(event: &TouchEvent, mut func: F) {
+    let touch_list = event.touches();
+    for i in 0..touch_list.length() {
+        if let Some(touch) = touch_list.get(i) {
+            func(touch);
+        }
+    }
+}
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 3/7] touch: fab: rename on_click to on_activate
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (5 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 2/7] touch: gesture detector: implement a touch only mode Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 4/7] touch: slidable action: " Dominik Csapak
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

to be consistent with buttons, etc.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/touch/fab.rs      | 18 +++++++++---------
 src/touch/fab_menu.rs | 25 ++++++++++++-------------
 2 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/src/touch/fab.rs b/src/touch/fab.rs
index 1cf06ec..72637c7 100644
--- a/src/touch/fab.rs
+++ b/src/touch/fab.rs
@@ -4,7 +4,7 @@ use yew::html::{IntoEventCallback, IntoPropValue};
 use yew::prelude::*;
 use yew::virtual_dom::{Key, VComp, VNode};
 
-use crate::props::{AsClassesMut, EventSubscriber, WidgetBuilder};
+use crate::props::{AsClassesMut, WidgetBuilder};
 use crate::widget::Button;
 
 /// Favorite action button.
@@ -31,7 +31,7 @@ pub struct Fab {
 
     /// Click callback
     #[prop_or_default]
-    pub on_click: Option<Callback<MouseEvent>>,
+    pub on_activate: Option<Callback<MouseEvent>>,
 }
 
 impl AsClassesMut for Fab {
@@ -104,9 +104,9 @@ impl Fab {
         self.text = text.into_prop_value();
     }
 
-    /// Builder style method to set the on_click callback.
-    pub fn on_click(mut self, cb: impl IntoEventCallback<MouseEvent>) -> Self {
-        self.on_click = cb.into_event_callback();
+    /// Builder style method to set the on_activate callback.
+    pub fn on_activate(mut self, cb: impl IntoEventCallback<MouseEvent>) -> Self {
+        self.on_activate = cb.into_event_callback();
         self
     }
 }
@@ -141,11 +141,11 @@ impl Component for PwtFab {
         button
             .class(class)
             .attribute("style", props.style.clone())
-            .onclick(Callback::from({
-                let on_click = props.on_click.clone();
+            .on_activate(Callback::from({
+                let on_activate = props.on_activate.clone();
                 move |event: MouseEvent| {
-                    if let Some(on_click) = &on_click {
-                        on_click.emit(event);
+                    if let Some(on_activate) = &on_activate {
+                        on_activate.emit(event);
                     }
                 }
             }))
diff --git a/src/touch/fab_menu.rs b/src/touch/fab_menu.rs
index afa48bb..4ad1234 100644
--- a/src/touch/fab_menu.rs
+++ b/src/touch/fab_menu.rs
@@ -204,7 +204,7 @@ impl Component for PwtFabMenu {
 
         let main_button = Fab::new(main_icon_class)
             .class(props.main_button_class.clone())
-            .on_click(ctx.link().callback(|_| Msg::Toggle));
+            .on_activate(ctx.link().callback(|_| Msg::Toggle));
 
         container.add_child(main_button);
 
@@ -213,20 +213,19 @@ impl Component for PwtFabMenu {
                 log::error!("FabMenu only supports 5 child buttons.");
                 break;
             }
-            let orig_on_click = child.on_click.clone();
+            let orig_on_activate = child.on_activate.clone();
             let link = ctx.link().clone();
 
-            let child_button =
-                child
-                    .clone()
-                    .small()
-                    .class("pwt-fab-menu-item")
-                    .on_click(move |event| {
-                        link.send_message(Msg::Toggle);
-                        if let Some(on_click) = &orig_on_click {
-                            on_click.emit(event);
-                        }
-                    });
+            let child_button = child
+                .clone()
+                .small()
+                .class("pwt-fab-menu-item")
+                .on_activate(move |event| {
+                    link.send_message(Msg::Toggle);
+                    if let Some(on_activate) = &orig_on_activate {
+                        on_activate.emit(event);
+                    }
+                });
             container.add_child(child_button);
         }
 
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 4/7] touch: slidable action: rename on_click to on_activate
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (6 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 3/7] touch: fab: rename on_click to on_activate Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 5/7] touch: slidable action: add css classes property Dominik Csapak
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

to be consistent with buttons, etc.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/touch/slidable/slidable_action.rs | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/touch/slidable/slidable_action.rs b/src/touch/slidable/slidable_action.rs
index 263ae0e..de37445 100644
--- a/src/touch/slidable/slidable_action.rs
+++ b/src/touch/slidable/slidable_action.rs
@@ -11,7 +11,7 @@ use super::{SlidableActionMouseEvent, SlidableController};
 
 /// A button which automatically connects to the [SlidableController].
 ///
-/// The [on_click](Self::on_click) handler gets an [SlidableActionMouseEvent] argument,
+/// The [on_activate](Self::on_activate) handler gets an [SlidableActionMouseEvent] argument,
 /// which lets you collapse or dismiss the slidable.
 #[derive(Properties, Clone, PartialEq)]
 pub struct SlidableAction {
@@ -30,7 +30,7 @@ pub struct SlidableAction {
     ///
     /// Emited when the user clicks on the entry.
     #[prop_or_default]
-    pub on_click: Option<Callback<SlidableActionMouseEvent>>,
+    pub on_activate: Option<Callback<SlidableActionMouseEvent>>,
 }
 
 impl SlidableAction {
@@ -51,9 +51,9 @@ impl SlidableAction {
         self.icon_class = Some(icon_class.into());
     }
 
-    /// Builder style method to set the on_click callback.
-    pub fn on_click(mut self, cb: impl IntoEventCallback<SlidableActionMouseEvent>) -> Self {
-        self.on_click = cb.into_event_callback();
+    /// Builder style method to set the on_activate callback.
+    pub fn on_activate(mut self, cb: impl IntoEventCallback<SlidableActionMouseEvent>) -> Self {
+        self.on_activate = cb.into_event_callback();
         self
     }
 }
@@ -102,11 +102,11 @@ impl Component for PwtSlidableAction {
 
         let onclick = Callback::from({
             let controller = self.controller.clone();
-            let on_click = props.on_click.clone();
+            let on_activate = props.on_activate.clone();
             move |event: MouseEvent| {
-                if let Some(on_click) = &on_click {
+                if let Some(on_activate) = &on_activate {
                     let event = SlidableActionMouseEvent::new(event);
-                    on_click.emit(event.clone());
+                    on_activate.emit(event.clone());
                     if let Some(controller) = &controller {
                         if event.get_dismiss() {
                             controller.dismiss();
@@ -115,7 +115,7 @@ impl Component for PwtSlidableAction {
                         }
                     }
                 } else {
-                    // Always collabse if action is without on_click callback
+                    // Always collapse if action is without on_activate callback
                     if let Some(controller) = &controller {
                         controller.collapse();
                     }
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 5/7] touch: slidable action: add css classes property
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (7 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 4/7] touch: slidable action: " Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 6/7] touch: slidable: add on_tap callback Dominik Csapak
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

With this it's possible to add more custom config to such an action,
e.g. a color scheme.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/touch/slidable/slidable_action.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/touch/slidable/slidable_action.rs b/src/touch/slidable/slidable_action.rs
index de37445..7ea54f5 100644
--- a/src/touch/slidable/slidable_action.rs
+++ b/src/touch/slidable/slidable_action.rs
@@ -26,6 +26,10 @@ pub struct SlidableAction {
     #[prop_or_default]
     pub icon_class: Option<Classes>,
 
+    #[prop_or_default]
+    /// Optional additional CSS classes
+    pub class: Classes,
+
     /// Click callback.
     ///
     /// Emited when the user clicks on the entry.
@@ -56,6 +60,17 @@ impl SlidableAction {
         self.on_activate = cb.into_event_callback();
         self
     }
+
+    /// Builder style method to add a CSS class.
+    pub fn class(mut self, class: impl Into<Classes>) -> Self {
+        self.add_class(class);
+        self
+    }
+
+    /// Method to add a CSS class.
+    pub fn add_class(&mut self, class: impl Into<Classes>) {
+        self.class.push(class.into());
+    }
 }
 
 pub enum Msg {
@@ -125,6 +140,7 @@ impl Component for PwtSlidableAction {
 
         Container::new()
             .class("pwt-slidable-action")
+            .class(props.class.clone())
             .with_optional_child(icon)
             .with_child(props.label.clone())
             .onclick(onclick)
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 6/7] touch: slidable: add on_tap callback
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (8 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 5/7] touch: slidable action: add css classes property Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 7/7] touch: slidable: cleanup unnecessary macro use Dominik Csapak
  2025-06-25  6:57 ` [yew-devel] applied: [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dietmar Maurer
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

simply exposing the callback of the same name of the underlying gesture
detector.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/touch/slidable/mod.rs | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/touch/slidable/mod.rs b/src/touch/slidable/mod.rs
index ffcf8c8..6802426 100644
--- a/src/touch/slidable/mod.rs
+++ b/src/touch/slidable/mod.rs
@@ -43,6 +43,12 @@ pub struct Slidable {
     /// Without a callback, dismiss is disabled on slidables without actions.
     #[prop_or_default]
     pub on_dismiss: Option<Callback<()>>,
+
+    #[prop_or_default]
+    /// Tap callback.
+    ///
+    /// Called when the main item is tapped.
+    pub on_tap: Option<Callback<InputEvent>>,
 }
 
 impl Slidable {
@@ -80,6 +86,17 @@ impl Slidable {
         self.on_dismiss = cb.into_event_callback();
         self
     }
+
+    /// Builder style method to set the `on_tap` callback.
+    pub fn on_tap(mut self, cb: impl IntoEventCallback<InputEvent>) -> Self {
+        self.set_on_tap(cb);
+        self
+    }
+
+    /// Method to set the `on_tap` callback.
+    pub fn set_on_tap(&mut self, cb: impl IntoEventCallback<InputEvent>) {
+        self.on_tap = cb.into_event_callback();
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -125,6 +142,7 @@ pub enum Msg {
     ContentResize(f64, f64),
     TransitionEnd,
     Controller(SlidableControllerMsg),
+    OnTap(InputEvent),
 }
 
 impl PwtSlidable {
@@ -320,6 +338,11 @@ impl Component for PwtSlidable {
                     }
                 }
             },
+            Msg::OnTap(pointer_event) => {
+                if let Some(on_tap) = &ctx.props().on_tap {
+                    on_tap.emit(pointer_event);
+                }
+            }
         }
         let pos = self.start_pos - (self.drag_pos.unwrap_or(0) as f64);
         if pos > 0f64 {
@@ -361,6 +384,7 @@ impl Component for PwtSlidable {
         .on_drag_start(ctx.link().callback(Msg::DragStart))
         .on_drag_end(ctx.link().callback(Msg::DragEnd))
         .on_drag_update(ctx.link().callback(Msg::Drag))
+        .on_tap(ctx.link().callback(Msg::OnTap))
         .on_swipe(ctx.link().callback(Msg::Swipe));
 
         let left_container = Container::new()
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] [PATCH yew-widget-toolkit 7/7] touch: slidable: cleanup unnecessary macro use
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (9 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 6/7] touch: slidable: add on_tap callback Dominik Csapak
@ 2025-06-24 12:19 ` Dominik Csapak
  2025-06-25  6:57 ` [yew-devel] applied: [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dietmar Maurer
  11 siblings, 0 replies; 14+ messages in thread
From: Dominik Csapak @ 2025-06-24 12:19 UTC (permalink / raw)
  To: yew-devel

Use `from_widget_props` instead of the yew::props macro to initialize
the Container struct.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/touch/slidable/mod.rs | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/src/touch/slidable/mod.rs b/src/touch/slidable/mod.rs
index 6802426..d8fd2fc 100644
--- a/src/touch/slidable/mod.rs
+++ b/src/touch/slidable/mod.rs
@@ -434,17 +434,14 @@ impl Component for PwtSlidable {
             .with_child(right_container)
             .ontransitionend(ctx.link().callback(|_| Msg::TransitionEnd));
 
-        yew::props!(Container {
-            std_props: props.std_props.clone(),
-            listeners: props.listeners.clone(),
-        })
-        .class("pwt-slidable")
-        .with_child(html! {
-            <ContextProvider<SlidableController> context={self.controller.clone()}>
-                {row}
-            </ContextProvider<SlidableController>>
-        })
-        .into()
+        Container::from_widget_props(props.std_props.clone(), Some(props.listeners.clone()))
+            .class("pwt-slidable")
+            .with_child(html! {
+                <ContextProvider<SlidableController> context={self.controller.clone()}>
+                    {row}
+                </ContextProvider<SlidableController>>
+            })
+            .into()
     }
 
     fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
-- 
2.39.5



_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] applied: [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent`
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent` Dominik Csapak
@ 2025-06-24 16:27   ` Dietmar Maurer
  0 siblings, 0 replies; 14+ messages in thread
From: Dietmar Maurer @ 2025-06-24 16:27 UTC (permalink / raw)
  To: Yew framework devel list at Proxmox, Dominik Csapak

applied


_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [yew-devel] applied: [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes
  2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
                   ` (10 preceding siblings ...)
  2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 7/7] touch: slidable: cleanup unnecessary macro use Dominik Csapak
@ 2025-06-25  6:57 ` Dietmar Maurer
  11 siblings, 0 replies; 14+ messages in thread
From: Dietmar Maurer @ 2025-06-25  6:57 UTC (permalink / raw)
  To: Yew framework devel list at Proxmox, Dominik Csapak

finally applied all patches


_______________________________________________
yew-devel mailing list
yew-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/yew-devel


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2025-06-25  6:56 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-24 12:19 [yew-devel] [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 1/4] slidable: don't add padding on top and bottom to actions Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 2/4] slidable: set background-color to pwt-color-background for actions Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 3/4] color scheme: add `pwt-default-colors` helper Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit-assets 4/4] material: add rounded corners to bottom sheet Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 1/7] touch: gesture detector: introduce `InputEvent` Dominik Csapak
2025-06-24 16:27   ` [yew-devel] applied: " Dietmar Maurer
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 2/7] touch: gesture detector: implement a touch only mode Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 3/7] touch: fab: rename on_click to on_activate Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 4/7] touch: slidable action: " Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 5/7] touch: slidable action: add css classes property Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 6/7] touch: slidable: add on_tap callback Dominik Csapak
2025-06-24 12:19 ` [yew-devel] [PATCH yew-widget-toolkit 7/7] touch: slidable: cleanup unnecessary macro use Dominik Csapak
2025-06-25  6:57 ` [yew-devel] applied: [PATCH yew-widget-toolkit/yew-widget-toolkit-assets 00/11] various touch fixes Dietmar Maurer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal