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