//! 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>, } 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 { self.inner.borrow_mut().events.pop_front() } } impl Keyboard { /// Initializes the keyboard. pub fn new(document: &web_sys::Document) -> Result { let inner = Rc::new(RefCell::new(InnerKeyboard::new())); let clone = Keyboard { inner: inner.clone(), }; let down_cb = Closure::::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::::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, /// The list of events to be processed. events: VecDeque, } 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 { 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 { let key = Key::from_str(&event.code())?; let event = if down { Some(Event::KeyPressed(key)) } else { Some(Event::KeyReleased(key)) }; event } }