leptos_motion_gestures/
simplified_gesture_api.rs

1//! Simplified Gesture API
2//!
3//! This module provides a simplified, user-friendly gesture API
4//! that provides a clean interface for gesture handling.
5
6use crate::*;
7use std::time::Instant;
8
9/// Simplified gesture types
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SimplifiedGestureType {
12    /// No gesture detected
13    None,
14    /// Pinch/zoom gesture
15    Pinch,
16    /// Rotation gesture
17    Rotation,
18    /// Pan/drag gesture
19    Pan,
20    /// Multi-touch gesture (combination of pinch, rotation, pan)
21    MultiTouch,
22}
23
24/// Simplified gesture configuration
25#[derive(Debug, Clone, PartialEq)]
26pub struct SimplifiedGestureConfig {
27    /// Maximum number of touches to track
28    pub max_touches: usize,
29    /// Minimum distance for gesture detection
30    pub min_distance: f64,
31    /// Gesture timeout in milliseconds
32    pub timeout: u64,
33    /// Enable pinch gesture detection
34    pub enable_pinch: bool,
35    /// Enable rotation gesture detection
36    pub enable_rotation: bool,
37    /// Enable pan gesture detection
38    pub enable_pan: bool,
39}
40
41/// Simplified gesture result
42#[derive(Debug, Clone, PartialEq)]
43pub struct SimplifiedGestureResult {
44    /// Gesture type
45    pub gesture_type: SimplifiedGestureType,
46    /// Scale factor (for pinch gestures)
47    pub scale: Option<f64>,
48    /// Rotation angle in degrees (for rotation gestures)
49    pub rotation: Option<f64>,
50    /// Translation vector (for pan gestures)
51    pub translation: Option<SimplifiedVector2D>,
52    /// Velocity vector
53    pub velocity: Option<SimplifiedVector2D>,
54    /// Gesture center point
55    pub center: Option<SimplifiedVector2D>,
56    /// Gesture confidence (0.0 to 1.0)
57    pub confidence: f64,
58    /// Gesture duration in milliseconds
59    pub duration: u64,
60}
61
62/// Simplified 2D vector
63#[derive(Debug, Clone, Copy, PartialEq)]
64pub struct SimplifiedVector2D {
65    pub x: f64,
66    pub y: f64,
67}
68
69/// Simplified gesture bounds
70#[derive(Debug, Clone, Copy, PartialEq)]
71pub struct SimplifiedGestureBounds {
72    pub min_x: f64,
73    pub max_x: f64,
74    pub min_y: f64,
75    pub max_y: f64,
76}
77
78/// Simplified gesture data
79#[derive(Debug, Clone, PartialEq)]
80pub struct SimplifiedGestureData {
81    pub gesture_type: SimplifiedGestureType,
82    pub touch_count: usize,
83    pub is_active: bool,
84    pub center: Option<SimplifiedVector2D>,
85    pub bounds: Option<SimplifiedGestureBounds>,
86    pub distance: Option<f64>,
87    pub angle: Option<f64>,
88    pub confidence: f64,
89    pub duration: u64,
90}
91
92/// Simplified gesture detector that provides a clean, simple interface
93///
94/// This is the main public API for gesture detection. It provides
95/// a clean, simple interface while hiding the complexity of the
96/// underlying multi-touch gesture detection system.
97pub struct SimplifiedGestureDetector {
98    /// Internal multi-touch detector (hidden from public API)
99    internal_detector: MultiTouchGestureDetector,
100    /// Current gesture state
101    current_gesture: SimplifiedGestureType,
102    /// Gesture start time
103    gesture_start: Option<Instant>,
104    /// Last gesture result
105    last_result: SimplifiedGestureResult,
106}
107
108impl SimplifiedGestureDetector {
109    /// Create a new simplified gesture detector with default configuration
110    pub fn new() -> Self {
111        Self::with_config(SimplifiedGestureConfig::default())
112    }
113
114    /// Create a new simplified gesture detector with custom configuration
115    pub fn with_config(config: SimplifiedGestureConfig) -> Self {
116        let internal_config = GestureConfig {
117            basic_gestures: true,
118            multi_touch: true,
119            pinch_to_zoom: config.enable_pinch,
120            rotation: config.enable_rotation,
121            sensitivity: 0.5,
122            min_distance: config.min_distance,
123            max_touches: config.max_touches,
124            timeout_ms: config.timeout,
125        };
126
127        Self {
128            internal_detector: MultiTouchGestureDetector::new(internal_config),
129            current_gesture: SimplifiedGestureType::None,
130            gesture_start: None,
131            last_result: SimplifiedGestureResult::default(),
132        }
133    }
134
135    /// Handle touch start event
136    pub fn handle_touch_start(&mut self, touches: Vec<TouchPoint>) -> SimplifiedGestureResult {
137        let result = self.internal_detector.handle_touch_start(touches);
138        self.current_gesture = self.convert_gesture_type(result.gesture_type.clone());
139
140        if self.current_gesture != SimplifiedGestureType::None {
141            self.gesture_start = Some(Instant::now());
142        }
143
144        self.last_result = self.create_simplified_result(result);
145        self.last_result.clone()
146    }
147
148    /// Handle touch move event
149    pub fn handle_touch_move(&mut self, touches: Vec<TouchPoint>) -> SimplifiedGestureResult {
150        let result = self.internal_detector.handle_touch_move(touches);
151        self.current_gesture = self.convert_gesture_type(result.gesture_type.clone());
152
153        self.last_result = self.create_simplified_result(result);
154        self.last_result.clone()
155    }
156
157    /// Handle touch end event
158    pub fn handle_touch_end(&mut self, touch_ids: Vec<u64>) -> SimplifiedGestureResult {
159        // Create empty touch points for the touch IDs that ended
160        let touches: Vec<TouchPoint> = touch_ids
161            .into_iter()
162            .map(|id| TouchPoint {
163                id,
164                x: 0.0,
165                y: 0.0,
166                pressure: 0.0,
167                timestamp: std::time::SystemTime::now()
168                    .duration_since(std::time::UNIX_EPOCH)
169                    .unwrap()
170                    .as_millis() as u64,
171            })
172            .collect();
173
174        let result = self.internal_detector.handle_touch_end(touches);
175        self.current_gesture = SimplifiedGestureType::None;
176        self.gesture_start = None;
177
178        self.last_result = self.create_simplified_result(result);
179        self.last_result.clone()
180    }
181
182    /// Cancel current gesture
183    pub fn cancel(&mut self) {
184        self.internal_detector.reset();
185        self.current_gesture = SimplifiedGestureType::None;
186        self.gesture_start = None;
187        self.last_result = SimplifiedGestureResult::default();
188    }
189
190    /// Reset gesture detector
191    pub fn reset(&mut self) {
192        self.internal_detector.reset();
193        self.current_gesture = SimplifiedGestureType::None;
194        self.gesture_start = None;
195        self.last_result = SimplifiedGestureResult::default();
196    }
197
198    /// Check if a gesture is currently active
199    pub fn is_active(&self) -> bool {
200        self.internal_detector.is_active()
201    }
202
203    /// Get the number of active touches
204    pub fn touch_count(&self) -> usize {
205        self.internal_detector.get_state().touches.len()
206    }
207
208    /// Get the current gesture type
209    pub fn gesture_type(&self) -> SimplifiedGestureType {
210        self.current_gesture
211    }
212
213    /// Get gesture data
214    pub fn get_gesture_data(&self) -> Option<SimplifiedGestureData> {
215        if !self.is_active() {
216            return None;
217        }
218
219        Some(SimplifiedGestureData {
220            gesture_type: self.current_gesture,
221            touch_count: self.touch_count(),
222            is_active: self.is_active(),
223            center: self.get_center(),
224            bounds: self.get_bounds(),
225            distance: self.get_distance(),
226            angle: self.get_angle(),
227            confidence: self.get_confidence(),
228            duration: self.get_duration(),
229        })
230    }
231
232    /// Get gesture confidence
233    pub fn get_confidence(&self) -> f64 {
234        // For now, return a default confidence since it's not available in MultiTouchState
235        0.8
236    }
237
238    /// Get gesture center point
239    pub fn get_center(&self) -> Option<SimplifiedVector2D> {
240        if self.touch_count() < 2 {
241            return None;
242        }
243
244        let state = self.internal_detector.get_state();
245        let touches: Vec<&TouchPoint> = state.touches.values().collect();
246
247        if touches.len() < 2 {
248            return None;
249        }
250
251        let center_x = touches.iter().map(|t| t.x).sum::<f64>() / touches.len() as f64;
252        let center_y = touches.iter().map(|t| t.y).sum::<f64>() / touches.len() as f64;
253
254        Some(SimplifiedVector2D {
255            x: center_x,
256            y: center_y,
257        })
258    }
259
260    /// Get gesture bounds
261    pub fn get_bounds(&self) -> Option<SimplifiedGestureBounds> {
262        if self.touch_count() < 2 {
263            return None;
264        }
265
266        let state = self.internal_detector.get_state();
267        let touches: Vec<&TouchPoint> = state.touches.values().collect();
268
269        if touches.is_empty() {
270            return None;
271        }
272
273        let min_x = touches.iter().map(|t| t.x).fold(f64::INFINITY, f64::min);
274        let max_x = touches
275            .iter()
276            .map(|t| t.x)
277            .fold(f64::NEG_INFINITY, f64::max);
278        let min_y = touches.iter().map(|t| t.y).fold(f64::INFINITY, f64::min);
279        let max_y = touches
280            .iter()
281            .map(|t| t.y)
282            .fold(f64::NEG_INFINITY, f64::max);
283
284        Some(SimplifiedGestureBounds {
285            min_x,
286            max_x,
287            min_y,
288            max_y,
289        })
290    }
291
292    /// Get gesture distance
293    pub fn get_distance(&self) -> Option<f64> {
294        if self.touch_count() < 2 {
295            return None;
296        }
297
298        let state = self.internal_detector.get_state();
299        let touches: Vec<&TouchPoint> = state.touches.values().collect();
300
301        if touches.len() < 2 {
302            return None;
303        }
304
305        let dx = touches[1].x - touches[0].x;
306        let dy = touches[1].y - touches[0].y;
307        Some((dx * dx + dy * dy).sqrt())
308    }
309
310    /// Get gesture angle
311    pub fn get_angle(&self) -> Option<f64> {
312        if self.touch_count() < 2 {
313            return None;
314        }
315
316        let state = self.internal_detector.get_state();
317        let touches: Vec<&TouchPoint> = state.touches.values().collect();
318
319        if touches.len() < 2 {
320            return None;
321        }
322
323        let dx = touches[1].x - touches[0].x;
324        let dy = touches[1].y - touches[0].y;
325        Some(dy.atan2(dx).to_degrees())
326    }
327
328    /// Get gesture duration
329    pub fn get_duration(&self) -> u64 {
330        if let Some(start) = self.gesture_start {
331            start.elapsed().as_millis() as u64
332        } else {
333            0
334        }
335    }
336
337    /// Update configuration
338    pub fn update_config(&mut self, config: SimplifiedGestureConfig) {
339        let internal_config = GestureConfig {
340            basic_gestures: true,
341            multi_touch: true,
342            pinch_to_zoom: config.enable_pinch,
343            rotation: config.enable_rotation,
344            sensitivity: 0.5,
345            min_distance: config.min_distance,
346            max_touches: config.max_touches,
347            timeout_ms: config.timeout,
348        };
349
350        self.internal_detector.update_config(internal_config);
351    }
352
353    /// Get current configuration
354    pub fn get_config(&self) -> SimplifiedGestureConfig {
355        // For now, return default config since we can't access internal config
356        // This could be improved by storing the config in the simplified detector
357        SimplifiedGestureConfig::default()
358    }
359
360    /// Convert internal gesture type to simplified gesture type
361    fn convert_gesture_type(&self, gesture_type: MultiTouchGestureType) -> SimplifiedGestureType {
362        match gesture_type {
363            MultiTouchGestureType::None => SimplifiedGestureType::None,
364            MultiTouchGestureType::Pinch => SimplifiedGestureType::Pinch,
365            MultiTouchGestureType::Rotation => SimplifiedGestureType::Rotation,
366            MultiTouchGestureType::MultiSwipe => SimplifiedGestureType::Pan,
367            MultiTouchGestureType::PinchAndRotate => SimplifiedGestureType::MultiTouch,
368            MultiTouchGestureType::MultiTap => SimplifiedGestureType::None,
369        }
370    }
371
372    /// Create simplified gesture result from internal result
373    fn create_simplified_result(&self, result: GestureResult) -> SimplifiedGestureResult {
374        SimplifiedGestureResult {
375            gesture_type: self.convert_gesture_type(result.gesture_type),
376            scale: None,       // Not available in GestureResult
377            rotation: None,    // Not available in GestureResult
378            translation: None, // Not available in GestureResult
379            velocity: None,    // Not available in GestureResult
380            center: self.get_center(),
381            confidence: result.confidence,
382            duration: self.get_duration(),
383        }
384    }
385}
386
387impl Default for SimplifiedGestureDetector {
388    fn default() -> Self {
389        Self::new()
390    }
391}
392
393impl Clone for SimplifiedGestureDetector {
394    fn clone(&self) -> Self {
395        // Create a new detector with the same configuration
396        let config = self.get_config();
397        Self {
398            internal_detector: MultiTouchGestureDetector::new(GestureConfig {
399                basic_gestures: true,
400                multi_touch: true,
401                pinch_to_zoom: config.enable_pinch,
402                rotation: config.enable_rotation,
403                sensitivity: 0.5,
404                min_distance: config.min_distance,
405                max_touches: config.max_touches,
406                timeout_ms: config.timeout,
407            }),
408            current_gesture: self.current_gesture,
409            gesture_start: self.gesture_start,
410            last_result: self.last_result.clone(),
411        }
412    }
413}
414
415impl std::fmt::Debug for SimplifiedGestureDetector {
416    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
417        f.debug_struct("SimplifiedGestureDetector")
418            .field("is_active", &self.is_active())
419            .field("touch_count", &self.touch_count())
420            .field("gesture_type", &self.gesture_type())
421            .finish()
422    }
423}
424
425impl Default for SimplifiedGestureConfig {
426    fn default() -> Self {
427        Self {
428            max_touches: 5,
429            min_distance: 10.0,
430            timeout: 1000,
431            enable_pinch: true,
432            enable_rotation: true,
433            enable_pan: true,
434        }
435    }
436}
437
438impl SimplifiedGestureConfig {
439    /// Create new simplified gesture config
440    pub fn new() -> Self {
441        Self::default()
442    }
443
444    /// Set maximum number of touches
445    pub fn max_touches(mut self, max_touches: usize) -> Self {
446        self.max_touches = max_touches;
447        self
448    }
449
450    /// Set minimum distance for gesture detection
451    pub fn min_distance(mut self, min_distance: f64) -> Self {
452        self.min_distance = min_distance;
453        self
454    }
455
456    /// Set gesture timeout
457    pub fn timeout(mut self, timeout: u64) -> Self {
458        self.timeout = timeout;
459        self
460    }
461
462    /// Enable pinch gesture detection
463    pub fn enable_pinch(mut self, enable: bool) -> Self {
464        self.enable_pinch = enable;
465        self
466    }
467
468    /// Enable rotation gesture detection
469    pub fn enable_rotation(mut self, enable: bool) -> Self {
470        self.enable_rotation = enable;
471        self
472    }
473
474    /// Enable pan gesture detection
475    pub fn enable_pan(mut self, enable: bool) -> Self {
476        self.enable_pan = enable;
477        self
478    }
479}
480
481impl Default for SimplifiedGestureResult {
482    fn default() -> Self {
483        Self {
484            gesture_type: SimplifiedGestureType::None,
485            scale: None,
486            rotation: None,
487            translation: None,
488            velocity: None,
489            center: None,
490            confidence: 0.0,
491            duration: 0,
492        }
493    }
494}
495
496#[cfg(test)]
497mod tests {
498    use super::*;
499
500    #[test]
501    fn test_simplified_gesture_detector_creation() {
502        let detector = SimplifiedGestureDetector::new();
503        assert!(!detector.is_active());
504        assert_eq!(detector.touch_count(), 0);
505        assert_eq!(detector.gesture_type(), SimplifiedGestureType::None);
506    }
507
508    #[test]
509    fn test_simplified_gesture_detector_with_config() {
510        let config = SimplifiedGestureConfig::new()
511            .max_touches(3)
512            .min_distance(5.0);
513
514        let detector = SimplifiedGestureDetector::with_config(config);
515        assert!(!detector.is_active());
516        assert_eq!(detector.touch_count(), 0);
517    }
518
519    #[test]
520    fn test_simplified_gesture_config_fluent_api() {
521        let config = SimplifiedGestureConfig::new()
522            .max_touches(5)
523            .min_distance(10.0)
524            .timeout(1000)
525            .enable_pinch(true)
526            .enable_rotation(false)
527            .enable_pan(true);
528
529        assert_eq!(config.max_touches, 5);
530        assert_eq!(config.min_distance, 10.0);
531        assert_eq!(config.timeout, 1000);
532        assert!(config.enable_pinch);
533        assert!(!config.enable_rotation);
534        assert!(config.enable_pan);
535    }
536
537    #[test]
538    fn test_simplified_gesture_detector_clone() {
539        let detector1 = SimplifiedGestureDetector::new();
540        let detector2 = detector1.clone();
541
542        assert_eq!(detector1.is_active(), detector2.is_active());
543        assert_eq!(detector1.touch_count(), detector2.touch_count());
544        assert_eq!(detector1.gesture_type(), detector2.gesture_type());
545    }
546
547    #[test]
548    fn test_simplified_gesture_detector_debug() {
549        let detector = SimplifiedGestureDetector::new();
550        let debug_str = format!("{:?}", detector);
551        assert!(debug_str.contains("SimplifiedGestureDetector"));
552        assert!(debug_str.contains("is_active"));
553        assert!(debug_str.contains("touch_count"));
554        assert!(debug_str.contains("gesture_type"));
555    }
556}