1use std::fmt::Debug;
4use std::hash::Hash;
5
6#[cfg(feature = "asset")]
7use bevy::asset::Asset;
8use bevy::input::gamepad::Gamepad;
9use bevy::math::{Vec2, Vec3};
10use bevy::platform::collections::HashMap;
11use bevy::prelude::{Component, Deref, DerefMut, Entity, Query, Reflect, With};
12use bevy::{log::error, prelude::ReflectComponent};
13use itertools::Itertools;
14use serde::{Deserialize, Serialize};
15
16use crate::buttonlike::ButtonValue;
17use crate::clashing_inputs::ClashStrategy;
18use crate::prelude::updating::CentralInputStore;
19use crate::prelude::{ActionState, UserInputWrapper};
20use crate::user_input::{Axislike, Buttonlike, DualAxislike, TripleAxislike};
21use crate::{Actionlike, InputControlKind};
22
23#[cfg(feature = "gamepad")]
24use crate::user_input::gamepad::find_gamepad;
25
26#[cfg(not(feature = "gamepad"))]
27fn find_gamepad(_: Option<Query<Entity, With<Gamepad>>>) -> Entity {
28 Entity::PLACEHOLDER
29}
30
31#[derive(Component, Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
104#[require(ActionState::<A>)]
105#[cfg_attr(feature = "asset", derive(Asset))]
106#[reflect(Component)]
107pub struct InputMap<A: Actionlike> {
108 buttonlike_map: HashMap<A, Vec<Box<dyn Buttonlike>>>,
110
111 axislike_map: HashMap<A, Vec<Box<dyn Axislike>>>,
113
114 dual_axislike_map: HashMap<A, Vec<Box<dyn DualAxislike>>>,
116
117 triple_axislike_map: HashMap<A, Vec<Box<dyn TripleAxislike>>>,
119
120 associated_gamepad: Option<Entity>,
122}
123
124impl<A: Actionlike> Default for InputMap<A> {
125 fn default() -> Self {
126 InputMap {
127 buttonlike_map: HashMap::default(),
128 axislike_map: HashMap::default(),
129 dual_axislike_map: HashMap::default(),
130 triple_axislike_map: HashMap::default(),
131 associated_gamepad: None,
132 }
133 }
134}
135
136impl<A: Actionlike> InputMap<A> {
138 #[inline(always)]
144 pub fn new(bindings: impl IntoIterator<Item = (A, impl Buttonlike)>) -> Self {
145 bindings
146 .into_iter()
147 .fold(Self::default(), |map, (action, input)| {
148 map.with(action, input)
149 })
150 }
151
152 #[inline(always)]
158 pub fn with(mut self, action: A, button: impl Buttonlike) -> Self {
159 self.insert(action, button);
160 self
161 }
162
163 #[inline(always)]
169 pub fn with_axis(mut self, action: A, axis: impl Axislike) -> Self {
170 self.insert_axis(action, axis);
171 self
172 }
173
174 #[inline(always)]
180 pub fn with_dual_axis(mut self, action: A, dual_axis: impl DualAxislike) -> Self {
181 self.insert_dual_axis(action, dual_axis);
182 self
183 }
184
185 #[inline(always)]
191 pub fn with_triple_axis(mut self, action: A, triple_axis: impl TripleAxislike) -> Self {
192 self.insert_triple_axis(action, triple_axis);
193 self
194 }
195
196 #[inline(always)]
202 pub fn with_one_to_many(
203 mut self,
204 action: A,
205 inputs: impl IntoIterator<Item = impl Buttonlike>,
206 ) -> Self {
207 self.insert_one_to_many(action, inputs);
208 self
209 }
210
211 #[inline(always)]
217 pub fn with_multiple(
218 mut self,
219 bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
220 ) -> Self {
221 self.insert_multiple(bindings);
222 self
223 }
224}
225
226#[inline(always)]
227fn insert_unique<K, V>(map: &mut HashMap<K, Vec<V>>, key: &K, value: V)
228where
229 K: Clone + Eq + Hash,
230 V: PartialEq,
231{
232 if let Some(list) = map.get_mut(key) {
233 if !list.contains(&value) {
234 list.push(value);
235 }
236 } else {
237 map.insert(key.clone(), vec![value]);
238 }
239}
240
241impl<A: Actionlike> InputMap<A> {
243 #[inline(always)]
249 #[track_caller]
250 pub fn insert(&mut self, action: A, button: impl Buttonlike) -> &mut Self {
251 self.insert_boxed(action, Box::new(button));
252 self
253 }
254
255 #[inline(always)]
260 #[track_caller]
261 pub fn insert_boxed(&mut self, action: A, button: Box<dyn Buttonlike>) -> &mut Self {
262 debug_assert!(
263 action.input_control_kind() == InputControlKind::Button,
264 "Cannot map a Buttonlike input for action {:?} of kind {:?}",
265 action,
266 action.input_control_kind()
267 );
268
269 if action.input_control_kind() != InputControlKind::Button {
270 error!(
271 "Cannot map a Buttonlike input for action {:?} of kind {:?}",
272 action,
273 action.input_control_kind()
274 );
275
276 return self;
277 }
278
279 insert_unique(&mut self.buttonlike_map, &action, button);
280 self
281 }
282
283 #[inline(always)]
289 #[track_caller]
290 pub fn insert_axis(&mut self, action: A, axis: impl Axislike) -> &mut Self {
291 self.insert_axis_boxed(action, Box::new(axis));
292 self
293 }
294
295 #[inline(always)]
300 #[track_caller]
301 pub fn insert_axis_boxed(&mut self, action: A, axis: Box<dyn Axislike>) -> &mut Self {
302 debug_assert!(
303 action.input_control_kind() == InputControlKind::Axis,
304 "Cannot map an Axislike input for action {:?} of kind {:?}",
305 action,
306 action.input_control_kind()
307 );
308
309 if action.input_control_kind() != InputControlKind::Axis {
310 error!(
311 "Cannot map an Axislike input for action {:?} of kind {:?}",
312 action,
313 action.input_control_kind()
314 );
315
316 return self;
317 }
318
319 insert_unique(&mut self.axislike_map, &action, axis);
320 self
321 }
322
323 #[inline(always)]
329 #[track_caller]
330 pub fn insert_dual_axis(&mut self, action: A, dual_axis: impl DualAxislike) -> &mut Self {
331 self.insert_dual_axis_boxed(action, Box::new(dual_axis));
332 self
333 }
334
335 #[inline(always)]
340 #[track_caller]
341 pub fn insert_dual_axis_boxed(&mut self, action: A, axis: Box<dyn DualAxislike>) -> &mut Self {
342 debug_assert!(
343 action.input_control_kind() == InputControlKind::DualAxis,
344 "Cannot map a DualAxislike input for action {:?} of kind {:?}",
345 action,
346 action.input_control_kind()
347 );
348
349 if action.input_control_kind() != InputControlKind::DualAxis {
350 error!(
351 "Cannot map a DualAxislike input for action {:?} of kind {:?}",
352 action,
353 action.input_control_kind()
354 );
355
356 return self;
357 }
358
359 insert_unique(&mut self.dual_axislike_map, &action, axis);
360 self
361 }
362
363 #[inline(always)]
369 #[track_caller]
370 pub fn insert_triple_axis(&mut self, action: A, triple_axis: impl TripleAxislike) -> &mut Self {
371 self.insert_triple_axis_boxed(action, Box::new(triple_axis));
372 self
373 }
374
375 #[inline(always)]
380 #[track_caller]
381 pub fn insert_triple_axis_boxed(
382 &mut self,
383 action: A,
384 triple_axis: Box<dyn TripleAxislike>,
385 ) -> &mut Self {
386 debug_assert!(
387 action.input_control_kind() == InputControlKind::TripleAxis,
388 "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
389 action,
390 action.input_control_kind()
391 );
392
393 if action.input_control_kind() != InputControlKind::TripleAxis {
394 error!(
395 "Cannot map a TripleAxislike input for action {:?} of kind {:?}",
396 action,
397 action.input_control_kind()
398 );
399
400 return self;
401 }
402
403 insert_unique(&mut self.triple_axislike_map, &action, triple_axis);
404 self
405 }
406
407 #[inline(always)]
415 pub fn insert_one_to_many(
416 &mut self,
417 action: A,
418 inputs: impl IntoIterator<Item = impl Buttonlike>,
419 ) -> &mut Self {
420 let inputs = inputs
421 .into_iter()
422 .map(|input| Box::new(input) as Box<dyn Buttonlike>);
423 self.insert_one_to_many_boxed(action, inputs);
424 self
425 }
426
427 #[inline(always)]
432 pub fn insert_one_to_many_boxed(
433 &mut self,
434 action: A,
435 inputs: impl IntoIterator<Item = Box<dyn Buttonlike>>,
436 ) -> &mut Self {
437 let inputs = inputs.into_iter();
438 if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
439 for input in inputs {
440 if !bindings.contains(&input) {
441 bindings.push(input);
442 }
443 }
444 } else {
445 self.buttonlike_map
446 .insert(action, inputs.unique().collect());
447 }
448 self
449 }
450
451 #[inline(always)]
457 pub fn insert_multiple(
458 &mut self,
459 bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
460 ) -> &mut Self {
461 for (action, input) in bindings.into_iter() {
462 self.insert(action, input);
463 }
464 self
465 }
466
467 #[inline(always)]
472 pub fn insert_multiple_boxed(
473 &mut self,
474 bindings: impl IntoIterator<Item = (A, Box<dyn Buttonlike>)>,
475 ) -> &mut Self {
476 for (action, input) in bindings.into_iter() {
477 self.insert_boxed(action, input);
478 }
479 self
480 }
481
482 pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
487 if self.associated_gamepad != other.associated_gamepad {
488 self.clear_gamepad();
489 }
490
491 for (other_action, other_inputs) in other.iter_buttonlike() {
492 for other_input in other_inputs.iter().cloned() {
493 insert_unique(&mut self.buttonlike_map, other_action, other_input);
494 }
495 }
496
497 for (other_action, other_inputs) in other.iter_axislike() {
498 for other_input in other_inputs.iter().cloned() {
499 insert_unique(&mut self.axislike_map, other_action, other_input);
500 }
501 }
502
503 for (other_action, other_inputs) in other.iter_dual_axislike() {
504 for other_input in other_inputs.iter().cloned() {
505 insert_unique(&mut self.dual_axislike_map, other_action, other_input);
506 }
507 }
508
509 for (other_action, other_inputs) in other.iter_triple_axislike() {
510 for other_input in other_inputs.iter().cloned() {
511 insert_unique(&mut self.triple_axislike_map, other_action, other_input);
512 }
513 }
514
515 self
516 }
517}
518
519impl<A: Actionlike> InputMap<A> {
521 #[must_use]
525 #[inline]
526 pub const fn gamepad(&self) -> Option<Entity> {
527 self.associated_gamepad
528 }
529
530 #[inline]
542 pub fn with_gamepad(mut self, gamepad: Entity) -> Self {
543 self.set_gamepad(gamepad);
544 self
545 }
546
547 #[inline]
559 pub fn set_gamepad(&mut self, gamepad: Entity) -> &mut Self {
560 self.associated_gamepad = Some(gamepad);
561 self
562 }
563
564 #[inline]
566 pub fn clear_gamepad(&mut self) -> &mut Self {
567 self.associated_gamepad = None;
568 self
569 }
570}
571
572impl<A: Actionlike> InputMap<A> {
574 #[must_use]
578 pub fn pressed(
579 &self,
580 action: &A,
581 input_store: &CentralInputStore,
582 clash_strategy: ClashStrategy,
583 ) -> bool {
584 let processed_actions = self.process_actions(None, input_store, clash_strategy);
585
586 let Some(updated_value) = processed_actions.get(action) else {
587 return false;
588 };
589
590 match updated_value {
591 UpdatedValue::Button(ButtonValue { pressed, value: _ }) => *pressed,
592 _ => false,
593 }
594 }
595
596 #[must_use]
607 pub fn process_actions(
608 &self,
609 gamepads: Option<Query<Entity, With<Gamepad>>>,
610 input_store: &CentralInputStore,
611 clash_strategy: ClashStrategy,
612 ) -> UpdatedActions<A> {
613 let mut updated_actions = UpdatedActions::default();
614 let gamepad = self.associated_gamepad.unwrap_or(find_gamepad(gamepads));
615
616 for (action, input_bindings) in self.iter_buttonlike() {
618 let mut final_state: Option<(bool, f32)> = None;
619 for binding in input_bindings {
620 let pressed = binding.pressed(input_store, gamepad);
621 let value = binding.value(input_store, gamepad);
622 if let Some((existing_state, existing_value)) = final_state {
623 final_state = Some((existing_state || pressed, existing_value.max(value)));
624 } else {
625 final_state = Some((pressed, value));
626 }
627 }
628
629 if let Some((pressed, value)) = final_state {
630 updated_actions.insert(
631 action.clone(),
632 UpdatedValue::Button(ButtonValue { pressed, value }),
633 );
634 }
635 }
636
637 for (action, input_bindings) in self.iter_axislike() {
638 let mut final_value = None;
639 for binding in input_bindings {
640 if let Some(value) = binding.get_value(input_store, gamepad) {
641 final_value = Some(final_value.unwrap_or(0.0) + value);
642 }
643 }
644
645 if let Some(final_value) = final_value {
646 updated_actions.insert(action.clone(), UpdatedValue::Axis(final_value));
647 }
648 }
649
650 for (action, _input_bindings) in self.iter_dual_axislike() {
651 let mut final_value = None;
652 for binding in _input_bindings {
653 if let Some(axis_pair) = binding.get_axis_pair(input_store, gamepad) {
654 final_value = Some(final_value.unwrap_or(Vec2::ZERO) + axis_pair);
655 }
656 }
657
658 if let Some(final_value) = final_value {
659 updated_actions.insert(action.clone(), UpdatedValue::DualAxis(final_value));
660 }
661 }
662
663 for (action, _input_bindings) in self.iter_triple_axislike() {
664 let mut final_value = Vec3::ZERO;
665 for binding in _input_bindings {
666 final_value += binding.axis_triple(input_store, gamepad);
667 }
668
669 updated_actions.insert(action.clone(), UpdatedValue::TripleAxis(final_value));
670 }
671
672 self.handle_clashes(&mut updated_actions, input_store, clash_strategy, gamepad);
674
675 updated_actions
676 }
677}
678
679#[derive(Debug, Clone, PartialEq, Deref, DerefMut)]
682pub struct UpdatedActions<A: Actionlike>(pub HashMap<A, UpdatedValue>);
683
684impl<A: Actionlike> UpdatedActions<A> {
685 pub fn pressed(&self, action: &A) -> bool {
687 match self.0.get(action) {
688 Some(UpdatedValue::Button(ButtonValue { pressed, value: _ })) => *pressed,
689 _ => false,
690 }
691 }
692}
693
694#[derive(Debug, Clone, Copy, PartialEq)]
698pub enum UpdatedValue {
699 Button(ButtonValue),
701 Axis(f32),
703 DualAxis(Vec2),
705 TripleAxis(Vec3),
707}
708
709impl<A: Actionlike> Default for UpdatedActions<A> {
710 fn default() -> Self {
711 Self(HashMap::default())
712 }
713}
714
715impl<A: Actionlike> InputMap<A> {
717 pub fn iter_buttonlike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Buttonlike>>)> {
719 self.buttonlike_map.iter()
720 }
721
722 pub fn iter_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Axislike>>)> {
724 self.axislike_map.iter()
725 }
726
727 pub fn iter_dual_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn DualAxislike>>)> {
729 self.dual_axislike_map.iter()
730 }
731
732 pub fn iter_triple_axislike(
734 &self,
735 ) -> impl Iterator<Item = (&A, &Vec<Box<dyn TripleAxislike>>)> {
736 self.triple_axislike_map.iter()
737 }
738
739 pub fn buttonlike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Buttonlike)> {
741 self.buttonlike_map
742 .iter()
743 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
744 }
745
746 pub fn axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Axislike)> {
748 self.axislike_map
749 .iter()
750 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
751 }
752
753 pub fn dual_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn DualAxislike)> {
755 self.dual_axislike_map
756 .iter()
757 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
758 }
759
760 pub fn triple_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn TripleAxislike)> {
762 self.triple_axislike_map
763 .iter()
764 .flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
765 }
766
767 pub fn buttonlike_actions(&self) -> impl Iterator<Item = &A> {
769 self.buttonlike_map.keys()
770 }
771
772 pub fn axislike_actions(&self) -> impl Iterator<Item = &A> {
774 self.axislike_map.keys()
775 }
776
777 pub fn dual_axislike_actions(&self) -> impl Iterator<Item = &A> {
779 self.dual_axislike_map.keys()
780 }
781
782 pub fn triple_axislike_actions(&self) -> impl Iterator<Item = &A> {
784 self.triple_axislike_map.keys()
785 }
786
787 #[must_use]
796 pub fn get(&self, action: &A) -> Option<Vec<UserInputWrapper>> {
797 match action.input_control_kind() {
798 InputControlKind::Button => {
799 let buttonlike = self.buttonlike_map.get(action)?;
800 Some(
801 buttonlike
802 .iter()
803 .map(|input| UserInputWrapper::Button(input.clone()))
804 .collect(),
805 )
806 }
807 InputControlKind::Axis => {
808 let axislike = self.axislike_map.get(action)?;
809 Some(
810 axislike
811 .iter()
812 .map(|input| UserInputWrapper::Axis(input.clone()))
813 .collect(),
814 )
815 }
816 InputControlKind::DualAxis => {
817 let dual_axislike = self.dual_axislike_map.get(action)?;
818 Some(
819 dual_axislike
820 .iter()
821 .map(|input| UserInputWrapper::DualAxis(input.clone()))
822 .collect(),
823 )
824 }
825 InputControlKind::TripleAxis => {
826 let triple_axislike = self.triple_axislike_map.get(action)?;
827 Some(
828 triple_axislike
829 .iter()
830 .map(|input| UserInputWrapper::TripleAxis(input.clone()))
831 .collect(),
832 )
833 }
834 }
835 }
836
837 #[must_use]
839 pub fn get_buttonlike(&self, action: &A) -> Option<&Vec<Box<dyn Buttonlike>>> {
840 self.buttonlike_map.get(action)
841 }
842
843 #[must_use]
845 pub fn get_buttonlike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Buttonlike>>> {
846 self.buttonlike_map.get_mut(action)
847 }
848
849 #[must_use]
851 pub fn get_axislike(&self, action: &A) -> Option<&Vec<Box<dyn Axislike>>> {
852 self.axislike_map.get(action)
853 }
854
855 #[must_use]
857 pub fn get_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Axislike>>> {
858 self.axislike_map.get_mut(action)
859 }
860
861 #[must_use]
863 pub fn get_dual_axislike(&self, action: &A) -> Option<&Vec<Box<dyn DualAxislike>>> {
864 self.dual_axislike_map.get(action)
865 }
866
867 #[must_use]
869 pub fn get_dual_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn DualAxislike>>> {
870 self.dual_axislike_map.get_mut(action)
871 }
872
873 #[must_use]
875 pub fn get_triple_axislike(&self, action: &A) -> Option<&Vec<Box<dyn TripleAxislike>>> {
876 self.triple_axislike_map.get(action)
877 }
878
879 #[must_use]
881 pub fn get_triple_axislike_mut(
882 &mut self,
883 action: &A,
884 ) -> Option<&mut Vec<Box<dyn TripleAxislike>>> {
885 self.triple_axislike_map.get_mut(action)
886 }
887
888 #[must_use]
890 pub fn len(&self) -> usize {
891 self.buttonlike_map.values().map(Vec::len).sum::<usize>()
892 + self.axislike_map.values().map(Vec::len).sum::<usize>()
893 + self.dual_axislike_map.values().map(Vec::len).sum::<usize>()
894 + self
895 .triple_axislike_map
896 .values()
897 .map(Vec::len)
898 .sum::<usize>()
899 }
900
901 #[inline]
903 #[must_use]
904 pub fn is_empty(&self) -> bool {
905 self.len() == 0
906 }
907
908 pub fn clear(&mut self) {
910 self.buttonlike_map.clear();
911 self.axislike_map.clear();
912 self.dual_axislike_map.clear();
913 self.triple_axislike_map.clear();
914 }
915}
916
917impl<A: Actionlike> InputMap<A> {
919 pub fn clear_action(&mut self, action: &A) {
921 match action.input_control_kind() {
922 InputControlKind::Button => {
923 self.buttonlike_map.remove(action);
924 }
925 InputControlKind::Axis => {
926 self.axislike_map.remove(action);
927 }
928 InputControlKind::DualAxis => {
929 self.dual_axislike_map.remove(action);
930 }
931 InputControlKind::TripleAxis => {
932 self.triple_axislike_map.remove(action);
933 }
934 }
935 }
936
937 pub fn remove_at(&mut self, action: &A, index: usize) -> Option<()> {
945 match action.input_control_kind() {
946 InputControlKind::Button => {
947 let input_bindings = self.buttonlike_map.get_mut(action)?;
948 if input_bindings.len() > index {
949 input_bindings.remove(index);
950 Some(())
951 } else {
952 None
953 }
954 }
955 InputControlKind::Axis => {
956 let input_bindings = self.axislike_map.get_mut(action)?;
957 if input_bindings.len() > index {
958 input_bindings.remove(index);
959 Some(())
960 } else {
961 None
962 }
963 }
964 InputControlKind::DualAxis => {
965 let input_bindings = self.dual_axislike_map.get_mut(action)?;
966 if input_bindings.len() > index {
967 input_bindings.remove(index);
968 Some(())
969 } else {
970 None
971 }
972 }
973 InputControlKind::TripleAxis => {
974 let input_bindings = self.triple_axislike_map.get_mut(action)?;
975 if input_bindings.len() > index {
976 input_bindings.remove(index);
977 Some(())
978 } else {
979 None
980 }
981 }
982 }
983 }
984
985 pub fn remove(&mut self, action: &A, input: impl Buttonlike) -> Option<usize> {
989 let bindings = self.buttonlike_map.get_mut(action)?;
990 let boxed_input: Box<dyn Buttonlike> = Box::new(input);
991 let index = bindings.iter().position(|input| input == &boxed_input)?;
992 bindings.remove(index);
993 Some(index)
994 }
995}
996
997impl<A: Actionlike, U: Buttonlike> From<HashMap<A, Vec<U>>> for InputMap<A> {
998 fn from(raw_map: HashMap<A, Vec<U>>) -> Self {
1025 let mut input_map = Self::default();
1026 for (action, inputs) in raw_map.into_iter() {
1027 input_map.insert_one_to_many(action, inputs);
1028 }
1029 input_map
1030 }
1031}
1032
1033impl<A: Actionlike, U: Buttonlike> FromIterator<(A, U)> for InputMap<A> {
1034 fn from_iter<T: IntoIterator<Item = (A, U)>>(iter: T) -> Self {
1035 let mut input_map = Self::default();
1036 for (action, input) in iter.into_iter() {
1037 input_map.insert(action, input);
1038 }
1039 input_map
1040 }
1041}
1042
1043#[cfg(test)]
1044#[cfg(feature = "keyboard")]
1045mod tests {
1046 use bevy::prelude::Reflect;
1047 use serde::{Deserialize, Serialize};
1048
1049 use super::*;
1050 use crate as leafwing_input_manager;
1051 use crate::prelude::*;
1052
1053 #[derive(Actionlike, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug, Reflect)]
1054 enum Action {
1055 Run,
1056 Jump,
1057 Hide,
1058 #[actionlike(Axis)]
1059 Axis,
1060 #[actionlike(DualAxis)]
1061 DualAxis,
1062 #[actionlike(TripleAxis)]
1063 TripleAxis,
1064 }
1065
1066 #[test]
1067 fn creation() {
1068 use bevy::input::keyboard::KeyCode;
1069
1070 let input_map = InputMap::default()
1071 .with(Action::Run, KeyCode::KeyW)
1072 .with(Action::Run, KeyCode::ShiftLeft)
1073 .with(Action::Run, KeyCode::ShiftLeft)
1075 .with_one_to_many(Action::Run, [KeyCode::KeyR, KeyCode::ShiftRight])
1076 .with_multiple([
1077 (Action::Jump, KeyCode::Space),
1078 (Action::Hide, KeyCode::ControlLeft),
1079 (Action::Hide, KeyCode::ControlRight),
1080 ]);
1081
1082 let expected_bindings: HashMap<Box<dyn Buttonlike>, Action> = [
1083 (Box::new(KeyCode::KeyW) as Box<dyn Buttonlike>, Action::Run),
1084 (
1085 Box::new(KeyCode::ShiftLeft) as Box<dyn Buttonlike>,
1086 Action::Run,
1087 ),
1088 (Box::new(KeyCode::KeyR) as Box<dyn Buttonlike>, Action::Run),
1089 (
1090 Box::new(KeyCode::ShiftRight) as Box<dyn Buttonlike>,
1091 Action::Run,
1092 ),
1093 (
1094 Box::new(KeyCode::Space) as Box<dyn Buttonlike>,
1095 Action::Jump,
1096 ),
1097 (
1098 Box::new(KeyCode::ControlLeft) as Box<dyn Buttonlike>,
1099 Action::Hide,
1100 ),
1101 (
1102 Box::new(KeyCode::ControlRight) as Box<dyn Buttonlike>,
1103 Action::Hide,
1104 ),
1105 ]
1106 .into_iter()
1107 .collect();
1108
1109 for (action, input) in input_map.buttonlike_bindings() {
1110 let expected_action = expected_bindings.get(input).unwrap();
1111 assert_eq!(expected_action, action);
1112 }
1113 }
1114
1115 #[test]
1116 fn insertion_idempotency() {
1117 use bevy::input::keyboard::KeyCode;
1118
1119 let mut input_map = InputMap::default();
1120 input_map.insert(Action::Run, KeyCode::Space);
1121
1122 let expected: Vec<Box<dyn Buttonlike>> = vec![Box::new(KeyCode::Space)];
1123 assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1124
1125 input_map.insert(Action::Run, KeyCode::Space);
1127 assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1128 }
1129
1130 #[test]
1131 fn multiple_insertion() {
1132 use bevy::input::keyboard::KeyCode;
1133
1134 let mut input_map = InputMap::default();
1135 input_map.insert(Action::Run, KeyCode::Space);
1136 input_map.insert(Action::Run, KeyCode::Enter);
1137
1138 let expected: Vec<Box<dyn Buttonlike>> =
1139 vec![Box::new(KeyCode::Space), Box::new(KeyCode::Enter)];
1140 assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
1141 }
1142
1143 #[test]
1144 fn input_clearing() {
1145 use bevy::input::keyboard::KeyCode;
1146
1147 let mut input_map = InputMap::default();
1148 input_map.insert(Action::Run, KeyCode::Space);
1149
1150 input_map.clear_action(&Action::Run);
1152 assert_eq!(input_map, InputMap::default());
1153
1154 input_map.insert(Action::Run, KeyCode::Space);
1156 input_map.insert(Action::Run, KeyCode::ShiftLeft);
1157 assert!(input_map.remove_at(&Action::Run, 1).is_some());
1158 assert!(
1159 input_map.remove_at(&Action::Run, 1).is_none(),
1160 "Should return None on second removal at the same index"
1161 );
1162 assert!(input_map.remove_at(&Action::Run, 0).is_some());
1163 assert!(
1164 input_map.remove_at(&Action::Run, 0).is_none(),
1165 "Should return None on second removal at the same index"
1166 );
1167 }
1168
1169 #[test]
1170 fn merging() {
1171 use bevy::input::keyboard::KeyCode;
1172
1173 let mut input_map = InputMap::default();
1174 let mut default_keyboard_map = InputMap::default();
1175 default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft);
1176 default_keyboard_map.insert(
1177 Action::Hide,
1178 ButtonlikeChord::new([KeyCode::ControlLeft, KeyCode::KeyH]),
1179 );
1180
1181 let mut default_gamepad_map = InputMap::default();
1182 default_gamepad_map.insert(Action::Run, KeyCode::Numpad0);
1183 default_gamepad_map.insert(Action::Hide, KeyCode::Numpad7);
1184
1185 input_map.merge(&default_keyboard_map);
1187 assert_eq!(input_map, default_keyboard_map);
1188
1189 input_map.merge(&default_keyboard_map);
1191 assert_eq!(input_map, default_keyboard_map);
1192 }
1193
1194 #[cfg(feature = "gamepad")]
1195 #[test]
1196 fn gamepad_swapping() {
1197 use bevy::ecs::world::World;
1198
1199 let mut input_map = InputMap::<Action>::default();
1200 assert_eq!(input_map.gamepad(), None);
1201
1202 let test_entity = World::new().spawn_empty().id();
1203 input_map.set_gamepad(test_entity);
1204 assert_eq!(input_map.gamepad(), Some(test_entity));
1205
1206 input_map.clear_gamepad();
1207 assert_eq!(input_map.gamepad(), None);
1208 }
1209
1210 #[cfg(feature = "keyboard")]
1211 #[test]
1212 fn input_map_serde() {
1213 use bevy::prelude::{App, KeyCode};
1214 use serde_test::{Token, assert_tokens};
1215
1216 let mut app = App::new();
1217
1218 app.add_plugins(InputManagerPlugin::<Action>::default());
1220
1221 let input_map = InputMap::new([(Action::Hide, KeyCode::ControlLeft)]);
1222 assert_tokens(
1223 &input_map,
1224 &[
1225 Token::Struct {
1226 name: "InputMap",
1227 len: 5,
1228 },
1229 Token::Str("buttonlike_map"),
1230 Token::Map { len: Some(1) },
1231 Token::UnitVariant {
1232 name: "Action",
1233 variant: "Hide",
1234 },
1235 Token::Seq { len: Some(1) },
1236 Token::Map { len: Some(1) },
1237 Token::BorrowedStr("KeyCode"),
1238 Token::UnitVariant {
1239 name: "KeyCode",
1240 variant: "ControlLeft",
1241 },
1242 Token::MapEnd,
1243 Token::SeqEnd,
1244 Token::MapEnd,
1245 Token::Str("axislike_map"),
1246 Token::Map { len: Some(0) },
1247 Token::MapEnd,
1248 Token::Str("dual_axislike_map"),
1249 Token::Map { len: Some(0) },
1250 Token::MapEnd,
1251 Token::Str("triple_axislike_map"),
1252 Token::Map { len: Some(0) },
1253 Token::MapEnd,
1254 Token::Str("associated_gamepad"),
1255 Token::None,
1256 Token::StructEnd,
1257 ],
1258 );
1259 }
1260}