dreamwell-engine 1.0.0

Dreamwell pure-logic engine library — transforms, hierarchy, canon pipeline, spatial math, hashing, tile rules, validation, waymark schema, material/lighting descriptors. No SpacetimeDB dependency.
Documentation
// ButtonState<T> — generic three-state input tracker.
// Adapted from Bevy's ButtonInput<T>. Tracks pressed, just_pressed, just_released per frame.

use std::collections::HashSet;
use std::hash::Hash;

/// Generic button state tracker. Tracks current, just-pressed, and just-released states.
/// `T` is a key/button identifier (e.g., `VirtualKey`, `MouseButton`).
///
/// Usage: call `press()`/`release()` when events arrive, query `pressed()`/`just_pressed()`/
/// `just_released()`, then call `clear_just()` at end of frame.
pub struct ButtonState<T: Eq + Hash + Copy> {
    pressed: HashSet<T>,
    just_pressed: HashSet<T>,
    just_released: HashSet<T>,
}

impl<T: Eq + Hash + Copy> ButtonState<T> {
    pub fn new() -> Self {
        Self {
            pressed: HashSet::new(),
            just_pressed: HashSet::new(),
            just_released: HashSet::new(),
        }
    }

    /// Register a button press. Sets pressed + just_pressed.
    pub fn press(&mut self, button: T) {
        if self.pressed.insert(button) {
            self.just_pressed.insert(button);
        }
    }

    /// Register a button release. Clears pressed, sets just_released.
    pub fn release(&mut self, button: T) {
        if self.pressed.remove(&button) {
            self.just_released.insert(button);
        }
    }

    /// Whether the button is currently held down.
    pub fn pressed(&self, button: T) -> bool {
        self.pressed.contains(&button)
    }

    /// Whether the button was pressed this frame.
    pub fn just_pressed(&self, button: T) -> bool {
        self.just_pressed.contains(&button)
    }

    /// Whether the button was released this frame.
    pub fn just_released(&self, button: T) -> bool {
        self.just_released.contains(&button)
    }

    /// Clear just_pressed and just_released. Call at end of frame.
    pub fn clear_just(&mut self) {
        self.just_pressed.clear();
        self.just_released.clear();
    }

    /// Reset all state.
    pub fn reset(&mut self) {
        self.pressed.clear();
        self.just_pressed.clear();
        self.just_released.clear();
    }

    /// All currently pressed buttons.
    pub fn get_pressed(&self) -> impl Iterator<Item = &T> {
        self.pressed.iter()
    }

    /// All buttons just pressed this frame.
    pub fn get_just_pressed(&self) -> impl Iterator<Item = &T> {
        self.just_pressed.iter()
    }

    /// All buttons just released this frame.
    pub fn get_just_released(&self) -> impl Iterator<Item = &T> {
        self.just_released.iter()
    }

    /// Number of currently pressed buttons.
    pub fn pressed_count(&self) -> usize {
        self.pressed.len()
    }

    /// Whether any button is pressed.
    pub fn any_pressed(&self) -> bool {
        !self.pressed.is_empty()
    }

    /// Whether any button was just pressed this frame.
    pub fn any_just_pressed(&self) -> bool {
        !self.just_pressed.is_empty()
    }
}

impl<T: Eq + Hash + Copy> Default for ButtonState<T> {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    enum TestButton {
        A,
        B,
    }

    #[test]
    fn press_and_query() {
        let mut state = ButtonState::new();
        state.press(TestButton::A);

        assert!(state.pressed(TestButton::A));
        assert!(state.just_pressed(TestButton::A));
        assert!(!state.just_released(TestButton::A));
        assert!(!state.pressed(TestButton::B));
    }

    #[test]
    fn release_and_query() {
        let mut state = ButtonState::new();
        state.press(TestButton::A);
        state.clear_just();
        state.release(TestButton::A);

        assert!(!state.pressed(TestButton::A));
        assert!(state.just_released(TestButton::A));
        assert!(!state.just_pressed(TestButton::A));
    }

    #[test]
    fn clear_just_resets_transient() {
        let mut state = ButtonState::new();
        state.press(TestButton::A);
        assert!(state.just_pressed(TestButton::A));

        state.clear_just();
        assert!(state.pressed(TestButton::A));
        assert!(!state.just_pressed(TestButton::A));
    }

    #[test]
    fn double_press_no_duplicate_just() {
        let mut state = ButtonState::new();
        state.press(TestButton::A);
        state.press(TestButton::A); // already pressed
        assert!(state.pressed(TestButton::A));
        assert_eq!(state.pressed_count(), 1);
    }

    #[test]
    fn release_without_press_noop() {
        let mut state = ButtonState::new();
        state.release(TestButton::A);
        assert!(!state.just_released(TestButton::A));
    }

    #[test]
    fn reset_clears_everything() {
        let mut state = ButtonState::new();
        state.press(TestButton::A);
        state.press(TestButton::B);
        state.reset();

        assert!(!state.pressed(TestButton::A));
        assert!(!state.pressed(TestButton::B));
        assert!(!state.any_pressed());
        assert!(!state.any_just_pressed());
    }

    #[test]
    fn multiple_buttons() {
        let mut state = ButtonState::new();
        state.press(TestButton::A);
        state.press(TestButton::B);

        assert_eq!(state.pressed_count(), 2);
        assert!(state.any_pressed());

        state.release(TestButton::A);
        assert_eq!(state.pressed_count(), 1);
        assert!(state.pressed(TestButton::B));
    }

    #[test]
    fn frame_lifecycle() {
        let mut state = ButtonState::new();

        // Frame 1: press A
        state.press(TestButton::A);
        assert!(state.just_pressed(TestButton::A));
        state.clear_just();

        // Frame 2: A still held, press B
        assert!(state.pressed(TestButton::A));
        assert!(!state.just_pressed(TestButton::A));
        state.press(TestButton::B);
        assert!(state.just_pressed(TestButton::B));
        state.clear_just();

        // Frame 3: release A
        state.release(TestButton::A);
        assert!(state.just_released(TestButton::A));
        assert!(!state.pressed(TestButton::A));
        assert!(state.pressed(TestButton::B));
    }
}