Skip to main content

oxide_input/
keyboard.rs

1use std::collections::HashSet;
2
3use oxide_ecs::Resource;
4use winit::keyboard::{KeyCode, PhysicalKey};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum ButtonState {
8    Released,
9    JustPressed,
10    Pressed,
11    JustReleased,
12}
13
14#[derive(Resource, Default)]
15pub struct KeyboardInput {
16    keys: HashSet<KeyCode>,
17    just_pressed: HashSet<KeyCode>,
18    just_released: HashSet<KeyCode>,
19}
20
21impl KeyboardInput {
22    pub fn update(&mut self) {
23        let to_press: Vec<_> = self.just_pressed.drain().collect();
24        for key in to_press {
25            self.keys.insert(key);
26        }
27        self.just_released.clear();
28    }
29
30    /// Processes a keyboard event using winit's physical key mapping.
31    /// Physical keys keep positional bindings stable across keyboard layouts.
32    pub fn process_event(&mut self, key: PhysicalKey, pressed: bool) {
33        if let PhysicalKey::Code(code) = key {
34            if pressed && !self.keys.contains(&code) {
35                self.just_pressed.insert(code);
36            } else if !pressed && self.keys.contains(&code) {
37                self.keys.remove(&code);
38                self.just_released.insert(code);
39            }
40        }
41    }
42
43    pub fn pressed(&self, key: KeyCode) -> bool {
44        self.keys.contains(&key)
45    }
46
47    pub fn just_pressed(&self, key: KeyCode) -> bool {
48        self.just_pressed.contains(&key)
49    }
50
51    pub fn just_released(&self, key: KeyCode) -> bool {
52        self.just_released.contains(&key)
53    }
54
55    pub fn button_state(&self, key: KeyCode) -> ButtonState {
56        if self.just_pressed.contains(&key) {
57            ButtonState::JustPressed
58        } else if self.just_released.contains(&key) {
59            ButtonState::JustReleased
60        } else if self.keys.contains(&key) {
61            ButtonState::Pressed
62        } else {
63            ButtonState::Released
64        }
65    }
66}