game/src/engine/event.rs

153 lines
3.7 KiB
Rust

//! This module helps us deal with controls and events.
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use crate::Result;
/// The state of the keyboard.
pub struct Keyboard {
/// The inner keyboard.
inner: Rc<RefCell<InnerKeyboard>>,
}
impl Keyboard {
/// Checks wether a key is pressed or not.
pub fn is_key_pressed(&self, key: Key) -> bool {
let mut b = self.inner.borrow_mut();
*b.keys.entry(key).or_insert(false)
}
/// Receives and treats an event.
pub fn manage_event(&self, event: Event) {
let mut b = self.inner.borrow_mut();
match event {
Event::KeyPressed(x) => *b.keys.entry(x).or_insert(true) = true,
Event::KeyReleased(x) => *b.keys.entry(x).or_insert(false) = false,
}
b.events.push_back(event);
}
/// Gets an event from the event stack.
pub fn pop(&self) -> Option<Event> {
self.inner.borrow_mut().events.pop_front()
}
}
impl Keyboard {
/// Initializes the keyboard.
pub fn new(document: &web_sys::Document) -> Result<Keyboard> {
let inner = Rc::new(RefCell::new(InnerKeyboard::new()));
let clone = Keyboard {
inner: inner.clone(),
};
let down_cb = Closure::<dyn FnMut(_)>::new(move |event: web_sys::KeyboardEvent| {
if let Some(event) = Event::from_js(true, event) {
clone.manage_event(event);
}
});
document.set_onkeydown(Some(down_cb.as_ref().unchecked_ref()));
down_cb.forget();
let clone = Keyboard {
inner: inner.clone(),
};
let up_cb = Closure::<dyn FnMut(_)>::new(move |event: web_sys::KeyboardEvent| {
if let Some(event) = Event::from_js(false, event) {
clone.manage_event(event);
}
});
document.set_onkeyup(Some(up_cb.as_ref().unchecked_ref()));
up_cb.forget();
Ok(Keyboard { inner })
}
}
/// The state of the keyboard.
pub struct InnerKeyboard {
/// Holds the state of the keys.
keys: HashMap<Key, bool>,
/// The list of events to be processed.
events: VecDeque<Event>,
}
impl InnerKeyboard {
/// Creates a new inner arrowboard.
pub fn new() -> InnerKeyboard {
InnerKeyboard {
keys: HashMap::new(),
events: VecDeque::new(),
}
}
}
/// The different events that can occur.
#[derive(Copy, Clone)]
pub enum Event {
/// A key was pressed down.
KeyPressed(Key),
/// A key was released.
KeyReleased(Key),
}
/// The different key we cant to support.
#[derive(Hash, PartialEq, Eq, Copy, Clone)]
pub enum Key {
/// The left arrow key.
ArrowLeft,
/// The right arrow key.
ArrowRight,
/// The up arrow key.
ArrowUp,
/// The bottom arrow key.
ArrowDown,
/// The space key.
Space,
}
impl Key {
/// Tries and converts a javascript key to our key type.
pub fn from_str(string: &str) -> Option<Key> {
match string {
"ArrowLeft" => Some(Key::ArrowLeft),
"ArrowRight" => Some(Key::ArrowRight),
"ArrowUp" => Some(Key::ArrowUp),
"ArrowDown" => Some(Key::ArrowDown),
"Space" => Some(Key::Space),
_ => None,
}
}
}
impl Event {
/// Tries and converts a javacript event to our event type.
pub fn from_js(down: bool, event: web_sys::KeyboardEvent) -> Option<Event> {
let key = Key::from_str(&event.code())?;
let event = if down {
Some(Event::KeyPressed(key))
} else {
Some(Event::KeyReleased(key))
};
event
}
}