use crate::context::Context;
use std::collections::HashSet;
use winit::ModifiersState;
pub use winit::VirtualKeyCode as KeyCode;
bitflags! {
#[derive(Default)]
pub struct KeyMods: u8 {
const NONE = 0b0000_0000;
const SHIFT = 0b0000_0001;
const CTRL = 0b0000_0010;
const ALT = 0b0000_0100;
const LOGO = 0b0000_1000;
}
}
impl From<ModifiersState> for KeyMods {
fn from(state: ModifiersState) -> Self {
let mut keymod = KeyMods::empty();
if state.shift {
keymod |= Self::SHIFT;
}
if state.ctrl {
keymod |= Self::CTRL;
}
if state.alt {
keymod |= Self::ALT;
}
if state.logo {
keymod |= Self::LOGO;
}
keymod
}
}
#[derive(Clone, Debug)]
pub struct KeyboardContext {
active_modifiers: KeyMods,
pressed_keys_set: HashSet<KeyCode>,
last_pressed: Option<KeyCode>,
current_pressed: Option<KeyCode>,
}
impl KeyboardContext {
pub(crate) fn new() -> Self {
Self {
active_modifiers: KeyMods::empty(),
pressed_keys_set: HashSet::with_capacity(256),
last_pressed: None,
current_pressed: None,
}
}
pub(crate) fn set_key(&mut self, key: KeyCode, pressed: bool) {
if pressed {
let _ = self.pressed_keys_set.insert(key);
self.last_pressed = self.current_pressed;
self.current_pressed = Some(key);
} else {
let _ = self.pressed_keys_set.remove(&key);
self.current_pressed = None;
match key {
KeyCode::LShift | KeyCode::RShift => self.active_modifiers -= KeyMods::SHIFT,
KeyCode::LControl | KeyCode::RControl => self.active_modifiers -= KeyMods::CTRL,
KeyCode::LAlt | KeyCode::RAlt => self.active_modifiers -= KeyMods::ALT,
KeyCode::LWin | KeyCode::RWin => self.active_modifiers -= KeyMods::LOGO,
_ => (),
}
}
}
pub(crate) fn set_modifiers(&mut self, keymods: KeyMods) {
self.active_modifiers = keymods;
}
pub(crate) fn is_key_pressed(&self, key: KeyCode) -> bool {
self.pressed_keys_set.contains(&key)
}
pub(crate) fn is_key_repeated(&self) -> bool {
if self.last_pressed.is_some() {
self.last_pressed == self.current_pressed
} else {
false
}
}
pub(crate) fn pressed_keys(&self) -> &HashSet<KeyCode> {
&self.pressed_keys_set
}
pub(crate) fn active_mods(&self) -> KeyMods {
self.active_modifiers
}
}
impl Default for KeyboardContext {
fn default() -> Self {
Self::new()
}
}
pub fn is_key_pressed(ctx: &Context, key: KeyCode) -> bool {
ctx.keyboard_context.is_key_pressed(key)
}
pub fn is_key_repeated(ctx: &Context) -> bool {
ctx.keyboard_context.is_key_repeated()
}
pub fn pressed_keys(ctx: &Context) -> &HashSet<KeyCode> {
ctx.keyboard_context.pressed_keys()
}
pub fn is_mod_active(ctx: &Context, keymods: KeyMods) -> bool {
ctx.keyboard_context.active_mods().contains(keymods)
}
pub fn active_mods(ctx: &Context) -> KeyMods {
ctx.keyboard_context.active_mods()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_mod_conversions() {
assert_eq!(
KeyMods::empty(),
KeyMods::from(ModifiersState {
shift: false,
ctrl: false,
alt: false,
logo: false,
})
);
assert_eq!(
KeyMods::SHIFT,
KeyMods::from(ModifiersState {
shift: true,
ctrl: false,
alt: false,
logo: false,
})
);
assert_eq!(
KeyMods::SHIFT | KeyMods::ALT,
KeyMods::from(ModifiersState {
shift: true,
ctrl: false,
alt: true,
logo: false,
})
);
assert_eq!(
KeyMods::SHIFT | KeyMods::ALT | KeyMods::CTRL,
KeyMods::from(ModifiersState {
shift: true,
ctrl: true,
alt: true,
logo: false,
})
);
assert_eq!(
KeyMods::SHIFT - KeyMods::ALT,
KeyMods::from(ModifiersState {
shift: true,
ctrl: false,
alt: false,
logo: false,
})
);
assert_eq!(
(KeyMods::SHIFT | KeyMods::ALT) - KeyMods::ALT,
KeyMods::from(ModifiersState {
shift: true,
ctrl: false,
alt: false,
logo: false,
})
);
assert_eq!(
KeyMods::SHIFT - (KeyMods::ALT | KeyMods::SHIFT),
KeyMods::from(ModifiersState {
shift: false,
ctrl: false,
alt: false,
logo: false,
})
);
}
#[test]
fn pressed_keys_tracking() {
let mut keyboard = KeyboardContext::new();
assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
assert!(!keyboard.is_key_pressed(KeyCode::A));
keyboard.set_key(KeyCode::A, true);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::A].iter().cloned().collect()
);
assert!(keyboard.is_key_pressed(KeyCode::A));
keyboard.set_key(KeyCode::A, false);
assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
assert!(!keyboard.is_key_pressed(KeyCode::A));
keyboard.set_key(KeyCode::A, true);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::A].iter().cloned().collect()
);
assert!(keyboard.is_key_pressed(KeyCode::A));
keyboard.set_key(KeyCode::A, true);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::A].iter().cloned().collect()
);
keyboard.set_key(KeyCode::B, true);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::A, KeyCode::B].iter().cloned().collect()
);
keyboard.set_key(KeyCode::B, true);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::A, KeyCode::B].iter().cloned().collect()
);
keyboard.set_key(KeyCode::A, false);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::B].iter().cloned().collect()
);
keyboard.set_key(KeyCode::A, false);
assert_eq!(
keyboard.pressed_keys(),
&[KeyCode::B].iter().cloned().collect()
);
keyboard.set_key(KeyCode::B, false);
assert_eq!(keyboard.pressed_keys(), &[].iter().cloned().collect());
}
#[test]
fn keyboard_modifiers() {
let mut keyboard = KeyboardContext::new();
assert_eq!(keyboard.active_mods(), KeyMods::default());
keyboard.set_modifiers(KeyMods::from(ModifiersState {
shift: true,
ctrl: true,
alt: true,
logo: true,
}));
assert_eq!(
keyboard.active_mods(),
KeyMods::SHIFT | KeyMods::CTRL | KeyMods::ALT | KeyMods::LOGO
);
keyboard.set_key(KeyCode::LControl, false);
assert_eq!(
keyboard.active_mods(),
KeyMods::SHIFT | KeyMods::ALT | KeyMods::LOGO
);
keyboard.set_key(KeyCode::RAlt, false);
assert_eq!(keyboard.active_mods(), KeyMods::SHIFT | KeyMods::LOGO);
keyboard.set_key(KeyCode::LWin, false);
assert_eq!(keyboard.active_mods(), KeyMods::SHIFT);
}
#[test]
fn repeated_keys_tracking() {
let mut keyboard = KeyboardContext::new();
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, true);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, false);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, true);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, true);
assert_eq!(keyboard.is_key_repeated(), true);
keyboard.set_key(KeyCode::A, false);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, true);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::B, true);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, true);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::A, true);
assert_eq!(keyboard.is_key_repeated(), true);
keyboard.set_key(KeyCode::B, true);
assert_eq!(keyboard.is_key_repeated(), false);
keyboard.set_key(KeyCode::B, true);
assert_eq!(keyboard.is_key_repeated(), true);
}
}