use std::collections::HashSet;
use crate::ecs::Resource;
pub use winit::keyboard::KeyCode;
#[derive(Resource, Default, Debug)]
pub struct Keys {
pressed: HashSet<KeyCode>,
just_pressed: HashSet<KeyCode>,
}
impl Keys {
pub fn pressed(&self, key: KeyCode) -> bool {
self.pressed.contains(&key)
}
pub fn just_pressed(&self, key: KeyCode) -> bool {
self.just_pressed.contains(&key)
}
pub(crate) fn press(&mut self, key: KeyCode, repeat: bool) {
if !repeat && self.pressed.insert(key) {
self.just_pressed.insert(key);
}
}
pub(crate) fn release(&mut self, key: KeyCode) {
self.pressed.remove(&key);
}
pub(crate) fn end_frame(&mut self) {
self.just_pressed.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_press_is_an_edge_that_lasts_one_frame() {
let mut keys = Keys::default();
keys.press(KeyCode::Escape, false);
assert!(keys.just_pressed(KeyCode::Escape));
assert!(keys.pressed(KeyCode::Escape));
keys.end_frame();
assert!(!keys.just_pressed(KeyCode::Escape), "the edge is spent");
assert!(keys.pressed(KeyCode::Escape), "but the key is still held");
}
#[test]
fn key_repeat_does_not_read_as_a_new_press() {
let mut keys = Keys::default();
keys.press(KeyCode::Escape, false);
keys.end_frame();
keys.press(KeyCode::Escape, true);
assert!(!keys.just_pressed(KeyCode::Escape));
keys.press(KeyCode::Escape, false);
assert!(!keys.just_pressed(KeyCode::Escape));
}
#[test]
fn releasing_and_pressing_again_is_a_fresh_edge() {
let mut keys = Keys::default();
keys.press(KeyCode::Escape, false);
keys.end_frame();
keys.release(KeyCode::Escape);
assert!(!keys.pressed(KeyCode::Escape));
keys.press(KeyCode::Escape, false);
assert!(keys.just_pressed(KeyCode::Escape));
}
#[test]
fn keys_are_tracked_independently() {
let mut keys = Keys::default();
keys.press(KeyCode::Escape, false);
keys.press(KeyCode::Space, false);
keys.release(KeyCode::Escape);
assert!(!keys.pressed(KeyCode::Escape));
assert!(keys.pressed(KeyCode::Space));
}
}