reaction 0.2.0

Universal low-latency input handling for game engines
Documentation
use crate::types::KeyCode;
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct KeyState {
    pub is_down: bool,
    pub just_pressed_frame: Option<u32>,
    pub just_released_frame: Option<u32>,
    pub held_duration_ms: f32,
    pub last_press_time: f64,
}

impl Default for KeyState {
    fn default() -> Self {
        Self {
            is_down: false,
            just_pressed_frame: None,
            just_released_frame: None,
            held_duration_ms: 0.0,
            last_press_time: 0.0,
        }
    }
}

pub struct KeyboardState {
    keys: HashMap<KeyCode, KeyState>,
}

impl Default for KeyboardState {
    fn default() -> Self {
        Self::new()
    }
}

impl KeyboardState {
    pub fn new() -> Self {
        Self {
            keys: HashMap::new(),
        }
    }

    pub fn is_down(&self, key: KeyCode) -> bool {
        self.keys.get(&key).map(|k| k.is_down).unwrap_or(false)
    }

    pub fn was_just_pressed(&self, key: KeyCode) -> bool {
        self.keys
            .get(&key)
            .map(|k| k.just_pressed_frame.is_some())
            .unwrap_or(false)
    }

    pub fn was_just_released(&self, key: KeyCode) -> bool {
        self.keys
            .get(&key)
            .map(|k| k.just_released_frame.is_some())
            .unwrap_or(false)
    }

    pub fn press(&mut self, key: KeyCode, timestamp: f64, frame: u32) {
        let state = self.keys.entry(key).or_default();
        if !state.is_down {
            state.is_down = true;
            state.just_pressed_frame = Some(frame);
            state.held_duration_ms = 0.0;
            state.last_press_time = timestamp;
        }
    }

    pub fn release(&mut self, key: KeyCode, frame: u32) {
        let state = self.keys.entry(key).or_default();
        if state.is_down {
            state.is_down = false;
            state.just_released_frame = Some(frame);
        }
    }

    pub fn end_frame(&mut self, delta_time_ms: f32) {
        for state in self.keys.values_mut() {
            state.just_pressed_frame = None;
            state.just_released_frame = None;

            if state.is_down {
                state.held_duration_ms += delta_time_ms;
            }
        }
    }
    pub fn get_held_duration(&self, key: KeyCode) -> f32 {
        self.keys
            .get(&key)
            .map(|k| k.held_duration_ms)
            .unwrap_or(0.0)
    }
}