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        debug_assert!(
253            action.input_control_kind() == InputControlKind::Button,
254            "Cannot map a Buttonlike input for action {:?} of kind {:?}",
255            action,
256            action.input_control_kind()
257        );
258
259        if action.input_control_kind() != InputControlKind::Button {
260            error!(
261                "Cannot map a Buttonlike input for action {:?} of kind {:?}",
262                action,
263                action.input_control_kind()
264            );
265
266            return self;
267        }
268
269        insert_unique(&mut self.buttonlike_map, &action, Box::new(button));
270        self
271    }
272
273    /// Inserts a binding between an `action` and a specific [`Axislike`] `input`.
274    /// Multiple inputs can be bound to the same action.
275    ///
276    /// This method ensures idempotence, meaning that adding the same input
277    /// for the same action multiple times will only result in a single binding being created.
278    #[inline(always)]
279    #[track_caller]
280    pub fn insert_axis(&mut self, action: A, axis: impl Axislike) -> &mut Self {
281        debug_assert!(
282            action.input_control_kind() == InputControlKind::Axis,
283            "Cannot map an Axislike input for action {:?} of kind {:?}",
284            action,
285            action.input_control_kind()
286        );
287
288        if action.input_control_kind() != InputControlKind::Axis {
289            error!(
290                "Cannot map an Axislike input for action {:?} of kind {:?}",
291                action,
292                action.input_control_kind()
293            );
294
295            return self;
296        }
297
298        insert_unique(&mut self.axislike_map, &action, Box::new(axis));
299        self
300    }
301
302    /// Inserts a binding between an `action` and a specific [`DualAxislike`] `input`.
303    /// Multiple inputs can be bound to the same action.
304    ///
305    /// This method ensures idempotence, meaning that adding the same input
306    /// for the same action multiple times will only result in a single binding being created.
307    #[inline(always)]
308    #[track_caller]
309    pub fn insert_dual_axis(&mut self, action: A, dual_axis: impl DualAxislike) -> &mut Self {
310        debug_assert!(
311            action.input_control_kind() == InputControlKind::DualAxis,
312            "Cannot map a DualAxislike input for action {:?} of kind {:?}",
313            action,
314            action.input_control_kind()
315        );
316
317        if action.input_control_kind() != InputControlKind::DualAxis {
318            error!(
319                "Cannot map a DualAxislike input for action {:?} of kind {:?}",
320                action,
321                action.input_control_kind()
322            );
323
324            return self;
325        }
326
327        insert_unique(&mut self.dual_axislike_map, &action, Box::new(dual_axis));
328        self
329    }
330
331    /// Inserts a binding between an `action` and a specific [`TripleAxislike`] `input`.
332    /// Multiple inputs can be bound to the same action.
333    ///
334    /// This method ensures idempotence, meaning that adding the same input
335    /// for the same action multiple times will only result in a single binding being created.
336    #[inline(always)]
337    #[track_caller]
338    pub fn insert_triple_axis(&mut self, action: A, triple_axis: impl TripleAxislike) -> &mut Self {
339        debug_assert!(
340            action.input_control_kind() == InputControlKind::TripleAxis,
341            "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
342            action,
343            action.input_control_kind()
344        );
345
346        if action.input_control_kind() != InputControlKind::TripleAxis {
347            error!(
348                "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
349                action,
350                action.input_control_kind()
351            );
352
353            return self;
354        }
355
356        let boxed = Box::new(triple_axis);
357        insert_unique(&mut self.triple_axislike_map, &action, boxed);
358        self
359    }
360
361    /// Inserts bindings between the same `action` and multiple [`Buttonlike`] `inputs` provided by an iterator.
362    /// Note that all elements within the iterator must be of the same type (homogeneous).
363    ///
364    /// To insert a chord, such as Control + A, use a [`ButtonlikeChord`](crate::user_input::ButtonlikeChord).
365    ///
366    /// This method ensures idempotence, meaning that adding the same input
367    /// for the same action multiple times will only result in a single binding being created.
368    #[inline(always)]
369    pub fn insert_one_to_many(
370        &mut self,
371        action: A,
372        inputs: impl IntoIterator<Item = impl Buttonlike>,
373    ) -> &mut Self {
374        let inputs = inputs
375            .into_iter()
376            .map(|input| Box::new(input) as Box<dyn Buttonlike>);
377        if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
378            for input in inputs {
379                if !bindings.contains(&input) {
380                    bindings.push(input);
381                }
382            }
383        } else {
384            self.buttonlike_map
385                .insert(action, inputs.unique().collect());
386        }
387        self
388    }
389
390    /// Inserts multiple action-input [`Buttonlike`] bindings provided by an iterator.
391    /// Note that all elements within the iterator must be of the same type (homogeneous).
392    ///
393    /// This method ensures idempotence, meaning that adding the same input
394    /// for the same action multiple times will only result in a single binding being created.
395    #[inline(always)]
396    pub fn insert_multiple(
397        &mut self,
398        bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
399    ) -> &mut Self {
400        for (action, input) in bindings.into_iter() {
401            self.insert(action, input);
402        }
403        self
404    }
405
406    /// Merges the provided [`InputMap`] into this `map`, combining their bindings,
407    /// avoiding duplicates.
408    ///
409    /// If the associated gamepads do not match, the association will be removed.
410    pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
411        if self.associated_gamepad != other.associated_gamepad {
412            self.clear_gamepad();
413        }
414
415        for (other_action, other_inputs) in other.iter_buttonlike() {
416            for other_input in other_inputs.iter().cloned() {
417                insert_unique(&mut self.buttonlike_map, other_action, other_input);
418            }
419        }
420
421        for (other_action, other_inputs) in other.iter_axislike() {
422            for other_input in other_inputs.iter().cloned() {
423                insert_unique(&mut self.axislike_map, other_action, other_input);
424            }
425        }
426
427        for (other_action, other_inputs) in other.iter_dual_axislike() {
428            for other_input in other_inputs.iter().cloned() {
429                insert_unique(&mut self.dual_axislike_map, other_action, other_input);
430            }
431        }
432
433        for (other_action, other_inputs) in other.iter_triple_axislike() {
434            for other_input in other_inputs.iter().cloned() {
435                insert_unique(&mut self.triple_axislike_map, other_action, other_input);
436            }
437        }
438
439        self
440    }
441}
442
443// Configuration
444impl<A: Actionlike> InputMap<A> {
445    /// Fetches the gamepad [`Entity`] associated with the one controlled by this input map.
446    ///
447    /// If this is [`None`], input from any connected gamepad will be used.
448    #[must_use]
449    #[inline]
450    pub const fn gamepad(&self) -> Option<Entity> {
451        self.associated_gamepad
452    }
453
454    /// Assigns a particular gamepad [`Entity`] to the one controlled by this input map.
455    ///
456    /// Use this when an [`InputMap`] should exclusively accept input
457    /// from a particular gamepad.
458    ///
459    /// If this is not called, input from any connected gamepad will be used.
460    /// The first matching non-zero input will be accepted,
461    /// as determined by gamepad registration order.
462    ///
463    /// Because of this robust fallback behavior,
464    /// this method can typically be ignored when writing single-player games.
465    #[inline]
466    pub fn with_gamepad(mut self, gamepad: Entity) -> Self {
467        self.set_gamepad(gamepad);
468        self
469    }
470
471    /// Assigns a particular gamepad [`Entity`] to the one controlled by this input map.
472    ///
473    /// Use this when an [`InputMap`] should exclusively accept input
474    /// from a particular gamepad.
475    ///
476    /// If this is not called, input from any connected gamepad will be used.
477    /// The first matching non-zero input will be accepted,
478    /// as determined by gamepad registration order.
479    ///
480    /// Because of this robust fallback behavior,
481    /// this method can typically be ignored when writing single-player games.
482    #[inline]
483    pub fn set_gamepad(&mut self, gamepad: Entity) -> &mut Self {
484        self.associated_gamepad = Some(gamepad);
485        self
486    }
487
488    /// Clears any gamepad [`Entity`] associated with the one controlled by this input map.
489    #[inline]
490    pub fn clear_gamepad(&mut self) -> &mut Self {
491        self.associated_gamepad = None;
492        self
493    }
494}
495
496// Check whether actions are pressed
497impl<A: Actionlike> InputMap<A> {
498    /// Checks if the `action` are currently pressed by any of the associated [`Buttonlike`]s.
499    ///
500    /// Accounts for clashing inputs according to the [`ClashStrategy`] and remove conflicting actions.
501    #[must_use]
502    pub fn pressed(
503        &self,
504        action: &A,
505        input_store: &CentralInputStore,
506        clash_strategy: ClashStrategy,
507    ) -> bool {
508        let processed_actions = self.process_actions(None, input_store, clash_strategy);
509
510        let Some(updated_value) = processed_actions.get(action) else {
511            return false;
512        };
513
514        match updated_value {
515            UpdatedValue::Button(state) => *state,
516            _ => false,
517        }
518    }
519
520    /// Determines the correct state for each action according to provided [`CentralInputStore`].
521    ///
522    /// This method uses the input bindings for each action to determine how to parse the input data,
523    /// and generates corresponding [`ButtonData`](crate::action_state::ButtonData),
524    /// [`AxisData`](crate::action_state::AxisData) and [`DualAxisData`](crate::action_state::DualAxisData).
525    ///
526    /// For [`Buttonlike`] actions, this accounts for clashing inputs according to the [`ClashStrategy`] and removes conflicting actions.
527    ///
528    /// [`Buttonlike`] inputs will be pressed if any of the associated inputs are pressed.
529    /// [`Axislike`] and [`DualAxislike`] inputs will be the sum of all associated inputs.
530    #[must_use]
531    pub fn process_actions(
532        &self,
533        gamepads: Option<Query<Entity, With<Gamepad>>>,
534        input_store: &CentralInputStore,
535        clash_strategy: ClashStrategy,
536    ) -> UpdatedActions<A> {
537        let mut updated_actions = UpdatedActions::default();
538        let gamepad = self.associated_gamepad.unwrap_or(find_gamepad(gamepads));
539
540        // Generate the base action data for each action
541        for (action, _input_bindings) in self.iter_buttonlike() {
542            let mut final_state = false;
543            for binding in _input_bindings {
544                if binding.pressed(input_store, gamepad) {
545                    final_state = true;
546                    break;
547                }
548            }
549
550            updated_actions.insert(action.clone(), UpdatedValue::Button(final_state));
551        }
552
553        for (action, _input_bindings) in self.iter_axislike() {
554            let mut final_value = 0.0;
555            for binding in _input_bindings {
556                final_value += binding.value(input_store, gamepad);
557            }
558
559            updated_actions.insert(action.clone(), UpdatedValue::Axis(final_value));
560        }
561
562        for (action, _input_bindings) in self.iter_dual_axislike() {
563            let mut final_value = Vec2::ZERO;
564            for binding in _input_bindings {
565                final_value += binding.axis_pair(input_store, gamepad);
566            }
567
568            updated_actions.insert(action.clone(), UpdatedValue::DualAxis(final_value));
569        }
570
571        for (action, _input_bindings) in self.iter_triple_axislike() {
572            let mut final_value = Vec3::ZERO;
573            for binding in _input_bindings {
574                final_value += binding.axis_triple(input_store, gamepad);
575            }
576
577            updated_actions.insert(action.clone(), UpdatedValue::TripleAxis(final_value));
578        }
579
580        // Handle clashing inputs, possibly removing some pressed actions from the list
581        self.handle_clashes(&mut updated_actions, input_store, clash_strategy, gamepad);
582
583        updated_actions
584    }
585}
586
587/// The output returned by [`InputMap::process_actions`],
588/// used by [`ActionState::update`](crate::action_state::ActionState) to update the state of each action.
589#[derive(Debug, Clone, PartialEq, Deref, DerefMut)]
590pub struct UpdatedActions<A: Actionlike>(pub HashMap<A, UpdatedValue>);
591
592impl<A: Actionlike> UpdatedActions<A> {
593    /// Returns `true` if the action is both buttonlike and pressed.
594    pub fn pressed(&self, action: &A) -> bool {
595        match self.0.get(action) {
596            Some(UpdatedValue::Button(state)) => *state,
597            _ => false,
598        }
599    }
600}
601
602/// An enum representing the updated value of an action.
603///
604/// Used in [`UpdatedActions`] to store the updated state of each action.
605#[derive(Debug, Clone, Copy, PartialEq)]
606pub enum UpdatedValue {
607    /// A buttonlike action that was pressed or released.
608    Button(bool),
609    /// An axislike action that was updated.
610    Axis(f32),
611    /// A dual-axislike action that was updated.
612    DualAxis(Vec2),
613    /// A triple-axislike action that was updated.
614    TripleAxis(Vec3),
615}
616
617impl<A: Actionlike> Default for UpdatedActions<A> {
618    fn default() -> Self {
619        Self(HashMap::default())
620    }
621}
622
623// Utilities
624impl<A: Actionlike> InputMap<A> {
625    /// Returns an iterator over all registered [`Buttonlike`] actions with their input bindings.
626    pub fn iter_buttonlike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Buttonlike>>)> {
627        self.buttonlike_map.iter()
628    }
629
630    /// Returns an iterator over all registered [`Axislike`] actions with their input bindings.
631    pub fn iter_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Axislike>>)> {
632        self.axislike_map.iter()
633    }
634
635    /// Returns an iterator over all registered [`DualAxislike`] actions with their input bindings.
636    pub fn iter_dual_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn DualAxislike>>)> {
637        self.dual_axislike_map.iter()
638    }
639
640    /// Returns an iterator over all registered [`TripleAxislike`] actions with their input bindings.
641    pub fn iter_triple_axislike(
642        &self,
643    ) -> impl Iterator<Item = (&A, &Vec<Box<dyn TripleAxislike>>)> {
644        self.triple_axislike_map.iter()
645    }
646
647    /// Returns an iterator over all registered [`Buttonlike`] action-input bindings.
648    pub fn buttonlike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Buttonlike)> {
649        self.buttonlike_map
650            .iter()
651            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
652    }
653
654    /// Returns an iterator over all registered [`Axislike`] action-input bindings.
655    pub fn axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Axislike)> {
656        self.axislike_map
657            .iter()
658            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
659    }
660
661    /// Returns an iterator over all registered [`DualAxislike`] action-input bindings.
662    pub fn dual_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn DualAxislike)> {
663        self.dual_axislike_map
664            .iter()
665            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
666    }
667
668    /// Returns an iterator over all registered [`TripleAxislike`] action-input bindings.
669    pub fn triple_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn TripleAxislike)> {
670        self.triple_axislike_map
671            .iter()
672            .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
673    }
674
675    /// Returns an iterator over all registered [`Buttonlike`] actions.
676    pub fn buttonlike_actions(&self) -> impl Iterator<Item = &A> {
677        self.buttonlike_map.keys()
678    }
679
680    /// Returns an iterator over all registered [`Axislike`] actions.
681    pub fn axislike_actions(&self) -> impl Iterator<Item = &A> {
682        self.axislike_map.keys()
683    }
684
685    /// Returns an iterator over all registered [`DualAxislike`] actions.
686    pub fn dual_axislike_actions(&self) -> impl Iterator<Item = &A> {
687        self.dual_axislike_map.keys()
688    }
689
690    /// Returns an iterator over all registered [`TripleAxislike`] actions.
691    pub fn triple_axislike_actions(&self) -> impl Iterator<Item = &A> {
692        self.triple_axislike_map.keys()
693    }
694
695    /// Returns a reference to the [`UserInput`](crate::user_input::UserInput) inputs associated with the given `action`.
696    ///
697    /// # Warning
698    ///
699    /// Unlike the other `get` methods, this method is forced to clone the inputs
700    /// due to the lack of [trait upcasting coercion](https://github.com/rust-lang/rust/issues/65991).
701    ///
702    /// As a result, no equivalent `get_mut` method is provided.
703    #[must_use]
704    pub fn get(&self, action: &A) -> Option<Vec<UserInputWrapper>> {
705        match action.input_control_kind() {
706            InputControlKind::Button => {
707                let buttonlike = self.buttonlike_map.get(action)?;
708                Some(
709                    buttonlike
710                        .iter()
711                        .map(|input| UserInputWrapper::Button(input.clone()))
712                        .collect(),
713                )
714            }
715            InputControlKind::Axis => {
716                let axislike = self.axislike_map.get(action)?;
717                Some(
718                    axislike
719                        .iter()
720                        .map(|input| UserInputWrapper::Axis(input.clone()))
721                        .collect(),
722                )
723            }
724            InputControlKind::DualAxis => {
725                let dual_axislike = self.dual_axislike_map.get(action)?;
726                Some(
727                    dual_axislike
728                        .iter()
729                        .map(|input| UserInputWrapper::DualAxis(input.clone()))
730                        .collect(),
731                )
732            }
733            InputControlKind::TripleAxis => {
734                let triple_axislike = self.triple_axislike_map.get(action)?;
735                Some(
736                    triple_axislike
737                        .iter()
738                        .map(|input| UserInputWrapper::TripleAxis(input.clone()))
739                        .collect(),
740                )
741            }
742        }
743    }
744
745    /// Returns a reference to the [`Buttonlike`] inputs associated with the given `action`.
746    #[must_use]
747    pub fn get_buttonlike(&self, action: &A) -> Option<&Vec<Box<dyn Buttonlike>>> {
748        self.buttonlike_map.get(action)
749    }
750
751    /// Returns a mutable reference to the [`Buttonlike`] inputs mapped to `action`
752    #[must_use]
753    pub fn get_buttonlike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Buttonlike>>> {
754        self.buttonlike_map.get_mut(action)
755    }
756
757    /// Returns a reference to the [`Axislike`] inputs associated with the given `action`.
758    #[must_use]
759    pub fn get_axislike(&self, action: &A) -> Option<&Vec<Box<dyn Axislike>>> {
760        self.axislike_map.get(action)
761    }
762
763    /// Returns a mutable reference to the [`Axislike`] inputs mapped to `action`
764    #[must_use]
765    pub fn get_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Axislike>>> {
766        self.axislike_map.get_mut(action)
767    }
768
769    /// Returns a reference to the [`DualAxislike`] inputs associated with the given `action`.
770    #[must_use]
771    pub fn get_dual_axislike(&self, action: &A) -> Option<&Vec<Box<dyn DualAxislike>>> {
772        self.dual_axislike_map.get(action)
773    }
774
775    /// Returns a mutable reference to the [`DualAxislike`] inputs mapped to `action`
776    #[must_use]
777    pub fn get_dual_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn DualAxislike>>> {
778        self.dual_axislike_map.get_mut(action)
779    }
780
781    /// Returns a reference to the [`TripleAxislike`] inputs associated with the given `action`.
782    #[must_use]
783    pub fn get_triple_axislike(&self, action: &A) -> Option<&Vec<Box<dyn TripleAxislike>>> {
784        self.triple_axislike_map.get(action)
785    }
786
787    /// Returns a mutable reference to the [`TripleAxislike`] inputs mapped to `action`
788    #[must_use]
789    pub fn get_triple_axislike_mut(
790        &mut self,
791        action: &A,
792    ) -> Option<&mut Vec<Box<dyn TripleAxislike>>> {
793        self.triple_axislike_map.get_mut(action)
794    }
795
796    /// Count the total number of registered input bindings.
797    #[must_use]
798    pub fn len(&self) -> usize {
799        self.buttonlike_map.values().map(Vec::len).sum::<usize>()
800            + self.axislike_map.values().map(Vec::len).sum::<usize>()
801            + self.dual_axislike_map.values().map(Vec::len).sum::<usize>()
802            + self
803                .triple_axislike_map
804                .values()
805                .map(Vec::len)
806                .sum::<usize>()
807    }
808
809    /// Returns `true` if the map contains no action-input bindings.
810    #[inline]
811    #[must_use]
812    pub fn is_empty(&self) -> bool {
813        self.len() == 0
814    }
815
816    /// Clears the map, removing all action-input bindings.
817    pub fn clear(&mut self) {
818        self.buttonlike_map.clear();
819        self.axislike_map.clear();
820        self.dual_axislike_map.clear();
821        self.triple_axislike_map.clear();
822    }
823}
824
825// Removing
826impl<A: Actionlike> InputMap<A> {
827    /// Clears all input bindings associated with the `action`.
828    pub fn clear_action(&mut self, action: &A) {
829        match action.input_control_kind() {
830            InputControlKind::Button => {
831                self.buttonlike_map.remove(action);
832            }
833            InputControlKind::Axis => {
834                self.axislike_map.remove(action);
835            }
836            InputControlKind::DualAxis => {
837                self.dual_axislike_map.remove(action);
838            }
839            InputControlKind::TripleAxis => {
840                self.triple_axislike_map.remove(action);
841            }
842        }
843    }
844
845    /// Removes the input for the `action` at the provided index.
846    ///
847    /// Returns `Some(())` if the input was found and removed, or `None` if no matching input was found.
848    ///
849    /// # Note
850    ///
851    /// The original input cannot be returned, as the trait object may differ based on the [`InputControlKind`].
852    pub fn remove_at(&mut self, action: &A, index: usize) -> Option<()> {
853        match action.input_control_kind() {
854            InputControlKind::Button => {
855                let input_bindings = self.buttonlike_map.get_mut(action)?;
856                if input_bindings.len() > index {
857                    input_bindings.remove(index);
858                    Some(())
859                } else {
860                    None
861                }
862            }
863            InputControlKind::Axis => {
864                let input_bindings = self.axislike_map.get_mut(action)?;
865                if input_bindings.len() > index {
866                    input_bindings.remove(index);
867                    Some(())
868                } else {
869                    None
870                }
871            }
872            InputControlKind::DualAxis => {
873                let input_bindings = self.dual_axislike_map.get_mut(action)?;
874                if input_bindings.len() > index {
875                    input_bindings.remove(index);
876                    Some(())
877                } else {
878                    None
879                }
880            }
881            InputControlKind::TripleAxis => {
882                let input_bindings = self.triple_axislike_map.get_mut(action)?;
883                if input_bindings.len() > index {
884                    input_bindings.remove(index);
885                    Some(())
886                } else {
887                    None
888                }
889            }
890        }
891    }
892
893    /// Removes the input for the `action` if it exists
894    ///
895    /// Returns [`Some`] with index if the input was found, or [`None`] if no matching input was found.
896    pub fn remove(&mut self, action: &A, input: impl Buttonlike) -> Option<usize> {
897        let bindings = self.buttonlike_map.get_mut(action)?;
898        let boxed_input: Box<dyn Buttonlike> = Box::new(input);
899        let index = bindings.iter().position(|input| input == &boxed_input)?;
900        bindings.remove(index);
901        Some(index)
902    }
903}
904
905impl<A: Actionlike, U: Buttonlike> From<HashMap<A, Vec<U>>> for InputMap<A> {
906    /// Converts a [`HashMap`] mapping actions to multiple [`Buttonlike`]s into an [`InputMap`].
907    ///
908    /// # Examples
909    ///
910    /// ```rust
911    /// use bevy::prelude::*;
912    /// use bevy::platform::collections::HashMap;
913    /// use leafwing_input_manager::prelude::*;
914    ///
915    /// #[derive(Actionlike, Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
916    /// enum Action {
917    ///     Run,
918    ///     Jump,
919    /// }
920    ///
921    /// // Create an InputMap from a HashMap mapping actions to their key bindings.
922    /// let mut map: HashMap<Action, Vec<KeyCode>> = HashMap::default();
923    ///
924    /// // Bind the "run" action to either the left or right shift keys to trigger the action.
925    /// map.insert(
926    ///     Action::Run,
927    ///     vec![KeyCode::ShiftLeft, KeyCode::ShiftRight],
928    /// );
929    ///
930    /// let input_map = InputMap::from(map);
931    /// ```
932    fn from(raw_map: HashMap<A, Vec<U>>) -> Self {
933        let mut input_map = Self::default();
934        for (action, inputs) in raw_map.into_iter() {
935            input_map.insert_one_to_many(action, inputs);
936        }
937        input_map
938    }
939}
940
941impl<A: Actionlike, U: Buttonlike> FromIterator<(A, U)> for InputMap<A> {
942    fn from_iter<T: IntoIterator<Item = (A, U)>>(iter: T) -> Self {
943        let mut input_map = Self::default();
944        for (action, input) in iter.into_iter() {
945            input_map.insert(action, input);
946        }
947        input_map
948    }
949}
950
951#[cfg(feature = "keyboard")]
952mod tests {
953    use bevy::prelude::Reflect;
954    use serde::{Deserialize, Serialize};
955
956    use super::*;
957    use crate as leafwing_input_manager;
958    use crate::prelude::*;
959
960    #[derive(Actionlike, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug, Reflect)]
961    enum Action {
962        Run,
963        Jump,
964        Hide,
965        #[actionlike(Axis)]
966        Axis,
967        #[actionlike(DualAxis)]
968        DualAxis,
969        #[actionlike(TripleAxis)]
970        TripleAxis,
971    }
972
973    #[test]
974    fn creation() {
975        use bevy::input::keyboard::KeyCode;
976
977        let input_map = InputMap::default()
978            .with(Action::Run, KeyCode::KeyW)
979            .with(Action::Run, KeyCode::ShiftLeft)
980            // Duplicate associations should be ignored
981            .with(Action::Run, KeyCode::ShiftLeft)
982            .with_one_to_many(Action::Run, [KeyCode::KeyR, KeyCode::ShiftRight])
983            .with_multiple([
984                (Action::Jump, KeyCode::Space),
985                (Action::Hide, KeyCode::ControlLeft),
986                (Action::Hide, KeyCode::ControlRight),
987            ]);
988
989        let expected_bindings: HashMap<Box<dyn Buttonlike>, Action> = [
990            (Box::new(KeyCode::KeyW) as Box<dyn Buttonlike>, Action::Run),
991            (
992                Box::new(KeyCode::ShiftLeft) as Box<dyn Buttonlike>,
993                Action::Run,
994            ),
995            (Box::new(KeyCode::KeyR) as Box<dyn Buttonlike>, Action::Run),
996            (
997                Box::new(KeyCode::ShiftRight) as Box<dyn Buttonlike>,
998                Action::Run,
999            ),
1000            (
1001                Box::new(KeyCode::Space) as Box<dyn Buttonlike>,
1002                Action::Jump,
1003            ),
1004            (
1005                Box::new(KeyCode::ControlLeft) as Box<dyn Buttonlike>,
1006                Action::Hide,
1007            ),
1008            (
1009                Box::new(KeyCode::ControlRight) as Box<dyn Buttonlike>,
1010                Action::Hide,
1011            ),
1012        ]
1013        .into_iter()
1014        .collect();
1015
1016        for (action, input) in input_map.buttonlike_bindings() {
1017            let expected_action = expected_bindings.get(input).unwrap();
1018            assert_eq!(expected_action, action);
1019        }
1020    }
1021
1022    #[test]
1023    fn insertion_idempotency() {
1024        use bevy::input::keyboard::KeyCode;
1025
1026        let mut input_map = InputMap::default();
1027        input_map.insert(Action::Run, KeyCode::Space);
1028
1029        let expected: Vec<Box<dyn Buttonlike>> = vec![Box::new(KeyCode::Space)];
1030        assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1031
1032        // Duplicate insertions should not change anything
1033        input_map.insert(Action::Run, KeyCode::Space);
1034        assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1035    }
1036
1037    #[test]
1038    fn multiple_insertion() {
1039        use bevy::input::keyboard::KeyCode;
1040
1041        let mut input_map = InputMap::default();
1042        input_map.insert(Action::Run, KeyCode::Space);
1043        input_map.insert(Action::Run, KeyCode::Enter);
1044
1045        let expected: Vec<Box<dyn Buttonlike>> =
1046            vec![Box::new(KeyCode::Space), Box::new(KeyCode::Enter)];
1047        assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1048    }
1049
1050    #[test]
1051    fn input_clearing() {
1052        use bevy::input::keyboard::KeyCode;
1053
1054        let mut input_map = InputMap::default();
1055        input_map.insert(Action::Run, KeyCode::Space);
1056
1057        // Clearing action
1058        input_map.clear_action(&Action::Run);
1059        assert_eq!(input_map, InputMap::default());
1060
1061        // Remove input at existing index
1062        input_map.insert(Action::Run, KeyCode::Space);
1063        input_map.insert(Action::Run, KeyCode::ShiftLeft);
1064        assert!(input_map.remove_at(&Action::Run, 1).is_some());
1065        assert!(
1066            input_map.remove_at(&Action::Run, 1).is_none(),
1067            "Should return None on second removal at the same index"
1068        );
1069        assert!(input_map.remove_at(&Action::Run, 0).is_some());
1070        assert!(
1071            input_map.remove_at(&Action::Run, 0).is_none(),
1072            "Should return None on second removal at the same index"
1073        );
1074    }
1075
1076    #[test]
1077    fn merging() {
1078        use bevy::input::keyboard::KeyCode;
1079
1080        let mut input_map = InputMap::default();
1081        let mut default_keyboard_map = InputMap::default();
1082        default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft);
1083        default_keyboard_map.insert(
1084            Action::Hide,
1085            ButtonlikeChord::new([KeyCode::ControlLeft, KeyCode::KeyH]),
1086        );
1087
1088        let mut default_gamepad_map = InputMap::default();
1089        default_gamepad_map.insert(Action::Run, KeyCode::Numpad0);
1090        default_gamepad_map.insert(Action::Hide, KeyCode::Numpad7);
1091
1092        // Merging works
1093        input_map.merge(&default_keyboard_map);
1094        assert_eq!(input_map, default_keyboard_map);
1095
1096        // Merging is idempotent
1097        input_map.merge(&default_keyboard_map);
1098        assert_eq!(input_map, default_keyboard_map);
1099    }
1100
1101    #[cfg(feature = "gamepad")]
1102    #[test]
1103    fn gamepad_swapping() {
1104        let mut input_map = InputMap::<Action>::default();
1105        assert_eq!(input_map.gamepad(), None);
1106
1107        input_map.set_gamepad(Entity::from_raw(123));
1108        assert_eq!(input_map.gamepad(), Some(Entity::from_raw(123)));
1109
1110        input_map.clear_gamepad();
1111        assert_eq!(input_map.gamepad(), None);
1112    }
1113
1114    #[cfg(feature = "keyboard")]
1115    #[test]
1116    fn input_map_serde() {
1117        use bevy::prelude::{App, KeyCode};
1118        use serde_test::{assert_tokens, Token};
1119
1120        let mut app = App::new();
1121
1122        // Add the plugin to register input deserializers
1123        app.add_plugins(InputManagerPlugin::<Action>::default());
1124
1125        let input_map = InputMap::new([(Action::Hide, KeyCode::ControlLeft)]);
1126        assert_tokens(
1127            &input_map,
1128            &[
1129                Token::Struct {
1130                    name: "InputMap",
1131                    len: 5,
1132                },
1133                Token::Str("buttonlike_map"),
1134                Token::Map { len: Some(1) },
1135                Token::UnitVariant {
1136                    name: "Action",
1137                    variant: "Hide",
1138                },
1139                Token::Seq { len: Some(1) },
1140                Token::Map { len: Some(1) },
1141                Token::BorrowedStr("KeyCode"),
1142                Token::UnitVariant {
1143                    name: "KeyCode",
1144                    variant: "ControlLeft",
1145                },
1146                Token::MapEnd,
1147                Token::SeqEnd,
1148                Token::MapEnd,
1149                Token::Str("axislike_map"),
1150                Token::Map { len: Some(0) },
1151                Token::MapEnd,
1152                Token::Str("dual_axislike_map"),
1153                Token::Map { len: Some(0) },
1154                Token::MapEnd,
1155                Token::Str("triple_axislike_map"),
1156                Token::Map { len: Some(0) },
1157                Token::MapEnd,
1158                Token::Str("associated_gamepad"),
1159                Token::None,
1160                Token::StructEnd,
1161            ],
1162        );
1163    }
1164}