use std::collections::HashSet;
#[derive(Debug, Default)]
pub struct InputState {
pub keys_down: HashSet<String>,
pub keys_pressed: HashSet<String>,
pub keys_released: HashSet<String>,
pub mouse_x: f32,
pub mouse_y: f32,
pub mouse_buttons: HashSet<u8>,
pub mouse_buttons_pressed: HashSet<u8>,
pub mouse_buttons_released: HashSet<u8>,
}
impl InputState {
pub fn begin_frame(&mut self) {
self.keys_pressed.clear();
self.keys_released.clear();
self.mouse_buttons_pressed.clear();
self.mouse_buttons_released.clear();
}
pub fn key_down(&mut self, key: &str) {
if self.keys_down.insert(key.to_string()) {
self.keys_pressed.insert(key.to_string());
}
}
pub fn key_up(&mut self, key: &str) {
if self.keys_down.remove(key) {
self.keys_released.insert(key.to_string());
}
}
pub fn mouse_move(&mut self, x: f32, y: f32) {
self.mouse_x = x;
self.mouse_y = y;
}
pub fn mouse_button_down(&mut self, button: u8) {
if self.mouse_buttons.insert(button) {
self.mouse_buttons_pressed.insert(button);
}
}
pub fn mouse_button_up(&mut self, button: u8) {
if self.mouse_buttons.remove(&button) {
self.mouse_buttons_released.insert(button);
}
}
pub fn is_key_down(&self, key: &str) -> bool {
self.keys_down.contains(key)
}
pub fn is_key_pressed(&self, key: &str) -> bool {
self.keys_pressed.contains(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_pressed_survives_until_read() {
let mut input = InputState::default();
input.key_down("ArrowUp");
assert!(input.is_key_pressed("ArrowUp"));
assert!(input.is_key_down("ArrowUp"));
assert!(input.is_key_pressed("ArrowUp"));
input.begin_frame();
assert!(!input.is_key_pressed("ArrowUp"));
assert!(input.is_key_down("ArrowUp")); }
#[test]
fn begin_frame_before_read_loses_input() {
let mut input = InputState::default();
input.key_down("w");
assert!(input.is_key_pressed("w"));
input.begin_frame();
assert!(!input.is_key_pressed("w")); }
#[test]
fn held_key_does_not_re_trigger_pressed() {
let mut input = InputState::default();
input.key_down("a");
assert!(input.is_key_pressed("a"));
input.begin_frame();
input.key_down("a");
assert!(!input.is_key_pressed("a"));
assert!(input.is_key_down("a"));
}
#[test]
fn key_release_tracked() {
let mut input = InputState::default();
input.key_down("Space");
input.begin_frame();
input.key_up("Space");
assert!(!input.is_key_down("Space"));
assert!(input.keys_released.contains("Space"));
input.begin_frame();
assert!(!input.keys_released.contains("Space"));
}
#[test]
fn mouse_position_is_tracked() {
let mut input = InputState::default();
assert_eq!(input.mouse_x, 0.0);
assert_eq!(input.mouse_y, 0.0);
input.mouse_x = 100.5;
input.mouse_y = 200.75;
assert_eq!(input.mouse_x, 100.5);
assert_eq!(input.mouse_y, 200.75);
}
#[test]
fn multiple_keys_can_be_down_simultaneously() {
let mut input = InputState::default();
input.key_down("w");
input.key_down("a");
input.key_down("d");
assert!(input.is_key_down("w"));
assert!(input.is_key_down("a"));
assert!(input.is_key_down("d"));
assert!(input.is_key_pressed("w"));
assert!(input.is_key_pressed("a"));
assert!(input.is_key_pressed("d"));
}
#[test]
fn releasing_one_key_does_not_affect_others() {
let mut input = InputState::default();
input.key_down("w");
input.key_down("a");
input.begin_frame();
input.key_up("w");
assert!(!input.is_key_down("w"));
assert!(input.is_key_down("a"));
}
#[test]
fn key_pressed_works_with_special_keys() {
let mut input = InputState::default();
input.key_down("Escape");
input.key_down("Return");
input.key_down("ArrowLeft");
assert!(input.is_key_pressed("Escape"));
assert!(input.is_key_pressed("Return"));
assert!(input.is_key_pressed("ArrowLeft"));
}
#[test]
fn default_input_state_has_no_keys_down() {
let input = InputState::default();
assert!(!input.is_key_down("a"));
assert!(!input.is_key_down("Space"));
assert!(!input.is_key_pressed("w"));
assert_eq!(input.keys_down.len(), 0);
assert_eq!(input.keys_pressed.len(), 0);
}
}