use platform_core::{Event, Key, ModifiersState};
use rustc_hash::FxHashSet;
use std::cell::RefCell;
#[derive(Default)]
struct Keyboard {
modifiers: ModifiersState,
held: FxHashSet<Key>,
pressed: FxHashSet<Key>,
}
thread_local! {
static KEYBOARD: RefCell<Keyboard> = RefCell::new(Keyboard::default());
}
pub fn observe(event: &Event) {
KEYBOARD.with(|k| {
let mut k = k.borrow_mut();
match event {
Event::ModifiersChanged { modifiers } => k.modifiers = *modifiers,
Event::KeyPressed { key, modifiers } => {
k.modifiers = *modifiers;
if k.held.insert(key.clone()) {
k.pressed.insert(key.clone());
}
}
Event::KeyReleased { key, modifiers } => {
k.modifiers = *modifiers;
k.held.remove(key);
}
Event::FocusChanged { is_focused: false } => {
k.held.clear();
k.pressed.clear();
k.modifiers = ModifiersState::default();
}
_ => {}
}
});
}
pub fn end_frame() {
KEYBOARD.with(|k| k.borrow_mut().pressed.clear());
}
pub fn modifiers() -> ModifiersState {
KEYBOARD.with(|k| k.borrow().modifiers)
}
pub fn key_held(key: &Key) -> bool {
KEYBOARD.with(|k| k.borrow().held.contains(key))
}
pub fn key_pressed(key: &Key) -> bool {
KEYBOARD.with(|k| k.borrow().pressed.contains(key))
}
pub fn reset() {
KEYBOARD.with(|k| *k.borrow_mut() = Keyboard::default());
}
#[cfg(test)]
mod tests {
use super::*;
use platform_core::NamedKey;
fn up() -> Key {
Key::Named(NamedKey::ArrowUp)
}
fn shift() -> ModifiersState {
ModifiersState {
is_shift: true,
..ModifiersState::default()
}
}
fn fresh() {
reset();
}
#[test]
fn a_key_stays_held_until_it_is_released() {
fresh();
assert!(!key_held(&up()));
observe(&Event::KeyPressed {
key: up(),
modifiers: ModifiersState::default(),
});
assert!(key_held(&up()));
end_frame();
assert!(key_held(&up()), "holding outlives the frame it began in");
observe(&Event::KeyReleased {
key: up(),
modifiers: ModifiersState::default(),
});
assert!(!key_held(&up()));
}
#[test]
fn a_press_answers_for_one_frame_only() {
fresh();
observe(&Event::KeyPressed {
key: up(),
modifiers: ModifiersState::default(),
});
assert!(key_pressed(&up()));
end_frame();
assert!(!key_pressed(&up()));
}
#[test]
fn a_repeated_key_is_not_a_new_press() {
fresh();
observe(&Event::KeyPressed {
key: up(),
modifiers: ModifiersState::default(),
});
end_frame();
observe(&Event::KeyPressed {
key: up(),
modifiers: ModifiersState::default(),
});
assert!(key_held(&up()));
assert!(!key_pressed(&up()), "the key never came back up");
}
#[test]
fn a_bare_modifier_is_visible_without_any_key_event() {
fresh();
assert!(!modifiers().is_shift);
observe(&Event::ModifiersChanged { modifiers: shift() });
assert!(modifiers().is_shift);
}
#[test]
fn losing_focus_forgets_what_was_held() {
fresh();
observe(&Event::KeyPressed {
key: up(),
modifiers: shift(),
});
assert!(key_held(&up()) && modifiers().is_shift);
observe(&Event::FocusChanged { is_focused: false });
assert!(!key_held(&up()));
assert!(!modifiers().is_shift);
}
}