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}