1pub mod keybindings;
23
24use glam::Vec2;
25use std::collections::HashSet;
26
27#[derive(Clone, Default)]
34pub struct InputState {
35 pub keys_pressed: HashSet<Key>,
38 pub keys_just_pressed: HashSet<Key>,
40 pub keys_just_released: HashSet<Key>,
42
43 pub mouse_x: f32,
46 pub mouse_y: f32,
48 pub mouse_delta: Vec2,
50 pub mouse_ndc: Vec2,
52
53 pub mouse_left: bool,
55 pub mouse_left_just_pressed: bool,
56 pub mouse_left_just_released: bool,
57
58 pub mouse_right: bool,
59 pub mouse_right_just_pressed: bool,
60 pub mouse_right_just_released: bool,
61
62 pub mouse_middle: bool,
63 pub mouse_middle_just_pressed: bool,
64
65 pub scroll_delta: f32,
68
69 pub window_resized: Option<(u32, u32)>,
72 pub quit_requested: bool,
74}
75
76impl InputState {
77 pub fn new() -> Self { Self::default() }
78
79 pub fn is_pressed(&self, key: Key) -> bool {
83 self.keys_pressed.contains(&key)
84 }
85
86 pub fn just_pressed(&self, key: Key) -> bool {
88 self.keys_just_pressed.contains(&key)
89 }
90
91 pub fn just_released(&self, key: Key) -> bool {
93 self.keys_just_released.contains(&key)
94 }
95
96 pub fn any_pressed(&self, keys: &[Key]) -> bool {
98 keys.iter().any(|k| self.keys_pressed.contains(k))
99 }
100
101 pub fn all_pressed(&self, keys: &[Key]) -> bool {
103 keys.iter().all(|k| self.keys_pressed.contains(k))
104 }
105
106 pub fn shift(&self) -> bool {
108 self.is_pressed(Key::LShift) || self.is_pressed(Key::RShift)
109 }
110
111 pub fn ctrl(&self) -> bool {
112 self.is_pressed(Key::LCtrl) || self.is_pressed(Key::RCtrl)
113 }
114
115 pub fn alt(&self) -> bool {
116 self.is_pressed(Key::LAlt) || self.is_pressed(Key::RAlt)
117 }
118
119 pub fn mouse_pos(&self) -> Vec2 {
123 Vec2::new(self.mouse_x, self.mouse_y)
124 }
125
126 pub fn mouse_left_down(&self) -> bool { self.mouse_left }
128
129 pub fn mouse_left_click(&self) -> bool { self.mouse_left_just_pressed }
131
132 pub fn mouse_right_down(&self) -> bool { self.mouse_right }
134
135 pub fn mouse_right_click(&self) -> bool { self.mouse_right_just_pressed }
137
138 pub fn mouse_drag(&self) -> bool {
140 self.mouse_left && self.mouse_delta.length_squared() > f32::EPSILON
141 }
142
143 pub fn mouse_delta_ndc(&self, window_width: u32, window_height: u32) -> Vec2 {
145 Vec2::new(
146 self.mouse_delta.x / window_width.max(1) as f32 * 2.0,
147 -self.mouse_delta.y / window_height.max(1) as f32 * 2.0,
148 )
149 }
150
151 pub fn wasd(&self) -> Vec2 {
156 let x = if self.is_pressed(Key::D) || self.is_pressed(Key::Right) { 1.0 }
157 else if self.is_pressed(Key::A) || self.is_pressed(Key::Left) { -1.0 }
158 else { 0.0 };
159 let y = if self.is_pressed(Key::W) || self.is_pressed(Key::Up) { 1.0 }
160 else if self.is_pressed(Key::S) || self.is_pressed(Key::Down) { -1.0 }
161 else { 0.0 };
162 Vec2::new(x, y)
163 }
164
165 pub fn arrows(&self) -> Vec2 {
167 let x = if self.is_pressed(Key::Right) { 1.0 }
168 else if self.is_pressed(Key::Left) { -1.0 }
169 else { 0.0 };
170 let y = if self.is_pressed(Key::Up) { 1.0 }
171 else if self.is_pressed(Key::Down) { -1.0 }
172 else { 0.0 };
173 Vec2::new(x, y)
174 }
175
176 pub fn clear_frame(&mut self) {
180 self.keys_just_pressed.clear();
181 self.keys_just_released.clear();
182 self.scroll_delta = 0.0;
183 self.mouse_delta = Vec2::ZERO;
184 self.mouse_left_just_pressed = false;
185 self.mouse_left_just_released = false;
186 self.mouse_right_just_pressed = false;
187 self.mouse_right_just_released = false;
188 self.mouse_middle_just_pressed = false;
189 self.window_resized = None;
190 self.quit_requested = false;
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
201pub enum Key {
202 A, B, C, D, E, F, G, H, I, J, K, L, M,
204 N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
205
206 Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
208
209 Up, Down, Left, Right,
211 PageUp, PageDown,
212 Home, End,
213 Insert, Delete,
214
215 Enter, Escape, Space, Backspace, Tab,
217
218 F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
220
221 Slash, Backslash, Period, Comma, Semicolon, Quote,
223 LBracket, RBracket, Minus, Equals, Backtick,
224
225 LShift, RShift, LCtrl, RCtrl, LAlt, RAlt,
227}
228
229impl Key {
230 pub fn display_name(&self) -> &'static str {
232 match self {
233 Key::A => "A", Key::B => "B", Key::C => "C", Key::D => "D",
234 Key::E => "E", Key::F => "F", Key::G => "G", Key::H => "H",
235 Key::I => "I", Key::J => "J", Key::K => "K", Key::L => "L",
236 Key::M => "M", Key::N => "N", Key::O => "O", Key::P => "P",
237 Key::Q => "Q", Key::R => "R", Key::S => "S", Key::T => "T",
238 Key::U => "U", Key::V => "V", Key::W => "W", Key::X => "X",
239 Key::Y => "Y", Key::Z => "Z",
240 Key::Num0 => "0", Key::Num1 => "1", Key::Num2 => "2", Key::Num3 => "3",
241 Key::Num4 => "4", Key::Num5 => "5", Key::Num6 => "6", Key::Num7 => "7",
242 Key::Num8 => "8", Key::Num9 => "9",
243 Key::Up => "↑", Key::Down => "↓", Key::Left => "←", Key::Right => "→",
244 Key::PageUp => "PgUp", Key::PageDown => "PgDn",
245 Key::Home => "Home", Key::End => "End",
246 Key::Insert => "Ins", Key::Delete => "Del",
247 Key::Enter => "Enter", Key::Escape => "Esc", Key::Space => "Space",
248 Key::Backspace => "Backspace", Key::Tab => "Tab",
249 Key::F1 => "F1", Key::F2 => "F2", Key::F3 => "F3", Key::F4 => "F4",
250 Key::F5 => "F5", Key::F6 => "F6", Key::F7 => "F7", Key::F8 => "F8",
251 Key::F9 => "F9", Key::F10 => "F10", Key::F11 => "F11", Key::F12 => "F12",
252 Key::Slash => "/", Key::Backslash => "\\", Key::Period => ".",
253 Key::Comma => ",", Key::Semicolon => ";", Key::Quote => "'",
254 Key::LBracket => "[", Key::RBracket => "]",
255 Key::Minus => "-", Key::Equals => "=", Key::Backtick => "`",
256 Key::LShift | Key::RShift => "Shift",
257 Key::LCtrl | Key::RCtrl => "Ctrl",
258 Key::LAlt | Key::RAlt => "Alt",
259 }
260 }
261
262 pub fn all_bindable() -> &'static [Key] {
264 &[
265 Key::A, Key::B, Key::C, Key::D, Key::E, Key::F, Key::G, Key::H,
266 Key::I, Key::J, Key::K, Key::L, Key::M, Key::N, Key::O, Key::P,
267 Key::Q, Key::R, Key::S, Key::T, Key::U, Key::V, Key::W, Key::X,
268 Key::Y, Key::Z,
269 Key::Num0, Key::Num1, Key::Num2, Key::Num3, Key::Num4,
270 Key::Num5, Key::Num6, Key::Num7, Key::Num8, Key::Num9,
271 Key::Up, Key::Down, Key::Left, Key::Right,
272 Key::Enter, Key::Escape, Key::Space, Key::Backspace, Key::Tab,
273 Key::F1, Key::F2, Key::F3, Key::F4, Key::F5, Key::F6,
274 Key::F7, Key::F8, Key::F9, Key::F10, Key::F11, Key::F12,
275 ]
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 #[test]
284 fn clear_frame_resets_transient() {
285 let mut input = InputState::new();
286 input.keys_just_pressed.insert(Key::Space);
287 input.scroll_delta = 3.0;
288 input.mouse_delta = Vec2::new(10.0, 5.0);
289 input.clear_frame();
290 assert!(input.keys_just_pressed.is_empty());
291 assert_eq!(input.scroll_delta, 0.0);
292 assert_eq!(input.mouse_delta, Vec2::ZERO);
293 }
294
295 #[test]
296 fn wasd_returns_correct_direction() {
297 let mut input = InputState::new();
298 input.keys_pressed.insert(Key::D);
299 input.keys_pressed.insert(Key::W);
300 let v = input.wasd();
301 assert!(v.x > 0.0 && v.y > 0.0);
302 }
303
304 #[test]
305 fn shift_ctrl_helpers() {
306 let mut input = InputState::new();
307 assert!(!input.shift());
308 input.keys_pressed.insert(Key::LShift);
309 assert!(input.shift());
310 assert!(!input.ctrl());
311 }
312
313 #[test]
314 fn mouse_drag_only_when_moving() {
315 let mut input = InputState::new();
316 input.mouse_left = true;
317 assert!(!input.mouse_drag()); input.mouse_delta = Vec2::new(3.0, 0.0);
319 assert!(input.mouse_drag());
320 }
321}