Skip to main content

leafwing_input_manager/
input_map.rs

1//! This module contains [`InputMap`] and its supporting methods and impls.
2
3use std::fmt::Debug;
4use std::hash::Hash;
5
6#[cfg(feature = "asset")]
7use bevy::asset::Asset;
8use bevy::input::gamepad::Gamepad;
9use bevy::math::{Vec2, Vec3};
10use bevy::platform::collections::HashMap;
11use bevy::prelude::{Component, Deref, DerefMut, Entity, Query, Reflect, With};
12use bevy::{log::error, prelude::ReflectComponent};
13use itertools::Itertools;
14use serde::{Deserialize, Serialize};
15
16use crate::buttonlike::ButtonValue;
17use crate::clashing_inputs::ClashStrategy;
18use crate::prelude::updating::CentralInputStore;
19use crate::prelude::{ActionState, UserInputWrapper};
20use crate::user_input::{Axislike, Buttonlike, DualAxislike, TripleAxislike};
21use crate::{Actionlike, InputControlKind};
22
23#[cfg(feature = "gamepad")]
24use crate::user_input::gamepad::find_gamepad;
25
26#[cfg(not(feature = "gamepad"))]
27fn find_gamepad(_: Option<Query<Entity, With<Gamepad>>>) -> Entity {
28    Entity::PLACEHOLDER
29}
30
31/// A Multi-Map that allows you to map actions to multiple [`UserInputs`](crate::user_input::UserInput)s,
32/// whether they are [`Buttonlike`], [`Axislike`], [`DualAxislike`], or [`TripleAxislike`].
33///
34/// When inserting a binding, the [`InputControlKind`] of the action variant must match that of the input type.
35/// Use [`InputMap::insert`] to insert buttonlike inputs,
36/// [`InputMap::insert_axis`] to insert axislike inputs,
37/// and [`InputMap::insert_dual_axis`] to insert dual-axislike inputs.
38///
39/// # One-to-Many Mapping
40///
41/// You can associate multiple [`Buttonlike`]s (e.g., keyboard keys, mouse buttons, gamepad buttons)
42/// with a single action, simplifying handling complex input combinations for the same action.
43/// Duplicate associations are ignored.
44///
45/// # Many-to-One Mapping
46///
47/// A single [`Buttonlike`] can be mapped to multiple actions simultaneously.
48/// This allows flexibility in defining alternative ways to trigger an action.
49///
50/// # Clash Resolution
51///
52/// By default, the [`InputMap`] prioritizes larger [`Buttonlike`] combinations to trigger actions.
53/// This means if two actions share some inputs, and one action requires all the inputs
54/// of the other plus additional ones; only the larger combination will be registered.
55///
56/// This avoids unintended actions from being triggered by more specific input combinations.
57/// For example, pressing both `S` and `Ctrl + S` in your text editor app
58/// would only save your file (the larger combination), and not enter the letter `s`.
59///
60/// This behavior can be customized using the [`ClashStrategy`] resource.
61///
62/// # Examples
63///
64/// ```rust
65/// use bevy::prelude::*;
66/// use leafwing_input_manager::prelude::*;
67///
68/// // Define your actions.
69/// #[derive(Actionlike, Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
70/// enum Action {
71///     #[actionlike(DualAxis)]
72///     Move,
73///     Run,
74///     Jump,
75/// }
76///
77/// // Create an InputMap from an iterable,
78/// // allowing for multiple input types per action.
79/// let mut input_map = InputMap::new([
80///     // Multiple inputs can be bound to the same action.
81///     // Note that the type of your iterators must be homogeneous.
82///     (Action::Run, KeyCode::ShiftLeft),
83///     (Action::Run, KeyCode::ShiftRight),
84///     // Note that duplicate associations are ignored.
85///     (Action::Run, KeyCode::ShiftRight),
86///     (Action::Jump, KeyCode::Space),
87/// ])
88/// // Associate actions with other input types.
89/// .with_dual_axis(Action::Move, VirtualDPad::wasd())
90/// .with_dual_axis(Action::Move, GamepadStick::LEFT)
91/// // Associate an action with multiple inputs at once.
92/// .with_one_to_many(Action::Jump, [KeyCode::KeyJ, KeyCode::KeyU]);
93///
94/// // You can also use methods like a normal MultiMap.
95/// input_map.insert(Action::Jump, KeyCode::KeyM);
96///
97/// // Remove all bindings to a specific action.
98/// input_map.clear_action(&Action::Jump);
99///
100/// // Remove all bindings.
101/// input_map.clear();
102/// ```
103#[derive(Component, Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
104#[require(ActionState::<A>)]
105#[cfg_attr(feature = "asset", derive(Asset))]
106#[reflect(Component)]
107pub struct InputMap<A: Actionlike> {
108    /// The underlying map that stores action-input mappings for [`Buttonlike`] actions.
109    buttonlike_map: HashMap<A, Vec<Box<dyn Buttonlike>>>,
110
111    /// The underlying map that stores action-input mappings for [`Axislike`] actions.
112    axislike_map: HashMap<A, Vec<Box<dyn Axislike>>>,
113
114    /// The underlying map that stores action-input mappings for [`DualAxislike`] actions.
115    dual_axislike_map: HashMap<A, Vec<Box<dyn DualAxislike>>>,
116
117    /// The underlying map that stores action-input mappings for [`TripleAxislike`] actions.
118    triple_axislike_map: HashMap<A, Vec<Box<dyn TripleAxislike>>>,
119
120    /// The specified gamepad from which this map exclusively accepts input.
121    associated_gamepad: Option<Entity>,
122}
123
124impl<A: Actionlike> Default for InputMap<A> {
125    fn default() -> Self {
126        InputMap {
127            buttonlike_map: HashMap::default(),
128            axislike_map: HashMap::default(),
129            dual_axislike_map: HashMap::default(),
130            triple_axislike_map: HashMap::default(),
131            associated_gamepad: None,
132        }
133    }
134}
135
136// Constructors
137impl<A: Actionlike> InputMap<A> {
138    /// Creates an [`InputMap`] from an iterator over [`Buttonlike`] action-input bindings.
139    /// Note that all elements within the iterator must be of the same type (homogeneous).
140    ///
141    /// This method ensures idempotence, meaning that adding the same input
142    /// for the same action multiple times will only result in a single binding being created.
143    #[inline(always)]
144    pub fn new(bindings: impl IntoIterator<Item = (A, impl Buttonlike)>) -> Self {
145        bindings
146            .into_iter()
147            .fold(Self::default(), |map, (action, input)| {
148                map.with(action, input)
149            })
150    }
151
152    /// Associates an `action` with a specific [`Buttonlike`] `input`.
153    /// Multiple inputs can be bound to the same action.
154    ///
155    /// This method ensures idempotence, meaning that adding the same input
156    /// for the same action multiple times will only result in a single binding being created.
157    #[inline(always)]
158    pub fn with(mut self, action: A, button: impl Buttonlike) -> Self {
159        self.insert(action, button);
160        self
161    }
162
163    /// Associates an `action` with a specific [`Axislike`] `input`.
164    /// Multiple inputs can be bound to the same action.
165    ///
166    /// This method ensures idempotence, meaning that adding the same input
167    /// for the same action multiple times will only result in a single binding being created.
168    #[inline(always)]
169    pub fn with_axis(mut self, action: A, axis: impl Axislike) -> Self {
170        self.insert_axis(action, axis);
171        self
172    }
173
174    /// Associates an `action` with a specific [`DualAxislike`] `input`.
175    /// Multiple inputs can be bound to the same action.
176    ///
177    /// This method ensures idempotence, meaning that adding the same input
178    /// for the same action multiple times will only result in a single binding being created.
179    #[inline(always)]
180    pub fn with_dual_axis(mut self, action: A, dual_axis: impl DualAxislike) -> Self {
181        self.insert_dual_axis(action, dual_axis);
182        self
183    }
184
185    /// Associates an `action` with a specific [`TripleAxislike`] `input`.
186    /// Multiple inputs can be bound to the same action.
187    ///
188    /// This method ensures idempotence, meaning that adding the same input
189    /// for the same action multiple times will only result in a single binding being created.
190    #[inline(always)]
191    pub fn with_triple_axis(mut self, action: A, triple_axis: impl TripleAxislike) -> Self {
192        self.insert_triple_axis(action, triple_axis);
193        self
194    }
195
196    /// Associates an `action` with multiple [`Buttonlike`] `inputs` provided by an iterator.
197    /// Note that all elements within the iterator must be of the same type (homogeneous).
198    ///
199    /// This method ensures idempotence, meaning that adding the same input
200    /// for the same action multiple times will only result in a single binding being created.
201    #[inline(always)]
202    pub fn with_one_to_many(
203        mut self,
204        action: A,
205        inputs: impl IntoIterator<Item = impl Buttonlike>,
206    ) -> Self {
207        self.insert_one_to_many(action, inputs);
208        self
209    }
210
211    /// Adds multiple action-input bindings provided by an iterator.
212    /// Note that all elements within the iterator must be of the same type (homogeneous).
213    ///
214    /// This method ensures idempotence, meaning that adding the same input
215    /// for the same action multiple times will only result in a single binding being created.
216    #[inline(always)]
217    pub fn with_multiple(
218        mut self,
219        bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
220    ) -> Self {
221        self.insert_multiple(bindings);
222        self
223    }
224}
225
226#[inline(always)]
227fn insert_unique<K, V>(map: &mut HashMap<K, Vec<V>>, key: &K, value: V)
228where
229    K: Clone + Eq + Hash,
230    V: PartialEq,
231{
232    if let Some(list) = map.get_mut(key) {
233        if !list.contains(&value) {
234            list.push(value);
235        }
236    } else {
237        map.insert(key.clone(), vec![value]);
238    }
239}
240
241// Insertion
242impl<A: Actionlike> InputMap<A> {
243    /// Inserts a binding between an `action` and a specific [`Buttonlike`] `input`.
244    /// Multiple inputs can be bound to the same action.
245    ///
246    /// This method ensures idempotence, meaning that adding the same input
247    /// for the same action multiple times will only result in a single binding being created.
248    #[inline(always)]
249    #[track_caller]
250    pub fn insert(&mut self, action: A, button: impl Buttonlike) -> &mut Self {
251        self.insert_boxed(action, Box::new(button));
252        self
253    }
254
255    /// See [`InputMap::insert`] for details.
256    ///
257    /// This method accepts a boxed [`Buttonlike`] `input`, allowing for
258    /// generics to be used in place of specifics. (E.g. serializing from a config file).
259    #[inline(always)]
260    #[track_caller]
261    pub fn insert_boxed(&mut self, action: A, button: Box<dyn Buttonlike>) -> &mut Self {
262        debug_assert!(
263            action.input_control_kind() == InputControlKind::Button,
264            "Cannot map a Buttonlike input for action {:?} of kind {:?}",
265            action,
266            action.input_control_kind()
267        );
268
269        if action.input_control_kind() != InputControlKind::Button {
270            error!(
271                "Cannot map a Buttonlike input for action {:?} of kind {:?}",
272                action,
273                action.input_control_kind()
274            );
275
276            return self;
277        }
278
279        insert_unique(&mut self.buttonlike_map, &action, button);
280        self
281    }
282
283    /// Inserts a binding between an `action` and a specific [`Axislike`] `input`.
284    /// Multiple inputs can be bound to the same action.
285    ///
286    /// This method ensures idempotence, meaning that adding the same input
287    /// for the same action multiple times will only result in a single binding being created.
288    #[inline(always)]
289    #[track_caller]
290    pub fn insert_axis(&mut self, action: A, axis: impl Axislike) -> &mut Self {
291        self.insert_axis_boxed(action, Box::new(axis));
292        self
293    }
294
295    /// See [`InputMap::insert_axis`] for details.
296    ///
297    /// This method accepts a boxed [`Axislike`] `input`, allowing for
298    /// generics to be used in place of specifics. (E.g. serializing from a config file).
299    #[inline(always)]
300    #[track_caller]
301    pub fn insert_axis_boxed(&mut self, action: A, axis: Box<dyn Axislike>) -> &mut Self {
302        debug_assert!(
303            action.input_control_kind() == InputControlKind::Axis,
304            "Cannot map an Axislike input for action {:?} of kind {:?}",
305            action,
306            action.input_control_kind()
307        );
308
309        if action.input_control_kind() != InputControlKind::Axis {
310            error!(
311                "Cannot map an Axislike input for action {:?} of kind {:?}",
312                action,
313                action.input_control_kind()
314            );
315
316            return self;
317        }
318
319        insert_unique(&mut self.axislike_map, &action, axis);
320        self
321    }
322
323    /// Inserts a binding between an `action` and a specific [`DualAxislike`] `input`.
324    /// Multiple inputs can be bound to the same action.
325    ///
326    /// This method ensures idempotence, meaning that adding the same input
327    /// for the same action multiple times will only result in a single binding being created.
328    #[inline(always)]
329    #[track_caller]
330    pub fn insert_dual_axis(&mut self, action: A, dual_axis: impl DualAxislike) -> &mut Self {
331        self.insert_dual_axis_boxed(action, Box::new(dual_axis));
332        self
333    }
334
335    /// See [`InputMap::insert_dual_axis`] for details.
336    ///
337    /// This method accepts a boxed [`DualAxislike`] `input`, allowing for
338    /// generics to be used in place of specifics. (E.g. serializing from a config file).
339    #[inline(always)]
340    #[track_caller]
341    pub fn insert_dual_axis_boxed(&mut self, action: A, axis: Box<dyn DualAxislike>) -> &mut Self {
342        debug_assert!(
343            action.input_control_kind() == InputControlKind::DualAxis,
344            "Cannot map a DualAxislike input for action {:?} of kind {:?}",
345            action,
346            action.input_control_kind()
347        );
348
349        if action.input_control_kind() != InputControlKind::DualAxis {
350            error!(
351                "Cannot map a DualAxislike input for action {:?} of kind {:?}",
352                action,
353                action.input_control_kind()
354            );
355
356            return self;
357        }
358
359        insert_unique(&mut self.dual_axislike_map, &action, axis);
360        self
361    }
362
363    /// Inserts a binding between an `action` and a specific [`TripleAxislike`] `input`.
364    /// Multiple inputs can be bound to the same action.
365    ///
366    /// This method ensures idempotence, meaning that adding the same input
367    /// for the same action multiple times will only result in a single binding being created.
368    #[inline(always)]
369    #[track_caller]
370    pub fn insert_triple_axis(&mut self, action: A, triple_axis: impl TripleAxislike) -> &mut Self {
371        self.insert_triple_axis_boxed(action, Box::new(triple_axis));
372        self
373    }
374
375    /// See [`InputMap::insert_triple_axis`] for details.
376    ///
377    /// This method accepts a boxed [`TripleAxislike`] `input`, allowing for
378    /// generics to be used in place of specifics. (E.g. serializing from a config file).
379    #[inline(always)]
380    #[track_caller]
381    pub fn insert_triple_axis_boxed(
382        &mut self,
383        action: A,
384        triple_axis: Box<dyn TripleAxislike>,
385    ) -> &mut Self {
386        debug_assert!(
387            action.input_control_kind() == InputControlKind::TripleAxis,
388            "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
389            action,
390            action.input_control_kind()
391        );
392
393        if action.input_control_kind() != InputControlKind::TripleAxis {
394            error!(
395                "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
396                action,
397                action.input_control_kind()
398            );
399
400            return self;
401        }
402
403        insert_unique(&mut self.triple_axislike_map, &action, triple_axis);
404        self
405    }
406
407    /// Inserts bindings between the same `action` and multiple [`Buttonlike`] `inputs` provided by an iterator.
408    /// Note that all elements within the iterator must be of the same type (homogeneous).
409    ///
410    /// To insert a chord, such as Control + A, use a [`ButtonlikeChord`](crate::user_input::ButtonlikeChord).
411    ///
412    /// This method ensures idempotence, meaning that adding the same input
413    /// for the same action multiple times will only result in a single binding being created.
414    #[inline(always)]
415    pub fn insert_one_to_many(
416        &mut self,
417        action: A,
418        inputs: impl IntoIterator<Item = impl Buttonlike>,
419    ) -> &mut Self {
420        let inputs = inputs
421            .into_iter()
422            .map(|input| Box::new(input) as Box<dyn Buttonlike>);
423        self.insert_one_to_many_boxed(action, inputs);
424        self
425    }
426
427    /// See [`InputMap::insert_one_to_many`] for details.
428    ///
429    /// This method accepts an iterator, over a boxed [`Buttonlike`] `input`, allowing for
430    /// generics to be used in place of specifics. (E.g. serializing from a config file).
431    #[inline(always)]
432    pub fn insert_one_to_many_boxed(
433        &mut self,
434        action: A,
435        inputs: impl IntoIterator<Item = Box<dyn Buttonlike>>,
436    ) -> &mut Self {
437        let inputs = inputs.into_iter();
438        if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
439            for input in inputs {
440                if !bindings.contains(&input) {
441                    bindings.push(input);
442                }
443            }
444        } else {
445            self.buttonlike_map
446                .insert(action, inputs.unique().collect());
447        }
448        self
449    }
450
451    /// Inserts multiple action-input [`Buttonlike`] bindings provided by an iterator.
452    /// Note that all elements within the iterator must be of the same type (homogeneous).
453    ///
454    /// This method ensures idempotence, meaning that adding the same input
455    /// for the same action multiple times will only result in a single binding being created.
456    #[inline(always)]
457    pub fn insert_multiple(
458        &mut self,
459        bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
460    ) -> &mut Self {
461        for (action, input) in bindings.into_iter() {
462            self.insert(action, input);
463        }
464        self
465    }
466
467    /// See [`InputMap::insert_multiple`] for details.
468    ///
469    /// This method accepts an iterator, over a boxed [`Buttonlike`] `input`, allowing for
470    /// generics to be used in place of specifics. (E.g. serializing from a config file).
471    #[inline(always)]
472    pub fn insert_multiple_boxed(
473        &mut self,
474        bindings: impl IntoIterator<Item = (A, Box<dyn Buttonlike>)>,
475    ) -> &mut Self {
476        for (action, input) in bindings.into_iter() {
477            self.insert_boxed(action, input);
478        }
479        self
480    }
481
482    /// Merges the provided [`InputMap`] into this `map`, combining their bindings,
483    /// avoiding duplicates.
484    ///
485    /// If the associated gamepads do not match, the association will be removed.
486    pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
487        if self.associated_gamepad != other.associated_gamepad {
488            self.clear_gamepad();
489        }
490
491        for (other_action, other_inputs) in other.iter_buttonlike() {
492            for other_input in other_inputs.iter().cloned() {
493                insert_unique(&mut self.buttonlike_map, other_action, other_input);
494            }
495        }
496
497        for (other_action, other_inputs) in other.iter_axislike() {
498            for other_input in other_inputs.iter().cloned() {
499                insert_unique(&mut self.axislike_map, other_action, other_input);
500            }
501        }
502
503        for (other_action, other_inputs) in other.iter_dual_axislike() {
504            for other_input in other_inputs.iter().cloned() {
505                insert_unique(&mut self.dual_axislike_map, other_action, other_input);
506            }
507        }
508
509        for (other_action, other_inputs) in other.iter_triple_axislike() {
510            for other_input in other_inputs.iter().cloned() {
511                insert_unique(&mut self.triple_axislike_map, other_action, other_input);
512            }
513        }
514
515        self
516    }
517}
518
519// Configuration
520impl<A: Actionlike> InputMap<A> {
521    /// Fetches the gamepad [`Entity`] associated with the one controlled by this input map.
522    ///
523    /// If this is [`None`], input from any connected gamepad will be used.
524    #[must_use]
525    #[inline]
526    pub const fn gamepad(&self) -> Option<Entity> {
527        self.associated_gamepad
528    }
529
530    /// Assigns a particular gamepad [`Entity`] to the one controlled by this input map.
531    ///
532    /// Use this when an [`InputMap`] should exclusively accept input
533    /// from a particular gamepad.
534    ///
535    /// If this is not called, input from any connected gamepad will be used.
536    /// The first matching non-zero input will be accepted,
537    /// as determined by gamepad registration order.
538    ///
539    /// Because of this robust fallback behavior,
540    /// this method can typically be ignored when writing single-player games.
541    #[inline]
542    pub fn with_gamepad(mut self, gamepad: Entity) -> Self {
543        self.set_gamepad(gamepad);
544        self
545    }
546
547    /// Assigns a particular gamepad [`Entity`] to the one controlled by this input map.
548    ///
549    /// Use this when an [`InputMap`] should exclusively accept input
550    /// from a particular gamepad.
551    ///
552    /// If this is not called, input from any connected gamepad will be used.
553    /// The first matching non-zero input will be accepted,
554    /// as determined by gamepad registration order.
555    ///
556    /// Because of this robust fallback behavior,
557    /// this method can typically be ignored when writing single-player games.
558    #[inline]
559    pub fn set_gamepad(&mut self, gamepad: Entity) -> &mut Self {
560        self.associated_gamepad = Some(gamepad);
561        self
562    }
563
564    /// Clears any gamepad [`Entity`] associated with the one controlled by this input map.
565    #[inline]
566    pub fn clear_gamepad(&mut self) -> &mut Self {
567        self.associated_gamepad = None;
568        self
569    }
570}
571
572// Check whether actions are pressed
573impl<A: Actionlike> InputMap<A> {
574    /// Checks if the `action` are currently pressed by any of the associated [`Buttonlike`]s.
575    ///
576    /// Accounts for clashing inputs according to the [`ClashStrategy`] and remove conflicting actions.
577    #[must_use]
578    pub fn pressed(
579        &self,
580        action: &A,
581        input_store: &CentralInputStore,
582        clash_strategy: ClashStrategy,
583    ) -> bool {
584        let processed_actions = self.process_actions(None, input_store, clash_strategy);
585
586        let Some(updated_value) = processed_actions.get(action) else {
587            return false;
588        };
589
590        match updated_value {
591            UpdatedValue::Button(ButtonValue { pressed, value: _ }) => *pressed,
592            _ => false,
593        }
594    }
595
596    /// Determines the correct state for each action according to provided [`CentralInputStore`].
597    ///
598    /// This method uses the input bindings for each action to determine how to parse the input data,
599    /// and generates corresponding [`ButtonData`](crate::action_state::ButtonData),
600    /// [`AxisData`](crate::action_state::AxisData) and [`DualAxisData`](crate::action_state::DualAxisData).
601    ///
602    /// For [`Buttonlike`] actions, this accounts for clashing inputs according to the [`ClashStrategy`] and removes conflicting actions.
603    ///
604    /// [`Buttonlike`] inputs will be pressed if any of the associated inputs are pressed.
605    /// [`Axislike`] and [`DualAxislike`] inputs will be the sum of all associated inputs.
606    #[must_use]
607    pub fn process_actions(
608        &self,
609        gamepads: Option<Query<Entity, With<Gamepad>>>,
610        input_store: &CentralInputStore,
611        clash_strategy: ClashStrategy,
612    ) -> UpdatedActions<A> {
613        let mut updated_actions = UpdatedActions::default();
614        let gamepad = self.associated_gamepad.unwrap_or(find_gamepad(gamepads));
615
616        // Generate the base action data for each action
617        for (action, input_bindings) in self.iter_buttonlike() {
618            let mut final_state: Option<(bool, f32)> = None;
619            for binding in input_bindings {
620                let pressed = binding.pressed(input_store, gamepad);
621                let value = binding.value(input_store, gamepad);
622                if let Some((existing_state, existing_value)) = final_state {
623                    final_state = Some((existing_state || pressed, existing_value.max(value)));
624                } else {
625                    final_state = Some((pressed, value));
626                }
627            }
628
629            if let Some((pressed, value)) = final_state {
630                updated_actions.insert(
631                    action.clone(),
632                    UpdatedValue::Button(ButtonValue { pressed, value }),
633                );
634            }
635        }
636
637        for (action, input_bindings) in self.iter_axislike() {
638            let mut final_value = None;
639            for binding in input_bindings {
640                if let Some(value) = binding.get_value(input_store, gamepad) {
641                    final_value = Some(final_value.unwrap_or(0.0) + value);
642                }
643            }
644
645            if let Some(final_value) = final_value {
646                updated_actions.insert(action.clone(), UpdatedValue::Axis(final_value));
647            }
648        }
649
650        for (action, _input_bindings) in self.iter_dual_axislike() {
651            let mut final_value = None;
652            for binding in _input_bindings {
653                if let Some(axis_pair) = binding.get_axis_pair(input_store, gamepad) {
654                    final_value = Some(final_value.unwrap_or(Vec2::ZERO) + axis_pair);
655                }
656            }
657
658            if let Some(final_value) = final_value {
659                updated_actions.insert(action.clone(), UpdatedValue::DualAxis(final_value));
660            }
661        }
662
663        for (action, _input_bindings) in self.iter_triple_axislike() {
664            let mut final_value = Vec3::ZERO;
665            for binding in _input_bindings {
666                final_value += binding.axis_triple(input_store, gamepad);
667            }
668
669            updated_actions.insert(action.clone(), UpdatedValue::TripleAxis(final_value));
670        }
671
672        // Handle clashing inputs, possibly removing some pressed actions from the list
673        self.handle_clashes(&mut updated_actions, input_store, clash_strategy, gamepad);
674
675        updated_actions
676    }
677}
678
679/// The output returned by [`InputMap::process_actions`],
680/// used by [`ActionState::update`](crate::action_state::ActionState) to update the state of each action.
681#[derive(Debug, Clone, PartialEq, Deref, DerefMut)]
682pub struct UpdatedActions<A: Actionlike>(pub HashMap<A, UpdatedValue>);
683
684impl<A: Actionlike> UpdatedActions<A> {
685    /// Returns `true` if the action is both buttonlike and pressed.
686    pub fn pressed(&self, action: &A) -> bool {
687        match self.0.get(action) {
688            Some(UpdatedValue::Button(ButtonValue { pressed, value: _ })) => *pressed,
689            _ => false,
690        }
691    }
692}
693
694/// An enum representing the updated value of an action.
695///
696/// Used in [`UpdatedActions`] to store the updated state of each action.
697#[derive(Debug, Clone, Copy, PartialEq)]
698pub enum UpdatedValue {
699    /// A buttonlike action that was pressed or released.
700    Button(ButtonValue),
701    /// An axislike action that was updated.
702    Axis(f32),
703    /// A dual-axislike action that was updated.
704    DualAxis(Vec2),
705    /// A triple-axislike action that was updated.
706    TripleAxis(Vec3),
707}
708
709impl<A: Actionlike> Default for UpdatedActions<A> {
710    fn default() -> Self {
711        Self(HashMap::default())
712    }
713}
714
715// Utilities
716impl<A: Actionlike> InputMap<A> {
717    /// Returns an iterator over all registered [`Buttonlike`] actions with their input bindings.
718    pub fn iter_buttonlike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Buttonlike>>)> {
719        self.buttonlike_map.iter()
720    }
721
722    /// Returns an iterator over all registered [`Axislike`] actions with their input bindings.
723    pub fn iter_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Axislike>>)> {
724        self.axislike_map.iter()
725    }
726
727    /// Returns an iterator over all registered [`DualAxislike`] actions with their input bindings.
728    pub fn iter_dual_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn DualAxislike>>)> {
729        self.dual_axislike_map.iter()
730    }
731
732    /// Returns an iterator over all registered [`TripleAxislike`] actions with their input bindings.
733    pub fn iter_triple_axislike(
734        &self,
735    ) -> impl Iterator<Item = (&A, &Vec<Box<dyn TripleAxislike>>)> {
736        self.triple_axislike_map.iter()
737    }
738
739    /// Returns an iterator over all registered [`Buttonlike`] action-input bindings.
740    pub fn buttonlike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Buttonlike)> {
741        self.buttonlike_map
742            .iter()
743            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
744    }
745
746    /// Returns an iterator over all registered [`Axislike`] action-input bindings.
747    pub fn axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Axislike)> {
748        self.axislike_map
749            .iter()
750            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
751    }
752
753    /// Returns an iterator over all registered [`DualAxislike`] action-input bindings.
754    pub fn dual_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn DualAxislike)> {
755        self.dual_axislike_map
756            .iter()
757            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
758    }
759
760    /// Returns an iterator over all registered [`TripleAxislike`] action-input bindings.
761    pub fn triple_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn TripleAxislike)> {
762        self.triple_axislike_map
763            .iter()
764            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
765    }
766
767    /// Returns an iterator over all registered [`Buttonlike`] actions.
768    pub fn buttonlike_actions(&self) -> impl Iterator<Item = &A> {
769        self.buttonlike_map.keys()
770    }
771
772    /// Returns an iterator over all registered [`Axislike`] actions.
773    pub fn axislike_actions(&self) -> impl Iterator<Item = &A> {
774        self.axislike_map.keys()
775    }
776
777    /// Returns an iterator over all registered [`DualAxislike`] actions.
778    pub fn dual_axislike_actions(&self) -> impl Iterator<Item = &A> {
779        self.dual_axislike_map.keys()
780    }
781
782    /// Returns an iterator over all registered [`TripleAxislike`] actions.
783    pub fn triple_axislike_actions(&self) -> impl Iterator<Item = &A> {
784        self.triple_axislike_map.keys()
785    }
786
787    /// Returns a reference to the [`UserInput`](crate::user_input::UserInput) inputs associated with the given `action`.
788    ///
789    /// # Warning
790    ///
791    /// Unlike the other `get` methods, this method is forced to clone the inputs
792    /// due to the lack of [trait upcasting coercion](https://github.com/rust-lang/rust/issues/65991).
793    ///
794    /// As a result, no equivalent `get_mut` method is provided.
795    #[must_use]
796    pub fn get(&self, action: &A) -> Option<Vec<UserInputWrapper>> {
797        match action.input_control_kind() {
798            InputControlKind::Button => {
799                let buttonlike = self.buttonlike_map.get(action)?;
800                Some(
801                    buttonlike
802                        .iter()
803                        .map(|input| UserInputWrapper::Button(input.clone()))
804                        .collect(),
805                )
806            }
807            InputControlKind::Axis => {
808                let axislike = self.axislike_map.get(action)?;
809                Some(
810                    axislike
811                        .iter()
812                        .map(|input| UserInputWrapper::Axis(input.clone()))
813                        .collect(),
814                )
815            }
816            InputControlKind::DualAxis => {
817                let dual_axislike = self.dual_axislike_map.get(action)?;
818                Some(
819                    dual_axislike
820                        .iter()
821                        .map(|input| UserInputWrapper::DualAxis(input.clone()))
822                        .collect(),
823                )
824            }
825            InputControlKind::TripleAxis => {
826                let triple_axislike = self.triple_axislike_map.get(action)?;
827                Some(
828                    triple_axislike
829                        .iter()
830                        .map(|input| UserInputWrapper::TripleAxis(input.clone()))
831                        .collect(),
832                )
833            }
834        }
835    }
836
837    /// Returns a reference to the [`Buttonlike`] inputs associated with the given `action`.
838    #[must_use]
839    pub fn get_buttonlike(&self, action: &A) -> Option<&Vec<Box<dyn Buttonlike>>> {
840        self.buttonlike_map.get(action)
841    }
842
843    /// Returns a mutable reference to the [`Buttonlike`] inputs mapped to `action`
844    #[must_use]
845    pub fn get_buttonlike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Buttonlike>>> {
846        self.buttonlike_map.get_mut(action)
847    }
848
849    /// Returns a reference to the [`Axislike`] inputs associated with the given `action`.
850    #[must_use]
851    pub fn get_axislike(&self, action: &A) -> Option<&Vec<Box<dyn Axislike>>> {
852        self.axislike_map.get(action)
853    }
854
855    /// Returns a mutable reference to the [`Axislike`] inputs mapped to `action`
856    #[must_use]
857    pub fn get_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Axislike>>> {
858        self.axislike_map.get_mut(action)
859    }
860
861    /// Returns a reference to the [`DualAxislike`] inputs associated with the given `action`.
862    #[must_use]
863    pub fn get_dual_axislike(&self, action: &A) -> Option<&Vec<Box<dyn DualAxislike>>> {
864        self.dual_axislike_map.get(action)
865    }
866
867    /// Returns a mutable reference to the [`DualAxislike`] inputs mapped to `action`
868    #[must_use]
869    pub fn get_dual_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn DualAxislike>>> {
870        self.dual_axislike_map.get_mut(action)
871    }
872
873    /// Returns a reference to the [`TripleAxislike`] inputs associated with the given `action`.
874    #[must_use]
875    pub fn get_triple_axislike(&self, action: &A) -> Option<&Vec<Box<dyn TripleAxislike>>> {
876        self.triple_axislike_map.get(action)
877    }
878
879    /// Returns a mutable reference to the [`TripleAxislike`] inputs mapped to `action`
880    #[must_use]
881    pub fn get_triple_axislike_mut(
882        &mut self,
883        action: &A,
884    ) -> Option<&mut Vec<Box<dyn TripleAxislike>>> {
885        self.triple_axislike_map.get_mut(action)
886    }
887
888    /// Count the total number of registered input bindings.
889    #[must_use]
890    pub fn len(&self) -> usize {
891        self.buttonlike_map.values().map(Vec::len).sum::<usize>()
892            + self.axislike_map.values().map(Vec::len).sum::<usize>()
893            + self.dual_axislike_map.values().map(Vec::len).sum::<usize>()
894            + self
895                .triple_axislike_map
896                .values()
897                .map(Vec::len)
898                .sum::<usize>()
899    }
900
901    /// Returns `true` if the map contains no action-input bindings.
902    #[inline]
903    #[must_use]
904    pub fn is_empty(&self) -> bool {
905        self.len() == 0
906    }
907
908    /// Clears the map, removing all action-input bindings.
909    pub fn clear(&mut self) {
910        self.buttonlike_map.clear();
911        self.axislike_map.clear();
912        self.dual_axislike_map.clear();
913        self.triple_axislike_map.clear();
914    }
915}
916
917// Removing
918impl<A: Actionlike> InputMap<A> {
919    /// Clears all input bindings associated with the `action`.
920    pub fn clear_action(&mut self, action: &A) {
921        match action.input_control_kind() {
922            InputControlKind::Button => {
923                self.buttonlike_map.remove(action);
924            }
925            InputControlKind::Axis => {
926                self.axislike_map.remove(action);
927            }
928            InputControlKind::DualAxis => {
929                self.dual_axislike_map.remove(action);
930            }
931            InputControlKind::TripleAxis => {
932                self.triple_axislike_map.remove(action);
933            }
934        }
935    }
936
937    /// Removes the input for the `action` at the provided index.
938    ///
939    /// Returns `Some(())` if the input was found and removed, or `None` if no matching input was found.
940    ///
941    /// # Note
942    ///
943    /// The original input cannot be returned, as the trait object may differ based on the [`InputControlKind`].
944    pub fn remove_at(&mut self, action: &A, index: usize) -> Option<()> {
945        match action.input_control_kind() {
946            InputControlKind::Button => {
947                let input_bindings = self.buttonlike_map.get_mut(action)?;
948                if input_bindings.len() > index {
949                    input_bindings.remove(index);
950                    Some(())
951                } else {
952                    None
953                }
954            }
955            InputControlKind::Axis => {
956                let input_bindings = self.axislike_map.get_mut(action)?;
957                if input_bindings.len() > index {
958                    input_bindings.remove(index);
959                    Some(())
960                } else {
961                    None
962                }
963            }
964            InputControlKind::DualAxis => {
965                let input_bindings = self.dual_axislike_map.get_mut(action)?;
966                if input_bindings.len() > index {
967                    input_bindings.remove(index);
968                    Some(())
969                } else {
970                    None
971                }
972            }
973            InputControlKind::TripleAxis => {
974                let input_bindings = self.triple_axislike_map.get_mut(action)?;
975                if input_bindings.len() > index {
976                    input_bindings.remove(index);
977                    Some(())
978                } else {
979                    None
980                }
981            }
982        }
983    }
984
985    /// Removes the input for the `action` if it exists
986    ///
987    /// Returns [`Some`] with index if the input was found, or [`None`] if no matching input was found.
988    pub fn remove(&mut self, action: &A, input: impl Buttonlike) -> Option<usize> {
989        let bindings = self.buttonlike_map.get_mut(action)?;
990        let boxed_input: Box<dyn Buttonlike> = Box::new(input);
991        let index = bindings.iter().position(|input| input == &boxed_input)?;
992        bindings.remove(index);
993        Some(index)
994    }
995}
996
997impl<A: Actionlike, U: Buttonlike> From<HashMap<A, Vec<U>>> for InputMap<A> {
998    /// Converts a [`HashMap`] mapping actions to multiple [`Buttonlike`]s into an [`InputMap`].
999    ///
1000    /// # Examples
1001    ///
1002    /// ```rust
1003    /// use bevy::prelude::*;
1004    /// use bevy::platform::collections::HashMap;
1005    /// use leafwing_input_manager::prelude::*;
1006    ///
1007    /// #[derive(Actionlike, Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
1008    /// enum Action {
1009    ///     Run,
1010    ///     Jump,
1011    /// }
1012    ///
1013    /// // Create an InputMap from a HashMap mapping actions to their key bindings.
1014    /// let mut map: HashMap<Action, Vec<KeyCode>> = HashMap::default();
1015    ///
1016    /// // Bind the "run" action to either the left or right shift keys to trigger the action.
1017    /// map.insert(
1018    ///     Action::Run,
1019    ///     vec![KeyCode::ShiftLeft, KeyCode::ShiftRight],
1020    /// );
1021    ///
1022    /// let input_map = InputMap::from(map);
1023    /// ```
1024    fn from(raw_map: HashMap<A, Vec<U>>) -> Self {
1025        let mut input_map = Self::default();
1026        for (action, inputs) in raw_map.into_iter() {
1027            input_map.insert_one_to_many(action, inputs);
1028        }
1029        input_map
1030    }
1031}
1032
1033impl<A: Actionlike, U: Buttonlike> FromIterator<(A, U)> for InputMap<A> {
1034    fn from_iter<T: IntoIterator<Item = (A, U)>>(iter: T) -> Self {
1035        let mut input_map = Self::default();
1036        for (action, input) in iter.into_iter() {
1037            input_map.insert(action, input);
1038        }
1039        input_map
1040    }
1041}
1042
1043#[cfg(test)]
1044#[cfg(feature = "keyboard")]
1045mod tests {
1046    use bevy::prelude::Reflect;
1047    use serde::{Deserialize, Serialize};
1048
1049    use super::*;
1050    use crate as leafwing_input_manager;
1051    use crate::prelude::*;
1052
1053    #[derive(Actionlike, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug, Reflect)]
1054    enum Action {
1055        Run,
1056        Jump,
1057        Hide,
1058        #[actionlike(Axis)]
1059        Axis,
1060        #[actionlike(DualAxis)]
1061        DualAxis,
1062        #[actionlike(TripleAxis)]
1063        TripleAxis,
1064    }
1065
1066    #[test]
1067    fn creation() {
1068        use bevy::input::keyboard::KeyCode;
1069
1070        let input_map = InputMap::default()
1071            .with(Action::Run, KeyCode::KeyW)
1072            .with(Action::Run, KeyCode::ShiftLeft)
1073            // Duplicate associations should be ignored
1074            .with(Action::Run, KeyCode::ShiftLeft)
1075            .with_one_to_many(Action::Run, [KeyCode::KeyR, KeyCode::ShiftRight])
1076            .with_multiple([
1077                (Action::Jump, KeyCode::Space),
1078                (Action::Hide, KeyCode::ControlLeft),
1079                (Action::Hide, KeyCode::ControlRight),
1080            ]);
1081
1082        let expected_bindings: HashMap<Box<dyn Buttonlike>, Action> = [
1083            (Box::new(KeyCode::KeyW) as Box<dyn Buttonlike>, Action::Run),
1084            (
1085                Box::new(KeyCode::ShiftLeft) as Box<dyn Buttonlike>,
1086                Action::Run,
1087            ),
1088            (Box::new(KeyCode::KeyR) as Box<dyn Buttonlike>, Action::Run),
1089            (
1090                Box::new(KeyCode::ShiftRight) as Box<dyn Buttonlike>,
1091                Action::Run,
1092            ),
1093            (
1094                Box::new(KeyCode::Space) as Box<dyn Buttonlike>,
1095                Action::Jump,
1096            ),
1097            (
1098                Box::new(KeyCode::ControlLeft) as Box<dyn Buttonlike>,
1099                Action::Hide,
1100            ),
1101            (
1102                Box::new(KeyCode::ControlRight) as Box<dyn Buttonlike>,
1103                Action::Hide,
1104            ),
1105        ]
1106        .into_iter()
1107        .collect();
1108
1109        for (action, input) in input_map.buttonlike_bindings() {
1110            let expected_action = expected_bindings.get(input).unwrap();
1111            assert_eq!(expected_action, action);
1112        }
1113    }
1114
1115    #[test]
1116    fn insertion_idempotency() {
1117        use bevy::input::keyboard::KeyCode;
1118
1119        let mut input_map = InputMap::default();
1120        input_map.insert(Action::Run, KeyCode::Space);
1121
1122        let expected: Vec<Box<dyn Buttonlike>> = vec![Box::new(KeyCode::Space)];
1123        assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1124
1125        // Duplicate insertions should not change anything
1126        input_map.insert(Action::Run, KeyCode::Space);
1127        assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1128    }
1129
1130    #[test]
1131    fn multiple_insertion() {
1132        use bevy::input::keyboard::KeyCode;
1133
1134        let mut input_map = InputMap::default();
1135        input_map.insert(Action::Run, KeyCode::Space);
1136        input_map.insert(Action::Run, KeyCode::Enter);
1137
1138        let expected: Vec<Box<dyn Buttonlike>> =
1139            vec![Box::new(KeyCode::Space), Box::new(KeyCode::Enter)];
1140        assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1141    }
1142
1143    #[test]
1144    fn input_clearing() {
1145        use bevy::input::keyboard::KeyCode;
1146
1147        let mut input_map = InputMap::default();
1148        input_map.insert(Action::Run, KeyCode::Space);
1149
1150        // Clearing action
1151        input_map.clear_action(&Action::Run);
1152        assert_eq!(input_map, InputMap::default());
1153
1154        // Remove input at existing index
1155        input_map.insert(Action::Run, KeyCode::Space);
1156        input_map.insert(Action::Run, KeyCode::ShiftLeft);
1157        assert!(input_map.remove_at(&Action::Run, 1).is_some());
1158        assert!(
1159            input_map.remove_at(&Action::Run, 1).is_none(),
1160            "Should return None on second removal at the same index"
1161        );
1162        assert!(input_map.remove_at(&Action::Run, 0).is_some());
1163        assert!(
1164            input_map.remove_at(&Action::Run, 0).is_none(),
1165            "Should return None on second removal at the same index"
1166        );
1167    }
1168
1169    #[test]
1170    fn merging() {
1171        use bevy::input::keyboard::KeyCode;
1172
1173        let mut input_map = InputMap::default();
1174        let mut default_keyboard_map = InputMap::default();
1175        default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft);
1176        default_keyboard_map.insert(
1177            Action::Hide,
1178            ButtonlikeChord::new([KeyCode::ControlLeft, KeyCode::KeyH]),
1179        );
1180
1181        let mut default_gamepad_map = InputMap::default();
1182        default_gamepad_map.insert(Action::Run, KeyCode::Numpad0);
1183        default_gamepad_map.insert(Action::Hide, KeyCode::Numpad7);
1184
1185        // Merging works
1186        input_map.merge(&default_keyboard_map);
1187        assert_eq!(input_map, default_keyboard_map);
1188
1189        // Merging is idempotent
1190        input_map.merge(&default_keyboard_map);
1191        assert_eq!(input_map, default_keyboard_map);
1192    }
1193
1194    #[cfg(feature = "gamepad")]
1195    #[test]
1196    fn gamepad_swapping() {
1197        use bevy::ecs::world::World;
1198
1199        let mut input_map = InputMap::<Action>::default();
1200        assert_eq!(input_map.gamepad(), None);
1201
1202        let test_entity = World::new().spawn_empty().id();
1203        input_map.set_gamepad(test_entity);
1204        assert_eq!(input_map.gamepad(), Some(test_entity));
1205
1206        input_map.clear_gamepad();
1207        assert_eq!(input_map.gamepad(), None);
1208    }
1209
1210    #[cfg(feature = "keyboard")]
1211    #[test]
1212    fn input_map_serde() {
1213        use bevy::prelude::{App, KeyCode};
1214        use serde_test::{Token, assert_tokens};
1215
1216        let mut app = App::new();
1217
1218        // Add the plugin to register input deserializers
1219        app.add_plugins(InputManagerPlugin::<Action>::default());
1220
1221        let input_map = InputMap::new([(Action::Hide, KeyCode::ControlLeft)]);
1222        assert_tokens(
1223            &input_map,
1224            &[
1225                Token::Struct {
1226                    name: "InputMap",
1227                    len: 5,
1228                },
1229                Token::Str("buttonlike_map"),
1230                Token::Map { len: Some(1) },
1231                Token::UnitVariant {
1232                    name: "Action",
1233                    variant: "Hide",
1234                },
1235                Token::Seq { len: Some(1) },
1236                Token::Map { len: Some(1) },
1237                Token::BorrowedStr("KeyCode"),
1238                Token::UnitVariant {
1239                    name: "KeyCode",
1240                    variant: "ControlLeft",
1241                },
1242                Token::MapEnd,
1243                Token::SeqEnd,
1244                Token::MapEnd,
1245                Token::Str("axislike_map"),
1246                Token::Map { len: Some(0) },
1247                Token::MapEnd,
1248                Token::Str("dual_axislike_map"),
1249                Token::Map { len: Some(0) },
1250                Token::MapEnd,
1251                Token::Str("triple_axislike_map"),
1252                Token::Map { len: Some(0) },
1253                Token::MapEnd,
1254                Token::Str("associated_gamepad"),
1255                Token::None,
1256                Token::StructEnd,
1257            ],
1258        );
1259    }
1260}