implement key binding system
This commit is contained in:
parent
57512a4c6b
commit
70fd50d3bc
7 changed files with 410 additions and 57 deletions
323
src/input.rs
Normal file
323
src/input.rs
Normal file
|
@ -0,0 +1,323 @@
|
|||
use std::{collections::HashMap, mem::transmute};
|
||||
|
||||
use raylib::{
|
||||
ffi::{KeyboardKey, MouseButton},
|
||||
RaylibHandle,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum ActionId {
|
||||
StepSim,
|
||||
Undo,
|
||||
Redo,
|
||||
// just like in C, because this way doesn't need more depenedencies
|
||||
_ActionIdSize,
|
||||
}
|
||||
|
||||
impl Default for Input {
|
||||
fn default() -> Self {
|
||||
use KeyboardKey::*;
|
||||
let mut bindings = [(); ActionId::SIZE].map(|_| Vec::new());
|
||||
let mut bind_key = |action, mods, key| {
|
||||
bindings[action as usize] = vec![Binding {
|
||||
modifiers: mods,
|
||||
trigger: InputTrigger::Key(key),
|
||||
}];
|
||||
};
|
||||
bind_key(ActionId::StepSim, vec![], KEY_SPACE);
|
||||
bind_key(ActionId::Undo, vec![KEY_LEFT_CONTROL], KEY_Z);
|
||||
bind_key(ActionId::Redo, vec![KEY_LEFT_CONTROL], KEY_Y);
|
||||
|
||||
Self {
|
||||
bindings,
|
||||
states: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
enum BindingState {
|
||||
#[default]
|
||||
Off,
|
||||
Pressed,
|
||||
Held,
|
||||
Released,
|
||||
}
|
||||
|
||||
type InputMap = HashMap<ActionId, Vec<Binding>>;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(from = "InputMap", into = "InputMap")]
|
||||
pub struct Input {
|
||||
bindings: [Vec<Binding>; ActionId::SIZE],
|
||||
states: [BindingState; ActionId::SIZE],
|
||||
}
|
||||
|
||||
impl Input {
|
||||
pub fn update(&mut self, rl: &RaylibHandle) {
|
||||
for i in 0..ActionId::SIZE {
|
||||
let bindings = &self.bindings[i];
|
||||
let mut is_active = false;
|
||||
for binding in bindings {
|
||||
if binding.modifiers.iter().all(|&m| rl.is_key_down(m)) {
|
||||
is_active |= match binding.trigger {
|
||||
InputTrigger::Mouse(btn) => rl.is_mouse_button_down(btn),
|
||||
InputTrigger::Key(key) => rl.is_key_down(key),
|
||||
}
|
||||
}
|
||||
}
|
||||
let state = &mut self.states[i];
|
||||
*state = if is_active {
|
||||
match state {
|
||||
BindingState::Off | BindingState::Released => BindingState::Pressed,
|
||||
BindingState::Pressed | BindingState::Held => BindingState::Held,
|
||||
}
|
||||
} else {
|
||||
match state {
|
||||
BindingState::Off | BindingState::Released => BindingState::Off,
|
||||
BindingState::Pressed | BindingState::Held => BindingState::Released,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pressed(&self, action: ActionId) -> bool {
|
||||
self.states[action as usize] == BindingState::Pressed
|
||||
}
|
||||
|
||||
pub fn is_held(&self, action: ActionId) -> bool {
|
||||
self.states[action as usize] == BindingState::Pressed
|
||||
|| self.states[action as usize] == BindingState::Held
|
||||
}
|
||||
|
||||
pub fn is_released(&self, action: ActionId) -> bool {
|
||||
self.states[action as usize] == BindingState::Released
|
||||
}
|
||||
}
|
||||
|
||||
impl ActionId {
|
||||
pub const SIZE: usize = Self::_ActionIdSize as usize;
|
||||
|
||||
fn from_usize(val: usize) -> Option<Self> {
|
||||
if val < Self::SIZE {
|
||||
Some(unsafe { transmute::<u8, ActionId>(val as u8) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(from = "BindingSerde", into = "BindingSerde")]
|
||||
pub struct Binding {
|
||||
modifiers: Vec<KeyboardKey>,
|
||||
trigger: InputTrigger,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum InputTrigger {
|
||||
Mouse(MouseButton),
|
||||
Key(KeyboardKey),
|
||||
}
|
||||
|
||||
// ###### everything below is for serialization of key bindings ######
|
||||
|
||||
impl From<InputMap> for Input {
|
||||
fn from(value: InputMap) -> Self {
|
||||
let mut new = Self::default();
|
||||
for (action, saved_bindings) in value {
|
||||
new.bindings[action as usize] = saved_bindings;
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Input> for InputMap {
|
||||
fn from(value: Input) -> Self {
|
||||
value
|
||||
.bindings
|
||||
.iter()
|
||||
.enumerate()
|
||||
// for this to panic, the .bindings array would have to be larger than ActionId::SIZE
|
||||
.map(|(i, b)| (ActionId::from_usize(i).unwrap(), b.clone()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct BindingSerde {
|
||||
modifiers: Vec<String>,
|
||||
trigger: InputTriggerSerde,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
enum InputTriggerSerde {
|
||||
Mouse(String),
|
||||
Key(String),
|
||||
}
|
||||
|
||||
impl From<BindingSerde> for Binding {
|
||||
fn from(value: BindingSerde) -> Self {
|
||||
Self {
|
||||
modifiers: value
|
||||
.modifiers
|
||||
.iter()
|
||||
.map(|s| key_string_to_enum(s))
|
||||
.collect(),
|
||||
trigger: value.trigger.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Binding> for BindingSerde {
|
||||
fn from(value: Binding) -> Self {
|
||||
Self {
|
||||
modifiers: value.modifiers.iter().map(|c| format!("{c:?}")).collect(),
|
||||
trigger: value.trigger.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InputTrigger> for InputTriggerSerde {
|
||||
fn from(value: InputTrigger) -> Self {
|
||||
match value {
|
||||
InputTrigger::Mouse(btn) => InputTriggerSerde::Mouse(format!("{btn:?}")),
|
||||
InputTrigger::Key(key) => InputTriggerSerde::Key(format!("{key:?}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InputTriggerSerde> for InputTrigger {
|
||||
fn from(value: InputTriggerSerde) -> Self {
|
||||
match value {
|
||||
InputTriggerSerde::Mouse(btn) => InputTrigger::Mouse(match btn.as_str() {
|
||||
"MOUSE_BUTTON_LEFT" => MouseButton::MOUSE_BUTTON_LEFT,
|
||||
"MOUSE_BUTTON_RIGHT" => MouseButton::MOUSE_BUTTON_RIGHT,
|
||||
"MOUSE_BUTTON_MIDDLE" => MouseButton::MOUSE_BUTTON_MIDDLE,
|
||||
"MOUSE_BUTTON_SIDE" => MouseButton::MOUSE_BUTTON_SIDE,
|
||||
"MOUSE_BUTTON_EXTRA" => MouseButton::MOUSE_BUTTON_EXTRA,
|
||||
"MOUSE_BUTTON_FORWARD" => MouseButton::MOUSE_BUTTON_FORWARD,
|
||||
"MOUSE_BUTTON_BACK" => MouseButton::MOUSE_BUTTON_BACK,
|
||||
_ => panic!("{btn} not a valid mouse button"),
|
||||
}),
|
||||
InputTriggerSerde::Key(key) => InputTrigger::Key(key_string_to_enum(key.as_str())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_string_to_enum(key: &str) -> KeyboardKey {
|
||||
match key {
|
||||
"KEY_NULL" => KeyboardKey::KEY_NULL,
|
||||
"KEY_APOSTROPHE" => KeyboardKey::KEY_APOSTROPHE,
|
||||
"KEY_COMMA" => KeyboardKey::KEY_COMMA,
|
||||
"KEY_MINUS" => KeyboardKey::KEY_MINUS,
|
||||
"KEY_PERIOD" => KeyboardKey::KEY_PERIOD,
|
||||
"KEY_SLASH" => KeyboardKey::KEY_SLASH,
|
||||
"KEY_ZERO" => KeyboardKey::KEY_ZERO,
|
||||
"KEY_ONE" => KeyboardKey::KEY_ONE,
|
||||
"KEY_TWO" => KeyboardKey::KEY_TWO,
|
||||
"KEY_THREE" => KeyboardKey::KEY_THREE,
|
||||
"KEY_FOUR" => KeyboardKey::KEY_FOUR,
|
||||
"KEY_FIVE" => KeyboardKey::KEY_FIVE,
|
||||
"KEY_SIX" => KeyboardKey::KEY_SIX,
|
||||
"KEY_SEVEN" => KeyboardKey::KEY_SEVEN,
|
||||
"KEY_EIGHT" => KeyboardKey::KEY_EIGHT,
|
||||
"KEY_NINE" => KeyboardKey::KEY_NINE,
|
||||
"KEY_SEMICOLON" => KeyboardKey::KEY_SEMICOLON,
|
||||
"KEY_EQUAL" => KeyboardKey::KEY_EQUAL,
|
||||
"KEY_A" => KeyboardKey::KEY_A,
|
||||
"KEY_B" => KeyboardKey::KEY_B,
|
||||
"KEY_C" => KeyboardKey::KEY_C,
|
||||
"KEY_D" => KeyboardKey::KEY_D,
|
||||
"KEY_E" => KeyboardKey::KEY_E,
|
||||
"KEY_F" => KeyboardKey::KEY_F,
|
||||
"KEY_G" => KeyboardKey::KEY_G,
|
||||
"KEY_H" => KeyboardKey::KEY_H,
|
||||
"KEY_I" => KeyboardKey::KEY_I,
|
||||
"KEY_J" => KeyboardKey::KEY_J,
|
||||
"KEY_K" => KeyboardKey::KEY_K,
|
||||
"KEY_L" => KeyboardKey::KEY_L,
|
||||
"KEY_M" => KeyboardKey::KEY_M,
|
||||
"KEY_N" => KeyboardKey::KEY_N,
|
||||
"KEY_O" => KeyboardKey::KEY_O,
|
||||
"KEY_P" => KeyboardKey::KEY_P,
|
||||
"KEY_Q" => KeyboardKey::KEY_Q,
|
||||
"KEY_R" => KeyboardKey::KEY_R,
|
||||
"KEY_S" => KeyboardKey::KEY_S,
|
||||
"KEY_T" => KeyboardKey::KEY_T,
|
||||
"KEY_U" => KeyboardKey::KEY_U,
|
||||
"KEY_V" => KeyboardKey::KEY_V,
|
||||
"KEY_W" => KeyboardKey::KEY_W,
|
||||
"KEY_X" => KeyboardKey::KEY_X,
|
||||
"KEY_Y" => KeyboardKey::KEY_Y,
|
||||
"KEY_Z" => KeyboardKey::KEY_Z,
|
||||
"KEY_LEFT_BRACKET" => KeyboardKey::KEY_LEFT_BRACKET,
|
||||
"KEY_BACKSLASH" => KeyboardKey::KEY_BACKSLASH,
|
||||
"KEY_RIGHT_BRACKET" => KeyboardKey::KEY_RIGHT_BRACKET,
|
||||
"KEY_GRAVE" => KeyboardKey::KEY_GRAVE,
|
||||
"KEY_SPACE" => KeyboardKey::KEY_SPACE,
|
||||
"KEY_ESCAPE" => KeyboardKey::KEY_ESCAPE,
|
||||
"KEY_ENTER" => KeyboardKey::KEY_ENTER,
|
||||
"KEY_TAB" => KeyboardKey::KEY_TAB,
|
||||
"KEY_BACKSPACE" => KeyboardKey::KEY_BACKSPACE,
|
||||
"KEY_INSERT" => KeyboardKey::KEY_INSERT,
|
||||
"KEY_DELETE" => KeyboardKey::KEY_DELETE,
|
||||
"KEY_RIGHT" => KeyboardKey::KEY_RIGHT,
|
||||
"KEY_LEFT" => KeyboardKey::KEY_LEFT,
|
||||
"KEY_DOWN" => KeyboardKey::KEY_DOWN,
|
||||
"KEY_UP" => KeyboardKey::KEY_UP,
|
||||
"KEY_PAGE_UP" => KeyboardKey::KEY_PAGE_UP,
|
||||
"KEY_PAGE_DOWN" => KeyboardKey::KEY_PAGE_DOWN,
|
||||
"KEY_HOME" => KeyboardKey::KEY_HOME,
|
||||
"KEY_END" => KeyboardKey::KEY_END,
|
||||
"KEY_CAPS_LOCK" => KeyboardKey::KEY_CAPS_LOCK,
|
||||
"KEY_SCROLL_LOCK" => KeyboardKey::KEY_SCROLL_LOCK,
|
||||
"KEY_NUM_LOCK" => KeyboardKey::KEY_NUM_LOCK,
|
||||
"KEY_PRINT_SCREEN" => KeyboardKey::KEY_PRINT_SCREEN,
|
||||
"KEY_PAUSE" => KeyboardKey::KEY_PAUSE,
|
||||
"KEY_F1" => KeyboardKey::KEY_F1,
|
||||
"KEY_F2" => KeyboardKey::KEY_F2,
|
||||
"KEY_F3" => KeyboardKey::KEY_F3,
|
||||
"KEY_F4" => KeyboardKey::KEY_F4,
|
||||
"KEY_F5" => KeyboardKey::KEY_F5,
|
||||
"KEY_F6" => KeyboardKey::KEY_F6,
|
||||
"KEY_F7" => KeyboardKey::KEY_F7,
|
||||
"KEY_F8" => KeyboardKey::KEY_F8,
|
||||
"KEY_F9" => KeyboardKey::KEY_F9,
|
||||
"KEY_F10" => KeyboardKey::KEY_F10,
|
||||
"KEY_F11" => KeyboardKey::KEY_F11,
|
||||
"KEY_F12" => KeyboardKey::KEY_F12,
|
||||
"KEY_LEFT_SHIFT" => KeyboardKey::KEY_LEFT_SHIFT,
|
||||
"KEY_LEFT_CONTROL" => KeyboardKey::KEY_LEFT_CONTROL,
|
||||
"KEY_LEFT_ALT" => KeyboardKey::KEY_LEFT_ALT,
|
||||
"KEY_LEFT_SUPER" => KeyboardKey::KEY_LEFT_SUPER,
|
||||
"KEY_RIGHT_SHIFT" => KeyboardKey::KEY_RIGHT_SHIFT,
|
||||
"KEY_RIGHT_CONTROL" => KeyboardKey::KEY_RIGHT_CONTROL,
|
||||
"KEY_RIGHT_ALT" => KeyboardKey::KEY_RIGHT_ALT,
|
||||
"KEY_RIGHT_SUPER" => KeyboardKey::KEY_RIGHT_SUPER,
|
||||
"KEY_KB_MENU" => KeyboardKey::KEY_KB_MENU,
|
||||
"KEY_KP_0" => KeyboardKey::KEY_KP_0,
|
||||
"KEY_KP_1" => KeyboardKey::KEY_KP_1,
|
||||
"KEY_KP_2" => KeyboardKey::KEY_KP_2,
|
||||
"KEY_KP_3" => KeyboardKey::KEY_KP_3,
|
||||
"KEY_KP_4" => KeyboardKey::KEY_KP_4,
|
||||
"KEY_KP_5" => KeyboardKey::KEY_KP_5,
|
||||
"KEY_KP_6" => KeyboardKey::KEY_KP_6,
|
||||
"KEY_KP_7" => KeyboardKey::KEY_KP_7,
|
||||
"KEY_KP_8" => KeyboardKey::KEY_KP_8,
|
||||
"KEY_KP_9" => KeyboardKey::KEY_KP_9,
|
||||
"KEY_KP_DECIMAL" => KeyboardKey::KEY_KP_DECIMAL,
|
||||
"KEY_KP_DIVIDE" => KeyboardKey::KEY_KP_DIVIDE,
|
||||
"KEY_KP_MULTIPLY" => KeyboardKey::KEY_KP_MULTIPLY,
|
||||
"KEY_KP_SUBTRACT" => KeyboardKey::KEY_KP_SUBTRACT,
|
||||
"KEY_KP_ADD" => KeyboardKey::KEY_KP_ADD,
|
||||
"KEY_KP_ENTER" => KeyboardKey::KEY_KP_ENTER,
|
||||
"KEY_KP_EQUAL" => KeyboardKey::KEY_KP_EQUAL,
|
||||
"KEY_BACK" => KeyboardKey::KEY_BACK,
|
||||
"KEY_VOLUME_UP" => KeyboardKey::KEY_VOLUME_UP,
|
||||
"KEY_VOLUME_DOWN" => KeyboardKey::KEY_VOLUME_DOWN,
|
||||
_ => panic!("{key} not a known key name"),
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue