Skip to main content

euv_ui/component/touch/hook/
struct.rs

1use super::*;
2
3/// Represents a single touch point from a multi-touch event.
4///
5/// Each `NativeTouchPoint` corresponds to one finger or stylus currently
6/// touching the screen. The `identifier` field distinguishes between
7/// simultaneous touch points, enabling multi-finger gesture tracking.
8#[derive(Clone, Data, Debug, Default, Eq, PartialEq)]
9pub struct NativeTouchPoint {
10    /// A unique identifier for this touch point.
11    ///
12    /// The browser assigns a distinct `identifier` to each active touch.
13    /// It remains constant for the duration of the touch (from
14    /// `touchstart` to `touchend`), allowing the same finger to be
15    /// tracked across `touchmove` events.
16    #[get(type(copy))]
17    pub identifier: i32,
18    /// The X coordinate of the touch relative to the viewport.
19    #[get(type(copy))]
20    pub client_x: i32,
21    /// The Y coordinate of the touch relative to the viewport.
22    #[get(type(copy))]
23    pub client_y: i32,
24    /// The X coordinate of the touch relative to the screen.
25    #[get(type(copy))]
26    pub screen_x: i32,
27    /// The Y coordinate of the touch relative to the screen.
28    #[get(type(copy))]
29    pub screen_y: i32,
30    /// The X coordinate of the touch relative to the target element.
31    #[get(type(copy))]
32    pub offset_x: i32,
33    /// The Y coordinate of the touch relative to the target element.
34    #[get(type(copy))]
35    pub offset_y: i32,
36    /// The X coordinate of the touch relative to the page.
37    #[get(type(copy))]
38    pub page_x: i32,
39    /// The Y coordinate of the touch relative to the page.
40    #[get(type(copy))]
41    pub page_y: i32,
42}
43
44/// Represents a single touch point with high-precision `f64` coordinates.
45///
46/// Used for pixel-precise interactions such as canvas drawing, where
47/// sub-pixel accuracy matters. The `identifier` field distinguishes
48/// between simultaneous touch points, enabling multi-finger gesture
49/// tracking.
50#[derive(Clone, Data, Debug, Default, PartialEq)]
51pub struct NativeTouchPointF64 {
52    /// A unique identifier for this touch point.
53    #[get(type(copy))]
54    pub identifier: i32,
55    /// The X coordinate of the touch relative to the viewport.
56    #[get(type(copy))]
57    pub client_x: f64,
58    /// The Y coordinate of the touch relative to the viewport.
59    #[get(type(copy))]
60    pub client_y: f64,
61    /// The X coordinate of the touch relative to the screen.
62    #[get(type(copy))]
63    pub screen_x: f64,
64    /// The Y coordinate of the touch relative to the screen.
65    #[get(type(copy))]
66    pub screen_y: f64,
67    /// The X coordinate of the touch relative to the target element.
68    #[get(type(copy))]
69    pub offset_x: f64,
70    /// The Y coordinate of the touch relative to the target element.
71    #[get(type(copy))]
72    pub offset_y: f64,
73    /// The X coordinate of the touch relative to the page.
74    #[get(type(copy))]
75    pub page_x: f64,
76    /// The Y coordinate of the touch relative to the page.
77    #[get(type(copy))]
78    pub page_y: f64,
79}
80
81/// A two-finger pinch described by the distance and midpoint of the two
82/// touch points.
83///
84/// The pair is emitted on every `touchmove` while exactly two fingers are
85/// down, so consumers can drive a continuous zoom rather than reacting to a
86/// discrete end-of-gesture event.
87#[derive(Clone, Copy, Data, Debug, Default, PartialEq)]
88pub struct EuvPinch {
89    /// Distance in CSS pixels between the two touch points.
90    #[get(type(copy))]
91    pub distance: f64,
92    /// Distance in CSS pixels when the pinch started, or the previous
93    /// distance when continuing an in-progress pinch.
94    #[get(type(copy))]
95    pub start_distance: f64,
96    /// Midpoint X in client coordinates.
97    #[get(type(copy))]
98    pub center_x: f64,
99    /// Midpoint Y in client coordinates.
100    #[get(type(copy))]
101    pub center_y: f64,
102    /// Change in distance since `start_distance`.
103    ///
104    /// Positive values mean the fingers spread apart (zoom in), negative
105    /// values mean they moved together (zoom out).
106    #[get(type(copy))]
107    pub delta: f64,
108}
109
110/// A single-finger drag described by the current position and the movement
111/// accumulated since the drag started.
112#[derive(Clone, Copy, Data, Debug, Default, PartialEq)]
113pub struct EuvDrag {
114    /// Current X in client coordinates.
115    #[get(type(copy))]
116    pub x: f64,
117    /// Current Y in client coordinates.
118    #[get(type(copy))]
119    pub y: f64,
120    /// X displacement since the drag started.
121    #[get(type(copy))]
122    pub delta_x: f64,
123    /// Y displacement since the drag started.
124    #[get(type(copy))]
125    pub delta_y: f64,
126    /// Total path length in CSS pixels, not the straight-line distance.
127    ///
128    /// A finger that traces a loop has a large path length and zero delta,
129    /// which is how a drag is told apart from a tap that wandered slightly.
130    #[get(type(copy))]
131    #[get(type(copy))]
132    pub travel: f64,
133}
134
135/// Tunable thresholds for [`EuvGestureRecognizer`].
136///
137/// Every field is public so a caller can tighten recognition for a
138/// carousel without affecting the default used elsewhere.
139#[derive(Clone, Copy, Data, Debug, PartialEq)]
140pub struct EuvGestureConfig {
141    /// Minimum horizontal or vertical travel, in CSS pixels, before a touch
142    /// is classified as a swipe instead of a tap.
143    #[get(type(copy))]
144    pub swipe_threshold: f64,
145    /// Maximum travel, in CSS pixels, for a touch to still count as a tap
146    /// or long press.
147    #[get(type(copy))]
148    pub tap_slop: f64,
149    /// Duration, in milliseconds, that turns a stationary touch into a long
150    /// press.
151    #[get(type(copy))]
152    pub long_press_millis: f64,
153    /// Minimum relative scale change before a pinch step is reported as
154    /// meaningful. Suppresses sub-percent jitter between adjacent moves.
155    #[get(type(copy))]
156    pub pinch_threshold: f64,
157}
158
159/// Live gesture state produced by [`EuvGestureRecognizer::use_gesture`].
160///
161/// The three continuous signals are always mounted. The discrete
162/// `last_gesture` signal only changes when a gesture completes, so a view
163/// that renders it does not re-render on every `touchmove`.
164#[derive(Clone, Data, New)]
165pub struct EuvGestureState {
166    /// The most recently completed single-finger gesture, or `None` when no
167    /// gesture has completed since the hook was mounted.
168    #[get(type(copy))]
169    pub last_gesture: Signal<Option<EuvGesture>>,
170    /// The in-progress drag, or `None` when no finger is dragging.
171    #[get(type(copy))]
172    pub drag: Signal<Option<EuvDrag>>,
173    /// The in-progress two-finger pinch, or `None` when fewer than two
174    /// fingers are down.
175    #[get(type(copy))]
176    pub pinch: Signal<Option<EuvPinch>>,
177    /// Handler for `ontouchstart`; begins a new gesture sequence.
178    pub on_start: Option<Rc<dyn Fn(Event)>>,
179    /// Handler for `ontouchmove`; updates the drag and pinch signals.
180    pub on_move: Option<Rc<dyn Fn(Event)>>,
181    /// Handler for `ontouchend`; completes the gesture.
182    pub on_end: Option<Rc<dyn Fn(Event)>>,
183    /// Handler for `ontouchcancel`; abandons the gesture.
184    pub on_cancel: Option<Rc<dyn Fn(Event)>>,
185}
186
187/// Stateless gesture classifier driven by [`EuvGestureState`].
188///
189/// The recognizer holds only the configured thresholds; all mutable
190/// per-gesture progress lives in signals owned by the hook, so a view can
191/// read the latest result without the classifier itself being reactive.
192///
193/// # Examples
194///
195/// ```
196/// use euv_ui::EuvGestureRecognizer;
197///
198/// let recognizer: EuvGestureRecognizer = EuvGestureRecognizer::new();
199/// assert_eq!(recognizer.classify(120.0, 5.0, 80.0, 120.5), Some(euv_ui::EuvGesture::Right));
200/// ```
201#[derive(Clone, Copy, Data)]
202pub struct EuvGestureRecognizer {
203    /// The thresholds applied to every classification decision.
204    #[get(type(copy))]
205    pub config: EuvGestureConfig,
206}
207
208/// Per-touch progress for one in-flight gesture sequence.
209///
210/// This is deliberately not a signal: it changes on every `touchmove` and
211/// nothing in a view should re-render until a gesture actually completes.
212#[derive(Clone, Copy, Data, Debug, Default)]
213pub struct GestureProgress {
214    /// X of the first finger when the sequence began.
215    #[get(type(copy))]
216    pub start_x: f64,
217    /// Y of the first finger when the sequence began.
218    #[get(type(copy))]
219    pub start_y: f64,
220    /// X of the most recently seen position of the first finger.
221    #[get(type(copy))]
222    pub last_x: f64,
223    /// Y of the most recently seen position of the first finger.
224    #[get(type(copy))]
225    pub last_y: f64,
226    /// Accumulated path length in CSS pixels.
227    pub travel: f64,
228    /// Distance between the two fingers when a pinch began.
229    #[get(type(copy))]
230    pub pinch_start_distance: f64,
231    /// Timestamp of `touchstart`, in milliseconds.
232    #[get(type(copy))]
233    pub started_at: f64,
234    /// Whether a sequence is currently being tracked.
235    #[get(type(copy))]
236    pub active: bool,
237}
238
239/// The live position of the tracked finger, in client coordinates.
240///
241/// A plain value type rather than a borrow of the event's touch list, so the
242/// recognizer can be exercised without constructing DOM events.
243#[derive(Clone, Copy, Data, Debug, Default, PartialEq)]
244pub struct GesturePoint {
245    /// X in client coordinates.
246    #[get(type(copy))]
247    pub client_x: f64,
248    /// Y in client coordinates.
249    #[get(type(copy))]
250    pub client_y: f64,
251}