use std::collections::HashMap;
use std::iter::Filter;
use uni_app::AppEvent;
pub trait InputApi {
fn key(&self, key: &str) -> bool;
fn key_pressed(&mut self, key: &str) -> bool;
fn keys_pressed(&self) -> Keys;
fn key_released(&mut self, key: &str) -> bool;
fn keys_released(&self) -> Keys;
fn text(&self) -> String;
fn mouse_button(&self, num: usize) -> bool;
fn mouse_button_pressed(&mut self, num: usize) -> bool;
fn mouse_button_released(&mut self, num: usize) -> bool;
fn mouse_pos(&self) -> (f32, f32);
fn close_requested(&self) -> bool;
}
pub struct DoryenInput {
kdown: HashMap<String, bool>,
kpressed: HashMap<String, bool>,
kreleased: HashMap<String, bool>,
mdown: HashMap<usize, bool>,
mpressed: HashMap<usize, bool>,
mreleased: HashMap<usize, bool>,
text: String,
close_request: bool,
mpos: (f32, f32),
screen_size: (f32, f32),
con_size: (f32, f32),
mouse_offset: (f32, f32),
}
impl DoryenInput {
pub fn new(
(screen_width, screen_height): (u32, u32),
(con_width, con_height): (u32, u32),
(x_offset, y_offset): (u32, u32),
) -> Self {
Self {
kdown: HashMap::new(),
kpressed: HashMap::new(),
kreleased: HashMap::new(),
mdown: HashMap::new(),
mpressed: HashMap::new(),
mreleased: HashMap::new(),
mpos: (0.0, 0.0),
text: String::new(),
close_request: false,
screen_size: (screen_width as f32, screen_height as f32),
con_size: (con_width as f32, con_height as f32),
mouse_offset: (x_offset as f32, y_offset as f32),
}
}
fn on_key_down(&mut self, scan_code: &str) {
if !self.key(scan_code) {
self.kpressed.insert(scan_code.to_owned(), true);
self.kdown.insert(scan_code.to_owned(), true);
}
}
fn on_key_up(&mut self, scan_code: &str) {
self.kpressed.insert(scan_code.to_owned(), false);
self.kdown.insert(scan_code.to_owned(), false);
self.kreleased.insert(scan_code.to_owned(), true);
}
fn on_mouse_down(&mut self, button: usize) {
if !self.mouse_button(button) {
self.mpressed.insert(button, true);
self.mdown.insert(button, true);
}
}
fn on_mouse_up(&mut self, button: usize) {
self.mpressed.insert(button, false);
self.mdown.insert(button, false);
self.mreleased.insert(button, true);
}
pub fn on_frame(&mut self) {
self.mpressed.clear();
self.mreleased.clear();
self.kreleased.clear();
self.kpressed.clear();
self.close_request = false;
self.text.clear();
}
pub fn on_event(&mut self, event: &AppEvent) {
match event {
AppEvent::KeyDown(ref key) => {
self.on_key_down(&key.code);
}
AppEvent::KeyUp(ref key) => {
self.on_key_up(&key.code);
}
AppEvent::CharEvent(ch) => {
if !ch.is_control() {
self.text.push(*ch);
}
}
AppEvent::MousePos(ref pos) => {
self.mpos = (
(pos.0 as f32 - self.mouse_offset.0) / self.screen_size.0 * self.con_size.0,
(pos.1 as f32 - self.mouse_offset.1) / self.screen_size.1 * self.con_size.1,
);
}
AppEvent::MouseDown(ref mouse) => {
self.on_mouse_down(mouse.button);
}
AppEvent::MouseUp(ref mouse) => {
self.on_mouse_up(mouse.button);
}
AppEvent::CloseRequested => {
self.close_request = true;
}
_ => (),
}
}
pub(crate) fn resize(
&mut self,
(screen_width, screen_height): (u32, u32),
(con_width, con_height): (u32, u32),
(x_offset, y_offset): (u32, u32),
) {
self.screen_size = (screen_width as f32, screen_height as f32);
self.con_size = (con_width as f32, con_height as f32);
self.mouse_offset = (x_offset as f32, y_offset as f32);
}
}
impl InputApi for DoryenInput {
fn key(&self, scan_code: &str) -> bool {
matches!(self.kdown.get(scan_code), Some(&true))
}
fn key_pressed(&mut self, scan_code: &str) -> bool {
matches!(self.kpressed.get(scan_code), Some(&true))
}
fn keys_pressed(&self) -> Keys {
Keys {
inner: self.kpressed.iter().filter(|&(_, &v)| v),
}
}
fn key_released(&mut self, scan_code: &str) -> bool {
matches!(self.kreleased.get(scan_code), Some(&true))
}
fn keys_released(&self) -> Keys {
Keys {
inner: self.kreleased.iter().filter(|&(_, &v)| v),
}
}
fn text(&self) -> String {
self.text.to_owned()
}
fn mouse_button(&self, num: usize) -> bool {
matches!(self.mdown.get(&num), Some(&true))
}
fn mouse_button_pressed(&mut self, num: usize) -> bool {
matches!(self.mpressed.get(&num), Some(&true))
}
fn mouse_button_released(&mut self, num: usize) -> bool {
matches!(self.mreleased.get(&num), Some(&true))
}
fn mouse_pos(&self) -> (f32, f32) {
self.mpos
}
fn close_requested(&self) -> bool {
self.close_request
}
}
type KeyMapFilter<'a> =
Filter<std::collections::hash_map::Iter<'a, String, bool>, fn(&(&'a String, &'a bool)) -> bool>;
pub struct Keys<'a> {
inner: KeyMapFilter<'a>,
}
impl<'a> Iterator for Keys<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, _)| k.as_ref())
}
}