use std::collections::HashSet;
pub use winit::keyboard::{Key, KeyCode};
use winit::{
event::KeyEvent,
keyboard::{ModifiersState, PhysicalKey},
};
#[derive(Clone, Debug)]
pub struct KeyInput {
pub event: KeyEvent,
pub mods: ModifiersState,
}
#[derive(Clone, Debug)]
pub struct KeyboardContext {
pub active_modifiers: ModifiersState,
pub pressed_physical_keys: HashSet<PhysicalKey>,
pub pressed_logical_keys: HashSet<Key>,
last_pressed: Option<PhysicalKey>,
current_pressed: Option<PhysicalKey>,
previously_pressed_physical_keys: HashSet<PhysicalKey>,
previously_pressed_logical_keys: HashSet<Key>,
}
impl KeyboardContext {
pub fn new() -> Self {
Self {
active_modifiers: ModifiersState::default(),
pressed_physical_keys: HashSet::with_capacity(256),
pressed_logical_keys: HashSet::with_capacity(256),
last_pressed: None,
current_pressed: None,
previously_pressed_physical_keys: HashSet::with_capacity(256),
previously_pressed_logical_keys: HashSet::with_capacity(256),
}
}
pub fn is_logical_key_pressed(&self, key: &Key) -> bool {
self.pressed_logical_keys.contains(key)
}
pub fn is_logical_key_just_pressed(&self, key: &Key) -> bool {
self.pressed_logical_keys.contains(key)
&& !self.previously_pressed_logical_keys.contains(key)
}
pub fn is_logical_key_just_released(&self, key: &Key) -> bool {
!self.pressed_logical_keys.contains(key)
&& self.previously_pressed_logical_keys.contains(key)
}
pub fn is_physical_key_pressed(&self, physical_key: &PhysicalKey) -> bool {
self.pressed_physical_keys.contains(physical_key)
}
pub fn is_physical_key_just_pressed(&self, physical_key: &PhysicalKey) -> bool {
self.pressed_physical_keys.contains(physical_key)
&& !self.previously_pressed_physical_keys.contains(physical_key)
}
pub fn is_physical_key_just_released(&self, physical_key: &PhysicalKey) -> bool {
!self.pressed_physical_keys.contains(physical_key)
&& self.previously_pressed_physical_keys.contains(physical_key)
}
pub fn is_key_repeated(&self) -> bool {
if self.last_pressed.is_some() {
self.last_pressed == self.current_pressed
} else {
false
}
}
pub fn save_keyboard_state(&mut self) {
self.previously_pressed_logical_keys
.clone_from(&self.pressed_logical_keys);
self.previously_pressed_physical_keys
.clone_from(&self.pressed_physical_keys);
}
pub(crate) fn set_logical_key(&mut self, key: &Key, pressed: bool) {
if pressed {
let _ = self.pressed_logical_keys.insert(key.clone());
} else {
let _ = self.pressed_logical_keys.remove(key);
}
}
pub(crate) fn set_physical_key(&mut self, physical_key: &PhysicalKey, pressed: bool) {
if pressed {
let _ = self.pressed_physical_keys.insert(*physical_key);
self.last_pressed = self.current_pressed;
self.current_pressed = Some(*physical_key);
} else {
let _ = self.pressed_physical_keys.remove(physical_key);
self.current_pressed = None;
}
}
}
impl Default for KeyboardContext {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pressed_logical_keys_tracking() {
let a = &Key::Character("a".into());
let b = &Key::Character("b".into());
let empty = HashSet::new();
let mut keyboard = KeyboardContext::new();
assert_eq!(keyboard.pressed_logical_keys, empty);
assert!(!keyboard.is_logical_key_pressed(a));
keyboard.set_logical_key(a, true);
assert_eq!(
keyboard.pressed_logical_keys,
[a.clone()].iter().cloned().collect()
);
assert!(keyboard.is_logical_key_pressed(a));
keyboard.set_logical_key(a, false);
assert_eq!(keyboard.pressed_logical_keys, empty);
assert!(!keyboard.is_logical_key_pressed(a));
keyboard.set_logical_key(a, true);
assert_eq!(
keyboard.pressed_logical_keys,
[a.clone()].iter().cloned().collect()
);
assert!(keyboard.is_logical_key_pressed(a));
keyboard.set_logical_key(a, true);
assert_eq!(
keyboard.pressed_logical_keys,
[a.clone()].iter().cloned().collect()
);
keyboard.set_logical_key(b, true);
assert_eq!(
keyboard.pressed_logical_keys,
[a.clone(), b.clone()].iter().cloned().collect()
);
keyboard.set_logical_key(b, true);
assert_eq!(
keyboard.pressed_logical_keys,
[a.clone(), b.clone()].iter().cloned().collect()
);
keyboard.set_logical_key(a, false);
assert_eq!(
keyboard.pressed_logical_keys,
[b.clone()].iter().cloned().collect()
);
keyboard.set_logical_key(a, false);
assert_eq!(
keyboard.pressed_logical_keys,
[b.clone()].iter().cloned().collect()
);
keyboard.set_logical_key(b, false);
assert_eq!(keyboard.pressed_logical_keys, empty);
}
#[test]
fn pressed_scancodes_tracking() {
let mut keyboard = KeyboardContext::new();
let a = &PhysicalKey::Code(KeyCode::KeyA);
let b = &PhysicalKey::Code(KeyCode::KeyB);
assert_eq!(keyboard.pressed_physical_keys, [].iter().copied().collect());
assert!(!keyboard.is_physical_key_pressed(a));
keyboard.set_physical_key(a, true);
assert_eq!(
keyboard.pressed_physical_keys,
[*a].iter().copied().collect()
);
assert!(keyboard.is_physical_key_pressed(a));
keyboard.set_physical_key(a, false);
assert_eq!(keyboard.pressed_physical_keys, [].iter().copied().collect());
assert!(!keyboard.is_physical_key_pressed(a));
keyboard.set_physical_key(a, true);
assert_eq!(
keyboard.pressed_physical_keys,
[*a].iter().copied().collect()
);
assert!(keyboard.is_physical_key_pressed(a));
keyboard.set_physical_key(a, true);
assert_eq!(
keyboard.pressed_physical_keys,
[*a].iter().copied().collect()
);
keyboard.set_physical_key(b, true);
assert_eq!(
keyboard.pressed_physical_keys,
[*a, *b].iter().copied().collect()
);
keyboard.set_physical_key(b, true);
assert_eq!(
keyboard.pressed_physical_keys,
[*a, *b].iter().copied().collect()
);
keyboard.set_physical_key(a, false);
assert_eq!(
keyboard.pressed_physical_keys,
[*b].iter().copied().collect()
);
keyboard.set_physical_key(a, false);
assert_eq!(
keyboard.pressed_physical_keys,
[*b].iter().copied().collect()
);
keyboard.set_physical_key(b, false);
assert_eq!(keyboard.pressed_physical_keys, [].iter().copied().collect());
}
#[test]
fn repeated_keys_tracking() {
let a = &PhysicalKey::Code(KeyCode::KeyA);
let b = &PhysicalKey::Code(KeyCode::KeyB);
let mut keyboard = KeyboardContext::new();
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, true);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, false);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, true);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, true);
assert!(keyboard.is_key_repeated());
keyboard.set_physical_key(a, false);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, true);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(b, true);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, true);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(a, true);
assert!(keyboard.is_key_repeated());
keyboard.set_physical_key(b, true);
assert!(!keyboard.is_key_repeated());
keyboard.set_physical_key(b, true);
assert!(keyboard.is_key_repeated());
}
}