Skip to main content

euv_ui/component/touch/hook/
impl.rs

1use crate::*;
2
3/// Implementation of touch point extraction from DOM touch events.
4impl NativeTouchPoint {
5    /// Extracts all active touch points from a `TouchEvent`.
6    ///
7    /// Iterates over the `touches` list of the given `TouchEvent` and
8    /// builds a `Vec<NativeTouchPoint>` with each touch point's
9    /// identifier, viewport coordinates, screen coordinates, page
10    /// coordinates, and offset coordinates relative to the target element.
11    ///
12    /// The offset coordinates (`offset_x`, `offset_y`) are computed by
13    /// subtracting the target element's bounding rect from the touch's
14    /// client coordinates, since the browser `Touch` object does not
15    /// provide `offsetX`/`offsetY` directly.
16    ///
17    /// # Arguments
18    ///
19    /// - `&Event` - The native DOM touch event.
20    ///
21    /// # Returns
22    ///
23    /// - `Vec<NativeTouchPoint>` - All currently active touch points.
24    pub fn extract_all(event: &Event) -> Vec<NativeTouchPoint> {
25        let touches_value: JsValue = Reflect::get(event.as_ref(), &JsValue::from_str("touches"))
26            .ok()
27            .unwrap_or(JsValue::NULL);
28        let touches: Array = touches_value.unchecked_into();
29        let target: JsValue = event
30            .target()
31            .map_or(JsValue::NULL, |event_target: EventTarget| {
32                event_target.into()
33            });
34        let element: Element = target.unchecked_into();
35        let rect: DomRect = element.get_bounding_client_rect();
36        let rect_left: f64 = rect.left();
37        let rect_top: f64 = rect.top();
38        (0..touches.length())
39            .map(|index: u32| {
40                let touch: JsValue = touches.get(index);
41                let identifier: i32 = Reflect::get(&touch, &JsValue::from_str("identifier"))
42                    .ok()
43                    .and_then(|value: JsValue| value.as_f64())
44                    .map(|value: f64| value as i32)
45                    .unwrap_or(0);
46                let client_x: i32 = Reflect::get(&touch, &JsValue::from_str("clientX"))
47                    .ok()
48                    .and_then(|value: JsValue| value.as_f64())
49                    .map(|value: f64| value as i32)
50                    .unwrap_or(0);
51                let client_y: i32 = Reflect::get(&touch, &JsValue::from_str("clientY"))
52                    .ok()
53                    .and_then(|value: JsValue| value.as_f64())
54                    .map(|value: f64| value as i32)
55                    .unwrap_or(0);
56                let screen_x: i32 = Reflect::get(&touch, &JsValue::from_str("screenX"))
57                    .ok()
58                    .and_then(|value: JsValue| value.as_f64())
59                    .map(|value: f64| value as i32)
60                    .unwrap_or(0);
61                let screen_y: i32 = Reflect::get(&touch, &JsValue::from_str("screenY"))
62                    .ok()
63                    .and_then(|value: JsValue| value.as_f64())
64                    .map(|value: f64| value as i32)
65                    .unwrap_or(0);
66                let page_x: i32 = Reflect::get(&touch, &JsValue::from_str("pageX"))
67                    .ok()
68                    .and_then(|value: JsValue| value.as_f64())
69                    .map(|value: f64| value as i32)
70                    .unwrap_or(0);
71                let page_y: i32 = Reflect::get(&touch, &JsValue::from_str("pageY"))
72                    .ok()
73                    .and_then(|value: JsValue| value.as_f64())
74                    .map(|value: f64| value as i32)
75                    .unwrap_or(0);
76                let offset_x: i32 = (client_x as f64 - rect_left).round() as i32;
77                let offset_y: i32 = (client_y as f64 - rect_top).round() as i32;
78                NativeTouchPoint {
79                    identifier,
80                    client_x,
81                    client_y,
82                    screen_x,
83                    screen_y,
84                    offset_x,
85                    offset_y,
86                    page_x,
87                    page_y,
88                }
89            })
90            .collect()
91    }
92
93    /// Extracts the changed touch points from a `TouchEvent`.
94    ///
95    /// The `changedTouches` list contains touch points that have changed
96    /// since the last touch event:
97    /// - For `touchstart` - newly added touch points.
98    /// - For `touchmove` - touch points that have moved.
99    /// - For `touchend` / `touchcancel` - removed touch points.
100    ///
101    /// This is useful for determining which specific fingers were lifted
102    /// in a `touchend` event, since the `touches` list no longer contains
103    /// them.
104    ///
105    /// # Arguments
106    ///
107    /// - `&Event` - The native DOM touch event.
108    ///
109    /// # Returns
110    ///
111    /// - `Vec<NativeTouchPoint>` - The touch points that changed in this event.
112    pub fn extract_changed(event: &Event) -> Vec<NativeTouchPoint> {
113        let touches_value: JsValue =
114            Reflect::get(event.as_ref(), &JsValue::from_str("changedTouches"))
115                .ok()
116                .unwrap_or(JsValue::NULL);
117        let touches: Array = touches_value.unchecked_into();
118        let target: JsValue = event
119            .target()
120            .map_or(JsValue::NULL, |event_target: EventTarget| {
121                event_target.into()
122            });
123        let element: Element = target.unchecked_into();
124        let rect: DomRect = element.get_bounding_client_rect();
125        let rect_left: f64 = rect.left();
126        let rect_top: f64 = rect.top();
127        (0..touches.length())
128            .map(|index: u32| {
129                let touch: JsValue = touches.get(index);
130                let identifier: i32 = Reflect::get(&touch, &JsValue::from_str("identifier"))
131                    .ok()
132                    .and_then(|value: JsValue| value.as_f64())
133                    .map(|value: f64| value as i32)
134                    .unwrap_or(0);
135                let client_x: i32 = Reflect::get(&touch, &JsValue::from_str("clientX"))
136                    .ok()
137                    .and_then(|value: JsValue| value.as_f64())
138                    .map(|value: f64| value as i32)
139                    .unwrap_or(0);
140                let client_y: i32 = Reflect::get(&touch, &JsValue::from_str("clientY"))
141                    .ok()
142                    .and_then(|value: JsValue| value.as_f64())
143                    .map(|value: f64| value as i32)
144                    .unwrap_or(0);
145                let screen_x: i32 = Reflect::get(&touch, &JsValue::from_str("screenX"))
146                    .ok()
147                    .and_then(|value: JsValue| value.as_f64())
148                    .map(|value: f64| value as i32)
149                    .unwrap_or(0);
150                let screen_y: i32 = Reflect::get(&touch, &JsValue::from_str("screenY"))
151                    .ok()
152                    .and_then(|value: JsValue| value.as_f64())
153                    .map(|value: f64| value as i32)
154                    .unwrap_or(0);
155                let page_x: i32 = Reflect::get(&touch, &JsValue::from_str("pageX"))
156                    .ok()
157                    .and_then(|value: JsValue| value.as_f64())
158                    .map(|value: f64| value as i32)
159                    .unwrap_or(0);
160                let page_y: i32 = Reflect::get(&touch, &JsValue::from_str("pageY"))
161                    .ok()
162                    .and_then(|value: JsValue| value.as_f64())
163                    .map(|value: f64| value as i32)
164                    .unwrap_or(0);
165                let offset_x: i32 = (client_x as f64 - rect_left).round() as i32;
166                let offset_y: i32 = (client_y as f64 - rect_top).round() as i32;
167                NativeTouchPoint {
168                    identifier,
169                    client_x,
170                    client_y,
171                    screen_x,
172                    screen_y,
173                    offset_x,
174                    offset_y,
175                    page_x,
176                    page_y,
177                }
178            })
179            .collect()
180    }
181}
182
183/// Implementation of high-precision touch point extraction from DOM touch events.
184impl NativeTouchPointF64 {
185    /// Extracts all active touch points with high-precision `f64` offset coordinates
186    /// from a `TouchEvent`.
187    ///
188    /// Similar to `NativeTouchPoint::extract_all`, but returns `f64` precision for
189    /// offset/client coordinates, which is essential for canvas drawing
190    /// and other pixel-precise interactions.
191    ///
192    /// # Arguments
193    ///
194    /// - `&Event` - The native DOM touch event.
195    ///
196    /// # Returns
197    ///
198    /// - `Vec<NativeTouchPointF64>` - All currently active touch points with `f64` coordinates.
199    pub fn extract_all(event: &Event) -> Vec<NativeTouchPointF64> {
200        let touches_value: JsValue = Reflect::get(event.as_ref(), &JsValue::from_str("touches"))
201            .ok()
202            .unwrap_or(JsValue::NULL);
203        let touches: Array = touches_value.unchecked_into();
204        let target: JsValue = event
205            .target()
206            .map_or(JsValue::NULL, |event_target: EventTarget| {
207                event_target.into()
208            });
209        let element: Element = target.unchecked_into();
210        let rect: DomRect = element.get_bounding_client_rect();
211        let rect_left: f64 = rect.left();
212        let rect_top: f64 = rect.top();
213        (0..touches.length())
214            .map(|index: u32| {
215                let touch: JsValue = touches.get(index);
216                let identifier: i32 = Reflect::get(&touch, &JsValue::from_str("identifier"))
217                    .ok()
218                    .and_then(|value: JsValue| value.as_f64())
219                    .map(|value: f64| value as i32)
220                    .unwrap_or(0);
221                let client_x: f64 = Reflect::get(&touch, &JsValue::from_str("clientX"))
222                    .ok()
223                    .and_then(|value: JsValue| value.as_f64())
224                    .unwrap_or(0.0);
225                let client_y: f64 = Reflect::get(&touch, &JsValue::from_str("clientY"))
226                    .ok()
227                    .and_then(|value: JsValue| value.as_f64())
228                    .unwrap_or(0.0);
229                let screen_x: f64 = Reflect::get(&touch, &JsValue::from_str("screenX"))
230                    .ok()
231                    .and_then(|value: JsValue| value.as_f64())
232                    .unwrap_or(0.0);
233                let screen_y: f64 = Reflect::get(&touch, &JsValue::from_str("screenY"))
234                    .ok()
235                    .and_then(|value: JsValue| value.as_f64())
236                    .unwrap_or(0.0);
237                let page_x: f64 = Reflect::get(&touch, &JsValue::from_str("pageX"))
238                    .ok()
239                    .and_then(|value: JsValue| value.as_f64())
240                    .unwrap_or(0.0);
241                let page_y: f64 = Reflect::get(&touch, &JsValue::from_str("pageY"))
242                    .ok()
243                    .and_then(|value: JsValue| value.as_f64())
244                    .unwrap_or(0.0);
245                let offset_x: f64 = client_x - rect_left;
246                let offset_y: f64 = client_y - rect_top;
247                NativeTouchPointF64 {
248                    identifier,
249                    client_x,
250                    client_y,
251                    screen_x,
252                    screen_y,
253                    offset_x,
254                    offset_y,
255                    page_x,
256                    page_y,
257                }
258            })
259            .collect()
260    }
261}