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