use ui_events::keyboard::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
extern crate alloc;
use alloc::vec::Vec;
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
struct KeyInfo(Key, Location, Code);
#[derive(Clone, Debug, Default)]
pub struct KeyboardState {
just_pressed: Vec<KeyInfo>,
just_released: Vec<KeyInfo>,
down: Vec<KeyInfo>,
pub modifiers: Modifiers,
}
impl KeyboardState {
pub fn key_just_pressed(&self, key: Key) -> bool {
self.just_pressed.iter().any(|KeyInfo(k, ..)| k == &key)
}
pub fn key_str_just_pressed(&self, s: &str) -> bool {
self.just_pressed
.iter()
.any(|KeyInfo(k, ..)| matches!(k, Key::Character(c) if c == s))
}
pub fn key_just_pressed_location(&self, key: Key, location: Location) -> bool {
self.just_pressed
.iter()
.any(|KeyInfo(k, l, _)| k == &key && l == &location)
}
pub fn key_str_just_pressed_location(&self, s: &str, location: Location) -> bool {
self.just_pressed
.iter()
.any(|KeyInfo(k, l, ..)| l == &location && matches!(k, Key::Character(c) if c == s))
}
pub fn code_just_pressed(&self, code: Code) -> bool {
self.just_pressed.iter().any(|KeyInfo(_, _, c)| c == &code)
}
pub fn key_just_released(&self, key: Key) -> bool {
self.just_released.iter().any(|KeyInfo(k, ..)| k == &key)
}
pub fn key_str_just_released(&self, s: &str) -> bool {
self.just_released
.iter()
.any(|KeyInfo(k, ..)| matches!(k, Key::Character(c) if c == s))
}
pub fn key_just_released_location(&self, key: Key, location: Location) -> bool {
self.just_released
.iter()
.any(|KeyInfo(k, l, _)| k == &key && l == &location)
}
pub fn key_str_just_released_location(&self, s: &str, location: Location) -> bool {
self.just_released
.iter()
.any(|KeyInfo(k, l, ..)| l == &location && matches!(k, Key::Character(c) if c == s))
}
pub fn code_just_released(&self, code: Code) -> bool {
self.just_released.iter().any(|KeyInfo(_, _, c)| c == &code)
}
pub fn is_any_down(&self) -> bool {
!self.down.is_empty()
}
pub fn key_down(&self, key: Key) -> bool {
self.down.iter().any(|KeyInfo(k, ..)| k == &key)
}
pub fn key_str_down(&self, s: &str) -> bool {
self.down
.iter()
.any(|KeyInfo(k, ..)| matches!(k, Key::Character(c) if c == s))
}
pub fn key_down_location(&self, key: Key, location: Location) -> bool {
self.down
.iter()
.any(|KeyInfo(k, l, _)| k == &key && l == &location)
}
pub fn key_str_down_location(&self, s: &str, location: Location) -> bool {
self.down
.iter()
.any(|KeyInfo(k, l, ..)| l == &location && matches!(k, Key::Character(c) if c == s))
}
pub fn code_down(&self, code: Code) -> bool {
self.down.iter().any(|KeyInfo(_, _, c)| c == &code)
}
pub fn clear_frame(&mut self) {
self.just_pressed.clear();
self.just_released.clear();
}
pub fn process_keyboard_event(&mut self, event: KeyboardEvent) {
self.modifiers = event.modifiers;
let info = KeyInfo(event.key, event.location, event.code);
match event.state {
KeyState::Down => {
self.just_pressed.push(info.clone());
self.down.push(info.clone());
}
KeyState::Up => {
self.just_released.push(info.clone());
self.down.retain(|other| other != &info);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ui_events::keyboard::{Code, NamedKey};
fn make_key_down_event(key: Key) -> KeyboardEvent {
KeyboardEvent {
state: KeyState::Down,
key,
location: Location::Standard,
code: Code::Unidentified,
modifiers: Default::default(),
is_composing: false,
repeat: false,
}
}
fn make_key_up_event(key: Key) -> KeyboardEvent {
KeyboardEvent {
state: KeyState::Up,
key,
location: Location::Standard,
code: Code::Unidentified,
modifiers: Default::default(),
is_composing: false,
repeat: false,
}
}
#[test]
fn press_and_hold_a() {
let mut state = KeyboardState::default();
state.process_keyboard_event(make_key_down_event(Key::Character("A".into())));
assert!(state.key_just_pressed(Key::Character("A".into())));
assert!(state.key_str_just_pressed("A"));
assert!(state.key_str_just_pressed_location("A", Location::Standard));
assert!(!state.key_str_just_pressed_location("A", Location::Left));
assert!(state.key_down(Key::Character("A".into())));
assert!(state.key_str_down("A"));
assert!(state.key_str_down_location("A", Location::Standard));
assert!(!state.key_str_down_location("A", Location::Left));
assert!(!state.key_just_released(Key::Character("A".into())));
assert!(!state.key_str_just_released("A"));
assert!(!state.key_str_just_released_location("A", Location::Standard));
assert!(!state.key_str_just_released_location("A", Location::Left));
state.clear_frame();
assert!(!state.key_just_pressed(Key::Character("A".into())));
assert!(!state.key_str_just_pressed("A"));
assert!(!state.key_str_just_pressed_location("A", Location::Standard));
assert!(!state.key_str_just_pressed_location("A", Location::Left));
assert!(state.key_down(Key::Character("A".into())));
assert!(state.key_str_down("A"));
assert!(state.key_str_down_location("A", Location::Standard));
assert!(!state.key_str_down_location("A", Location::Left));
}
#[test]
fn press_and_release_a() {
let mut state = KeyboardState::default();
state.process_keyboard_event(make_key_down_event(Key::Character("A".into())));
state.process_keyboard_event(make_key_up_event(Key::Character("A".into())));
assert!(state.key_just_pressed(Key::Character("A".into())));
assert!(state.key_str_just_pressed("A"));
assert!(state.key_str_just_pressed_location("A", Location::Standard));
assert!(!state.key_str_just_pressed_location("A", Location::Left));
assert!(state.key_just_released(Key::Character("A".into())));
assert!(state.key_str_just_released("A"));
assert!(state.key_str_just_released_location("A", Location::Standard));
assert!(!state.key_str_just_released_location("A", Location::Left));
assert!(!state.key_down(Key::Character("A".into())));
assert!(!state.key_str_down("A"));
assert!(!state.key_str_down_location("A", Location::Standard));
assert!(!state.key_str_down_location("A", Location::Left));
}
#[test]
fn release_after_hold() {
let mut state = KeyboardState::default();
state.process_keyboard_event(make_key_down_event(Key::Character("A".into())));
state.clear_frame();
state.process_keyboard_event(make_key_up_event(Key::Character("A".into())));
assert!(!state.key_just_pressed(Key::Character("A".into())));
assert!(!state.key_str_just_pressed("A"));
assert!(!state.key_str_just_pressed_location("A", Location::Standard));
assert!(!state.key_str_just_pressed_location("A", Location::Left));
assert!(state.key_just_released(Key::Character("A".into())));
assert!(state.key_str_just_released("A"));
assert!(state.key_str_just_released_location("A", Location::Standard));
assert!(!state.key_str_just_released_location("A", Location::Left));
assert!(!state.key_down(Key::Character("A".into())));
assert!(!state.key_str_down("A"));
assert!(!state.key_str_down_location("A", Location::Standard));
assert!(!state.key_str_down_location("A", Location::Left));
}
fn make_code_down_event(code: Code) -> KeyboardEvent {
KeyboardEvent {
state: KeyState::Down,
key: Key::Named(NamedKey::Unidentified),
location: Location::Standard,
code,
modifiers: Default::default(),
is_composing: false,
repeat: false,
}
}
fn make_code_up_event(code: Code) -> KeyboardEvent {
KeyboardEvent {
state: KeyState::Up,
key: Key::Named(NamedKey::Unidentified),
location: Location::Standard,
code,
modifiers: Default::default(),
is_composing: false,
repeat: false,
}
}
#[test]
fn press_and_hold_a_code() {
let mut state = KeyboardState::default();
state.process_keyboard_event(make_code_down_event(Code::KeyA));
assert!(state.code_just_pressed(Code::KeyA));
assert!(state.code_down(Code::KeyA));
assert!(!state.code_just_released(Code::KeyA));
state.clear_frame();
assert!(!state.code_just_pressed(Code::KeyA));
assert!(state.code_down(Code::KeyA));
}
#[test]
fn press_and_release_a_code() {
let mut state = KeyboardState::default();
state.process_keyboard_event(make_code_down_event(Code::KeyA));
state.process_keyboard_event(make_code_up_event(Code::KeyA));
assert!(state.code_just_pressed(Code::KeyA));
assert!(state.code_just_released(Code::KeyA));
assert!(!state.code_down(Code::KeyA));
}
#[test]
fn release_after_hold_code() {
let mut state = KeyboardState::default();
state.process_keyboard_event(make_code_down_event(Code::KeyA));
state.clear_frame();
state.process_keyboard_event(make_code_up_event(Code::KeyA));
assert!(!state.code_just_pressed(Code::KeyA));
assert!(state.code_just_released(Code::KeyA));
assert!(!state.code_down(Code::KeyA));
}
}