tessera_ui_basic_components/
ripple_state.rs

1use std::sync::atomic;
2
3/// State for managing ripple animation and hover effects
4pub struct RippleState {
5    pub is_animating: atomic::AtomicBool,
6    pub start_time: atomic::AtomicU64, // Store as u64 millis since epoch
7    pub click_pos_x: atomic::AtomicI32, // Store as fixed-point * 1000
8    pub click_pos_y: atomic::AtomicI32, // Store as fixed-point * 1000
9    pub is_hovered: atomic::AtomicBool, // Track hover state
10}
11
12impl Default for RippleState {
13    fn default() -> Self {
14        Self::new()
15    }
16}
17
18impl RippleState {
19    pub fn new() -> Self {
20        Self {
21            is_animating: atomic::AtomicBool::new(false),
22            start_time: atomic::AtomicU64::new(0),
23            click_pos_x: atomic::AtomicI32::new(0),
24            click_pos_y: atomic::AtomicI32::new(0),
25            is_hovered: atomic::AtomicBool::new(false),
26        }
27    }
28
29    pub fn start_animation(&self, click_pos: [f32; 2]) {
30        let now = std::time::SystemTime::now()
31            .duration_since(std::time::UNIX_EPOCH)
32            .unwrap()
33            .as_millis() as u64;
34
35        self.start_time.store(now, atomic::Ordering::SeqCst);
36        self.click_pos_x
37            .store((click_pos[0] * 1000.0) as i32, atomic::Ordering::SeqCst);
38        self.click_pos_y
39            .store((click_pos[1] * 1000.0) as i32, atomic::Ordering::SeqCst);
40        self.is_animating.store(true, atomic::Ordering::SeqCst);
41    }
42
43    pub fn get_animation_progress(&self) -> Option<(f32, [f32; 2])> {
44        let is_animating = self.is_animating.load(atomic::Ordering::SeqCst);
45
46        if !is_animating {
47            return None;
48        }
49
50        let now = std::time::SystemTime::now()
51            .duration_since(std::time::UNIX_EPOCH)
52            .unwrap()
53            .as_millis() as u64;
54        let start = self.start_time.load(atomic::Ordering::SeqCst);
55        let elapsed_ms = now.saturating_sub(start);
56        let progress = (elapsed_ms as f32) / 600.0; // 600ms animation
57
58        if progress >= 1.0 {
59            self.is_animating.store(false, atomic::Ordering::SeqCst);
60            return None;
61        }
62
63        let click_pos = [
64            self.click_pos_x.load(atomic::Ordering::SeqCst) as f32 / 1000.0,
65            self.click_pos_y.load(atomic::Ordering::SeqCst) as f32 / 1000.0,
66        ];
67
68        Some((progress, click_pos))
69    }
70
71    /// Set hover state
72    pub fn set_hovered(&self, hovered: bool) {
73        self.is_hovered.store(hovered, atomic::Ordering::SeqCst);
74    }
75
76    /// Get hover state
77    pub fn is_hovered(&self) -> bool {
78        self.is_hovered.load(atomic::Ordering::SeqCst)
79    }
80}