leafwing_input_manager/user_input/
keyboard.rs

1//! Keyboard inputs
2
3use bevy::ecs::message::Messages;
4use bevy::ecs::system::lifetimeless::SRes;
5use bevy::ecs::system::StaticSystemParam;
6use bevy::input::keyboard::{Key, KeyboardInput, NativeKey};
7use bevy::input::{ButtonInput, ButtonState};
8use bevy::prelude::{Entity, KeyCode, Reflect, ResMut, World};
9use leafwing_input_manager_macros::serde_typetag;
10use serde::{Deserialize, Serialize};
11
12use crate as leafwing_input_manager;
13use crate::buttonlike::ButtonValue;
14use crate::clashing_inputs::BasicInputs;
15use crate::user_input::{ButtonlikeChord, UserInput};
16use crate::InputControlKind;
17
18use super::updating::{CentralInputStore, UpdatableInput};
19use super::Buttonlike;
20
21// Built-in support for Bevy's KeyCode
22impl UserInput for KeyCode {
23    /// [`KeyCode`] acts as a button.
24    #[inline]
25    fn kind(&self) -> InputControlKind {
26        InputControlKind::Button
27    }
28
29    /// Returns a [`BasicInputs`] that only contains the [`KeyCode`] itself,
30    /// as it represents a simple physical button.
31    #[inline]
32    fn decompose(&self) -> BasicInputs {
33        BasicInputs::Simple(Box::new(*self))
34    }
35}
36
37impl UpdatableInput for KeyCode {
38    type SourceData = SRes<ButtonInput<KeyCode>>;
39
40    fn compute(
41        mut central_input_store: ResMut<CentralInputStore>,
42        source_data: StaticSystemParam<Self::SourceData>,
43    ) {
44        for key in source_data.get_pressed() {
45            central_input_store.update_buttonlike(*key, ButtonValue::from_pressed(true));
46        }
47
48        for key in source_data.get_just_released() {
49            central_input_store.update_buttonlike(*key, ButtonValue::from_pressed(false));
50        }
51    }
52}
53
54#[serde_typetag]
55impl Buttonlike for KeyCode {
56    /// Checks if the specified key is currently pressed down.
57    #[inline]
58    fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
59        input_store.pressed(self)
60    }
61
62    /// Sends a fake [`KeyboardInput`] message to the world with [`ButtonState::Pressed`].
63    ///
64    /// # Note
65    ///
66    /// The `logical_key` and `window` fields will be filled with placeholder values.
67    fn press(&self, world: &mut World) {
68        let mut messages = world.resource_mut::<Messages<KeyboardInput>>();
69        messages.write(KeyboardInput {
70            key_code: *self,
71            logical_key: Key::Unidentified(NativeKey::Unidentified),
72            state: ButtonState::Pressed,
73            repeat: false,
74            window: Entity::PLACEHOLDER,
75            text: None,
76        });
77    }
78
79    /// Sends a fake [`KeyboardInput`] message to the world with [`ButtonState::Released`].
80    ///
81    /// # Note
82    ///
83    /// The `logical_key` and `window` fields will be filled with placeholder values.
84    fn release(&self, world: &mut World) {
85        let mut messages = world.resource_mut::<Messages<KeyboardInput>>();
86        messages.write(KeyboardInput {
87            key_code: *self,
88            logical_key: Key::Unidentified(NativeKey::Unidentified),
89            state: ButtonState::Released,
90            repeat: false,
91            window: Entity::PLACEHOLDER,
92            text: None,
93        });
94    }
95
96    /// If the value is greater than `0.0`, press the key; otherwise release it.
97    fn set_value(&self, world: &mut World, value: f32) {
98        if value > 0.0 {
99            self.press(world);
100        } else {
101            self.release(world);
102        }
103    }
104}
105
106/// Keyboard modifiers like Alt, Control, Shift, and Super (OS symbol key).
107///
108/// Each variant represents a pair of [`KeyCode`]s, the left and right version of the modifier key,
109/// allowing for handling modifiers regardless of which side is pressed.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
111#[must_use]
112pub enum ModifierKey {
113    /// The Alt key, representing either [`KeyCode::AltLeft`] or [`KeyCode::AltRight`].
114    Alt,
115
116    /// The Control key, representing either [`KeyCode::ControlLeft`] or [`KeyCode::ControlRight`].
117    Control,
118
119    /// The Shift key, representing either [`KeyCode::ShiftLeft`] or [`KeyCode::ShiftRight`].
120    Shift,
121
122    /// The Super (OS symbol) key, representing either [`KeyCode::SuperLeft`] or [`KeyCode::SuperRight`].
123    Super,
124}
125
126impl ModifierKey {
127    /// Returns a pair of [`KeyCode`]s corresponding to both modifier keys.
128    #[must_use]
129    #[inline]
130    pub const fn keycodes(&self) -> [KeyCode; 2] {
131        [self.left(), self.right()]
132    }
133
134    /// Returns the [`KeyCode`] corresponding to the left modifier key.
135    #[must_use]
136    #[inline]
137    pub const fn left(&self) -> KeyCode {
138        match self {
139            ModifierKey::Alt => KeyCode::AltLeft,
140            ModifierKey::Control => KeyCode::ControlLeft,
141            ModifierKey::Shift => KeyCode::ShiftLeft,
142            ModifierKey::Super => KeyCode::SuperLeft,
143        }
144    }
145
146    /// Returns the [`KeyCode`] corresponding to the right modifier key.
147    #[must_use]
148    #[inline]
149    pub const fn right(&self) -> KeyCode {
150        match self {
151            ModifierKey::Alt => KeyCode::AltRight,
152            ModifierKey::Control => KeyCode::ControlRight,
153            ModifierKey::Shift => KeyCode::ShiftRight,
154            ModifierKey::Super => KeyCode::SuperRight,
155        }
156    }
157
158    /// Create an [`ButtonlikeChord`] that includes this [`ModifierKey`] and the given `input`.
159    #[inline]
160    pub fn with(&self, other: impl Buttonlike) -> ButtonlikeChord {
161        ButtonlikeChord::from_single(*self).with(other)
162    }
163}
164
165impl UserInput for ModifierKey {
166    /// [`ModifierKey`] acts as a button.
167    #[inline]
168    fn kind(&self) -> InputControlKind {
169        InputControlKind::Button
170    }
171
172    /// Returns the two [`KeyCode`]s used by this [`ModifierKey`].
173    #[inline]
174    fn decompose(&self) -> BasicInputs {
175        BasicInputs::Composite(vec![Box::new(self.left()), Box::new(self.right())])
176    }
177}
178
179#[serde_typetag]
180impl Buttonlike for ModifierKey {
181    /// Checks if the specified modifier key is currently pressed down.
182    #[inline]
183    fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
184        let left = input_store.pressed(&self.left());
185        let right = input_store.pressed(&self.right());
186        if (None, None) == (left, right) {
187            None
188        } else {
189            Some(left.unwrap_or(false) || right.unwrap_or(false))
190        }
191    }
192
193    /// Sends a fake [`KeyboardInput`] message to the world with [`ButtonState::Pressed`].
194    ///
195    /// The left and right keys will be pressed simultaneously.
196    ///
197    /// # Note
198    ///
199    /// The `logical_key` and `window` fields will be filled with placeholder values.
200    fn press(&self, world: &mut World) {
201        self.left().press(world);
202        self.right().press(world);
203    }
204
205    /// Sends a fake [`KeyboardInput`] message to the world with [`ButtonState::Released`].
206    ///
207    /// The left and right keys will be released simultaneously.
208    ///
209    /// # Note
210    ///
211    /// The `logical_key` and `window` fields will be filled with placeholder values.
212    fn release(&self, world: &mut World) {
213        self.left().release(world);
214        self.right().release(world);
215    }
216
217    /// If the value is greater than `0.0`, press the keys; otherwise release it.
218    fn set_value(&self, world: &mut World, value: f32) {
219        if value > 0.0 {
220            self.press(world);
221        } else {
222            self.release(world);
223        }
224    }
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use crate::plugin::CentralInputStorePlugin;
231    use bevy::input::InputPlugin;
232    use bevy::prelude::*;
233
234    fn test_app() -> App {
235        let mut app = App::new();
236        app.add_plugins(InputPlugin)
237            .add_plugins(CentralInputStorePlugin);
238        app
239    }
240
241    #[test]
242    fn test_keyboard_input() {
243        let up = KeyCode::ArrowUp;
244        assert_eq!(up.kind(), InputControlKind::Button);
245
246        let left = KeyCode::ArrowLeft;
247        assert_eq!(left.kind(), InputControlKind::Button);
248
249        let alt = ModifierKey::Alt;
250        assert_eq!(alt.kind(), InputControlKind::Button);
251
252        // No inputs
253        let mut app = test_app();
254        app.update();
255        let gamepad = app.world_mut().spawn(()).id();
256        let inputs = app.world().resource::<CentralInputStore>();
257
258        assert!(!up.pressed(inputs, gamepad));
259        assert!(!left.pressed(inputs, gamepad));
260        assert!(!alt.pressed(inputs, gamepad));
261
262        // Press arrow up
263        let mut app = test_app();
264        KeyCode::ArrowUp.press(app.world_mut());
265        app.update();
266        let inputs = app.world().resource::<CentralInputStore>();
267
268        assert!(up.pressed(inputs, gamepad));
269        assert!(!left.pressed(inputs, gamepad));
270        assert!(!alt.pressed(inputs, gamepad));
271
272        // Press arrow down
273        let mut app = test_app();
274        KeyCode::ArrowDown.press(app.world_mut());
275        app.update();
276        let inputs = app.world().resource::<CentralInputStore>();
277
278        assert!(!up.pressed(inputs, gamepad));
279        assert!(!left.pressed(inputs, gamepad));
280        assert!(!alt.pressed(inputs, gamepad));
281
282        // Press arrow left
283        let mut app = test_app();
284        KeyCode::ArrowLeft.press(app.world_mut());
285        app.update();
286        let inputs = app.world().resource::<CentralInputStore>();
287
288        assert!(!up.pressed(inputs, gamepad));
289        assert!(left.pressed(inputs, gamepad));
290        assert!(!alt.pressed(inputs, gamepad));
291
292        // Press arrow down and arrow up
293        let mut app = test_app();
294        KeyCode::ArrowDown.press(app.world_mut());
295        KeyCode::ArrowUp.press(app.world_mut());
296        app.update();
297        let inputs = app.world().resource::<CentralInputStore>();
298
299        assert!(up.pressed(inputs, gamepad));
300        assert!(!left.pressed(inputs, gamepad));
301        assert!(!alt.pressed(inputs, gamepad));
302
303        // Press arrow left and arrow up
304        let mut app = test_app();
305        KeyCode::ArrowLeft.press(app.world_mut());
306        KeyCode::ArrowUp.press(app.world_mut());
307        app.update();
308        let inputs = app.world().resource::<CentralInputStore>();
309
310        assert!(up.pressed(inputs, gamepad));
311        assert!(left.pressed(inputs, gamepad));
312        assert!(!alt.pressed(inputs, gamepad));
313
314        // Press left Alt
315        let mut app = test_app();
316        KeyCode::AltLeft.press(app.world_mut());
317        app.update();
318        let inputs = app.world().resource::<CentralInputStore>();
319
320        assert!(!up.pressed(inputs, gamepad));
321        assert!(!left.pressed(inputs, gamepad));
322        assert!(alt.pressed(inputs, gamepad));
323
324        // Press right Alt
325        let mut app = test_app();
326        KeyCode::AltRight.press(app.world_mut());
327        app.update();
328        let inputs = app.world().resource::<CentralInputStore>();
329
330        assert!(!up.pressed(inputs, gamepad));
331        assert!(!left.pressed(inputs, gamepad));
332        assert!(alt.pressed(inputs, gamepad));
333    }
334}