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