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