diff options
author | Akshay <[email protected]> | 2021-05-08 16:55:47 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-05-08 16:55:47 +0100 |
commit | 591d2b6167af53ce07b060711a4074f1e19c5f3f (patch) | |
tree | 6299e12574732b063aa57c977104eacf938e4eda /src/keybind.rs | |
parent | 225351e7c98e91451e0b02fd2b9c3060ebd153a7 (diff) |
add basic user-definable keybinds
Diffstat (limited to 'src/keybind.rs')
-rw-r--r-- | src/keybind.rs | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/keybind.rs b/src/keybind.rs new file mode 100644 index 0000000..8ab479a --- /dev/null +++ b/src/keybind.rs | |||
@@ -0,0 +1,146 @@ | |||
1 | use std::{fmt, str::FromStr}; | ||
2 | |||
3 | use sdl2::keyboard::{Keycode, Mod}; | ||
4 | |||
5 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
6 | pub struct Keybind { | ||
7 | keycode: Keycode, | ||
8 | keymod: Mod, | ||
9 | } | ||
10 | |||
11 | impl Keybind { | ||
12 | pub fn new(keycode: Keycode, keymod: Mod) -> Self { | ||
13 | Self { keycode, keymod } | ||
14 | } | ||
15 | } | ||
16 | |||
17 | // ctrl keys: C-<letter> len 3 | ||
18 | // shift keys: S-<letter> len 3 | ||
19 | // normal keys: <letter> len 1 | ||
20 | |||
21 | #[derive(Debug, Copy, Clone, PartialEq)] | ||
22 | pub enum KeybindError { | ||
23 | Parse, | ||
24 | InvalidMod, | ||
25 | InvalidKey, | ||
26 | } | ||
27 | |||
28 | impl fmt::Display for KeybindError { | ||
29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
30 | match self { | ||
31 | Self::Parse => write!(f, "error parsing keybind"), | ||
32 | Self::InvalidKey => write!(f, "unsupported keybind, try `A-Z0-9`"), | ||
33 | Self::InvalidMod => { | ||
34 | write!(f, "unsupported keybind modifier, try `C-<key> or S-<key>`") | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl std::error::Error for KeybindError {} | ||
41 | |||
42 | impl FromStr for Keybind { | ||
43 | type Err = KeybindError; | ||
44 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
45 | parse_from_str(s) | ||
46 | } | ||
47 | } | ||
48 | |||
49 | fn parse_from_str(s: &str) -> Result<Keybind, KeybindError> { | ||
50 | let s = s.to_lowercase(); | ||
51 | match s.len() { | ||
52 | // either ctrl or shift key | ||
53 | 3 => { | ||
54 | let keymod = parse_modifier(&s[..2])?; | ||
55 | let keybind = parse_keybind(&s[2..])?; | ||
56 | Ok(Keybind::new(keybind, keymod)) | ||
57 | } | ||
58 | 1 => { | ||
59 | let keybind = parse_keybind(&s)?; | ||
60 | Ok(Keybind::new(keybind, Mod::NOMOD)) | ||
61 | } | ||
62 | _ => Err(KeybindError::Parse), | ||
63 | } | ||
64 | } | ||
65 | |||
66 | fn parse_modifier(s: &str) -> Result<Mod, KeybindError> { | ||
67 | match s { | ||
68 | "c-" => Ok(Mod::LCTRLMOD), | ||
69 | "s-" => Ok(Mod::LSHIFTMOD), | ||
70 | _ => Err(KeybindError::InvalidMod), | ||
71 | } | ||
72 | } | ||
73 | |||
74 | fn parse_keybind(s: &str) -> Result<Keycode, KeybindError> { | ||
75 | match s.to_uppercase().as_str() { | ||
76 | "0" => Ok(Keycode::Num0), | ||
77 | "1" => Ok(Keycode::Num1), | ||
78 | "2" => Ok(Keycode::Num2), | ||
79 | "3" => Ok(Keycode::Num3), | ||
80 | "4" => Ok(Keycode::Num4), | ||
81 | "5" => Ok(Keycode::Num5), | ||
82 | "6" => Ok(Keycode::Num6), | ||
83 | "7" => Ok(Keycode::Num7), | ||
84 | "8" => Ok(Keycode::Num8), | ||
85 | "9" => Ok(Keycode::Num9), | ||
86 | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | ||
87 | | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" => { | ||
88 | Ok(Keycode::from_name(s).unwrap()) | ||
89 | } | ||
90 | _ => Err(KeybindError::InvalidKey), | ||
91 | } | ||
92 | } | ||
93 | |||
94 | #[cfg(test)] | ||
95 | mod tests { | ||
96 | use super::*; | ||
97 | #[test] | ||
98 | fn without_mod() { | ||
99 | assert_eq!( | ||
100 | Keybind::from_str("x").unwrap(), | ||
101 | Keybind::new(Keycode::X, Mod::NOMOD) | ||
102 | ); | ||
103 | assert_eq!( | ||
104 | Keybind::from_str("X").unwrap(), | ||
105 | Keybind::new(Keycode::X, Mod::NOMOD) | ||
106 | ); | ||
107 | } | ||
108 | #[test] | ||
109 | fn with_ctrl_mod() { | ||
110 | assert_eq!( | ||
111 | Keybind::from_str("c-x").unwrap(), | ||
112 | Keybind::new(Keycode::X, Mod::LCTRLMOD) | ||
113 | ); | ||
114 | assert_eq!( | ||
115 | Keybind::from_str("C-x").unwrap(), | ||
116 | Keybind::new(Keycode::X, Mod::LCTRLMOD) | ||
117 | ); | ||
118 | assert_eq!( | ||
119 | Keybind::from_str("c-X").unwrap(), | ||
120 | Keybind::new(Keycode::X, Mod::LCTRLMOD) | ||
121 | ); | ||
122 | assert_eq!( | ||
123 | Keybind::from_str("C-X").unwrap(), | ||
124 | Keybind::new(Keycode::X, Mod::LCTRLMOD) | ||
125 | ); | ||
126 | } | ||
127 | #[test] | ||
128 | fn with_shift_mod() { | ||
129 | assert_eq!( | ||
130 | Keybind::from_str("s-x").unwrap(), | ||
131 | Keybind::new(Keycode::X, Mod::LSHIFTMOD) | ||
132 | ); | ||
133 | assert_eq!( | ||
134 | Keybind::from_str("S-x").unwrap(), | ||
135 | Keybind::new(Keycode::X, Mod::LSHIFTMOD) | ||
136 | ); | ||
137 | assert_eq!( | ||
138 | Keybind::from_str("s-X").unwrap(), | ||
139 | Keybind::new(Keycode::X, Mod::LSHIFTMOD) | ||
140 | ); | ||
141 | assert_eq!( | ||
142 | Keybind::from_str("S-X").unwrap(), | ||
143 | Keybind::new(Keycode::X, Mod::LSHIFTMOD) | ||
144 | ); | ||
145 | } | ||
146 | } | ||