Skip to main content

cotis_utils/interactivity/
mouse.rs

1use crate::math::Vector2;
2use std::fmt::Debug;
3use std::sync::{Arc, Mutex};
4
5/// Mouse button identifiers used by Cotis backends.
6#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
7pub enum MouseButton {
8    MouseButtonLeft = 0,
9    MouseButtonRight = 1,
10    MouseButtonMiddle = 2,
11    MouseButtonSide = 3,
12    MouseButtonExtra = 4,
13    MouseButtonForward = 5,
14    MouseButtonBack = 6,
15}
16
17/// Full mouse button list for polling-based integrations.
18pub const MOUSE_BUTTON_LIST: [MouseButton; 7] = [
19    MouseButton::MouseButtonLeft,
20    MouseButton::MouseButtonRight,
21    MouseButton::MouseButtonMiddle,
22    MouseButton::MouseButtonSide,
23    MouseButton::MouseButtonExtra,
24    MouseButton::MouseButtonForward,
25    MouseButton::MouseButtonBack,
26];
27
28/// Provides current-frame mouse state.
29pub trait MouseProvider {
30    /// Returns mouse position in pixel coordinates.
31    fn get_mouse_position(&self) -> Vector2;
32
33    /// Returns whether the button is currently down.
34    fn mouse_button_down(&self, button: MouseButton) -> bool;
35    /// Returns mouse wheel delta for the current frame.
36    ///
37    /// By convention, `x` is horizontal scroll and `y` is vertical scroll.
38    fn get_mouse_wheel_move_v(&self) -> Vector2;
39}
40
41/// Delegates mouse access to the inner provider behind a [`Mutex`].
42///
43/// # Panics
44///
45/// Panics if the mutex is poisoned.
46impl<T: MouseProvider> MouseProvider for Mutex<T> {
47    fn get_mouse_position(&self) -> Vector2 {
48        self.lock().unwrap().get_mouse_position()
49    }
50
51    fn mouse_button_down(&self, button: MouseButton) -> bool {
52        self.lock().unwrap().mouse_button_down(button)
53    }
54
55    fn get_mouse_wheel_move_v(&self) -> Vector2 {
56        self.lock().unwrap().get_mouse_wheel_move_v()
57    }
58}
59
60/// Delegates mouse access to the inner provider behind an [`Arc`].
61impl<T: MouseProvider> MouseProvider for Arc<T> {
62    fn get_mouse_position(&self) -> Vector2 {
63        (**self).get_mouse_position()
64    }
65
66    fn mouse_button_down(&self, button: MouseButton) -> bool {
67        (**self).mouse_button_down(button)
68    }
69
70    fn get_mouse_wheel_move_v(&self) -> Vector2 {
71        (**self).get_mouse_wheel_move_v()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use std::sync::{Arc, Mutex};
79
80    struct MockMouse {
81        position: Vector2,
82        left_down: bool,
83        wheel: Vector2,
84    }
85
86    impl MouseProvider for MockMouse {
87        fn get_mouse_position(&self) -> Vector2 {
88            self.position
89        }
90
91        fn mouse_button_down(&self, button: MouseButton) -> bool {
92            button == MouseButton::MouseButtonLeft && self.left_down
93        }
94
95        fn get_mouse_wheel_move_v(&self) -> Vector2 {
96            self.wheel
97        }
98    }
99
100    #[test]
101    fn mutex_and_arc_delegate_mouse_provider() {
102        let mock = MockMouse {
103            position: Vector2::new(12.0, 34.0),
104            left_down: true,
105            wheel: Vector2::new(0.0, 2.5),
106        };
107
108        let mutex = Mutex::new(mock);
109        assert_eq!(mutex.get_mouse_position().x, 12.0);
110        assert!(mutex.mouse_button_down(MouseButton::MouseButtonLeft));
111        assert_eq!(mutex.get_mouse_wheel_move_v().y, 2.5);
112
113        let arc = Arc::new(MockMouse {
114            position: Vector2::new(1.0, 2.0),
115            left_down: false,
116            wheel: Vector2::new(-1.0, 0.0),
117        });
118        assert_eq!(arc.get_mouse_position().y, 2.0);
119        assert!(!arc.mouse_button_down(MouseButton::MouseButtonLeft));
120        assert_eq!(arc.get_mouse_wheel_move_v().x, -1.0);
121    }
122}