cotis-utils 0.1.0-alpha

Modular Rust UI framework core
Documentation
use crate::math::Vector2;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};

/// Mouse button identifiers used by Cotis backends.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum MouseButton {
    MouseButtonLeft = 0,
    MouseButtonRight = 1,
    MouseButtonMiddle = 2,
    MouseButtonSide = 3,
    MouseButtonExtra = 4,
    MouseButtonForward = 5,
    MouseButtonBack = 6,
}

/// Full mouse button list for polling-based integrations.
pub const MOUSE_BUTTON_LIST: [MouseButton; 7] = [
    MouseButton::MouseButtonLeft,
    MouseButton::MouseButtonRight,
    MouseButton::MouseButtonMiddle,
    MouseButton::MouseButtonSide,
    MouseButton::MouseButtonExtra,
    MouseButton::MouseButtonForward,
    MouseButton::MouseButtonBack,
];

/// Provides current-frame mouse state.
pub trait MouseProvider {
    /// Returns mouse position in pixel coordinates.
    fn get_mouse_position(&self) -> Vector2;

    /// Returns whether the button is currently down.
    fn mouse_button_down(&self, button: MouseButton) -> bool;
    /// Returns mouse wheel delta for the current frame.
    ///
    /// By convention, `x` is horizontal scroll and `y` is vertical scroll.
    fn get_mouse_wheel_move_v(&self) -> Vector2;
}

/// Delegates mouse access to the inner provider behind a [`Mutex`].
///
/// # Panics
///
/// Panics if the mutex is poisoned.
impl<T: MouseProvider> MouseProvider for Mutex<T> {
    fn get_mouse_position(&self) -> Vector2 {
        self.lock().unwrap().get_mouse_position()
    }

    fn mouse_button_down(&self, button: MouseButton) -> bool {
        self.lock().unwrap().mouse_button_down(button)
    }

    fn get_mouse_wheel_move_v(&self) -> Vector2 {
        self.lock().unwrap().get_mouse_wheel_move_v()
    }
}

/// Delegates mouse access to the inner provider behind an [`Arc`].
impl<T: MouseProvider> MouseProvider for Arc<T> {
    fn get_mouse_position(&self) -> Vector2 {
        (**self).get_mouse_position()
    }

    fn mouse_button_down(&self, button: MouseButton) -> bool {
        (**self).mouse_button_down(button)
    }

    fn get_mouse_wheel_move_v(&self) -> Vector2 {
        (**self).get_mouse_wheel_move_v()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::{Arc, Mutex};

    struct MockMouse {
        position: Vector2,
        left_down: bool,
        wheel: Vector2,
    }

    impl MouseProvider for MockMouse {
        fn get_mouse_position(&self) -> Vector2 {
            self.position
        }

        fn mouse_button_down(&self, button: MouseButton) -> bool {
            button == MouseButton::MouseButtonLeft && self.left_down
        }

        fn get_mouse_wheel_move_v(&self) -> Vector2 {
            self.wheel
        }
    }

    #[test]
    fn mutex_and_arc_delegate_mouse_provider() {
        let mock = MockMouse {
            position: Vector2::new(12.0, 34.0),
            left_down: true,
            wheel: Vector2::new(0.0, 2.5),
        };

        let mutex = Mutex::new(mock);
        assert_eq!(mutex.get_mouse_position().x, 12.0);
        assert!(mutex.mouse_button_down(MouseButton::MouseButtonLeft));
        assert_eq!(mutex.get_mouse_wheel_move_v().y, 2.5);

        let arc = Arc::new(MockMouse {
            position: Vector2::new(1.0, 2.0),
            left_down: false,
            wheel: Vector2::new(-1.0, 0.0),
        });
        assert_eq!(arc.get_mouse_position().y, 2.0);
        assert!(!arc.mouse_button_down(MouseButton::MouseButtonLeft));
        assert_eq!(arc.get_mouse_wheel_move_v().x, -1.0);
    }
}