Skip to main content

dreamwell_engine/input/
button.rs

1// ButtonState<T> — generic three-state input tracker.
2// Adapted from Bevy's ButtonInput<T>. Tracks pressed, just_pressed, just_released per frame.
3
4use std::collections::HashSet;
5use std::hash::Hash;
6
7/// Generic button state tracker. Tracks current, just-pressed, and just-released states.
8/// `T` is a key/button identifier (e.g., `VirtualKey`, `MouseButton`).
9///
10/// Usage: call `press()`/`release()` when events arrive, query `pressed()`/`just_pressed()`/
11/// `just_released()`, then call `clear_just()` at end of frame.
12pub struct ButtonState<T: Eq + Hash + Copy> {
13    pressed: HashSet<T>,
14    just_pressed: HashSet<T>,
15    just_released: HashSet<T>,
16}
17
18impl<T: Eq + Hash + Copy> ButtonState<T> {
19    pub fn new() -> Self {
20        Self {
21            pressed: HashSet::new(),
22            just_pressed: HashSet::new(),
23            just_released: HashSet::new(),
24        }
25    }
26
27    /// Register a button press. Sets pressed + just_pressed.
28    pub fn press(&mut self, button: T) {
29        if self.pressed.insert(button) {
30            self.just_pressed.insert(button);
31        }
32    }
33
34    /// Register a button release. Clears pressed, sets just_released.
35    pub fn release(&mut self, button: T) {
36        if self.pressed.remove(&button) {
37            self.just_released.insert(button);
38        }
39    }
40
41    /// Whether the button is currently held down.
42    pub fn pressed(&self, button: T) -> bool {
43        self.pressed.contains(&button)
44    }
45
46    /// Whether the button was pressed this frame.
47    pub fn just_pressed(&self, button: T) -> bool {
48        self.just_pressed.contains(&button)
49    }
50
51    /// Whether the button was released this frame.
52    pub fn just_released(&self, button: T) -> bool {
53        self.just_released.contains(&button)
54    }
55
56    /// Clear just_pressed and just_released. Call at end of frame.
57    pub fn clear_just(&mut self) {
58        self.just_pressed.clear();
59        self.just_released.clear();
60    }
61
62    /// Reset all state.
63    pub fn reset(&mut self) {
64        self.pressed.clear();
65        self.just_pressed.clear();
66        self.just_released.clear();
67    }
68
69    /// All currently pressed buttons.
70    pub fn get_pressed(&self) -> impl Iterator<Item = &T> {
71        self.pressed.iter()
72    }
73
74    /// All buttons just pressed this frame.
75    pub fn get_just_pressed(&self) -> impl Iterator<Item = &T> {
76        self.just_pressed.iter()
77    }
78
79    /// All buttons just released this frame.
80    pub fn get_just_released(&self) -> impl Iterator<Item = &T> {
81        self.just_released.iter()
82    }
83
84    /// Number of currently pressed buttons.
85    pub fn pressed_count(&self) -> usize {
86        self.pressed.len()
87    }
88
89    /// Whether any button is pressed.
90    pub fn any_pressed(&self) -> bool {
91        !self.pressed.is_empty()
92    }
93
94    /// Whether any button was just pressed this frame.
95    pub fn any_just_pressed(&self) -> bool {
96        !self.just_pressed.is_empty()
97    }
98}
99
100impl<T: Eq + Hash + Copy> Default for ButtonState<T> {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111    enum TestButton {
112        A,
113        B,
114    }
115
116    #[test]
117    fn press_and_query() {
118        let mut state = ButtonState::new();
119        state.press(TestButton::A);
120
121        assert!(state.pressed(TestButton::A));
122        assert!(state.just_pressed(TestButton::A));
123        assert!(!state.just_released(TestButton::A));
124        assert!(!state.pressed(TestButton::B));
125    }
126
127    #[test]
128    fn release_and_query() {
129        let mut state = ButtonState::new();
130        state.press(TestButton::A);
131        state.clear_just();
132        state.release(TestButton::A);
133
134        assert!(!state.pressed(TestButton::A));
135        assert!(state.just_released(TestButton::A));
136        assert!(!state.just_pressed(TestButton::A));
137    }
138
139    #[test]
140    fn clear_just_resets_transient() {
141        let mut state = ButtonState::new();
142        state.press(TestButton::A);
143        assert!(state.just_pressed(TestButton::A));
144
145        state.clear_just();
146        assert!(state.pressed(TestButton::A));
147        assert!(!state.just_pressed(TestButton::A));
148    }
149
150    #[test]
151    fn double_press_no_duplicate_just() {
152        let mut state = ButtonState::new();
153        state.press(TestButton::A);
154        state.press(TestButton::A); // already pressed
155        assert!(state.pressed(TestButton::A));
156        assert_eq!(state.pressed_count(), 1);
157    }
158
159    #[test]
160    fn release_without_press_noop() {
161        let mut state = ButtonState::new();
162        state.release(TestButton::A);
163        assert!(!state.just_released(TestButton::A));
164    }
165
166    #[test]
167    fn reset_clears_everything() {
168        let mut state = ButtonState::new();
169        state.press(TestButton::A);
170        state.press(TestButton::B);
171        state.reset();
172
173        assert!(!state.pressed(TestButton::A));
174        assert!(!state.pressed(TestButton::B));
175        assert!(!state.any_pressed());
176        assert!(!state.any_just_pressed());
177    }
178
179    #[test]
180    fn multiple_buttons() {
181        let mut state = ButtonState::new();
182        state.press(TestButton::A);
183        state.press(TestButton::B);
184
185        assert_eq!(state.pressed_count(), 2);
186        assert!(state.any_pressed());
187
188        state.release(TestButton::A);
189        assert_eq!(state.pressed_count(), 1);
190        assert!(state.pressed(TestButton::B));
191    }
192
193    #[test]
194    fn frame_lifecycle() {
195        let mut state = ButtonState::new();
196
197        // Frame 1: press A
198        state.press(TestButton::A);
199        assert!(state.just_pressed(TestButton::A));
200        state.clear_just();
201
202        // Frame 2: A still held, press B
203        assert!(state.pressed(TestButton::A));
204        assert!(!state.just_pressed(TestButton::A));
205        state.press(TestButton::B);
206        assert!(state.just_pressed(TestButton::B));
207        state.clear_just();
208
209        // Frame 3: release A
210        state.release(TestButton::A);
211        assert!(state.just_released(TestButton::A));
212        assert!(!state.pressed(TestButton::A));
213        assert!(state.pressed(TestButton::B));
214    }
215}