use std::fmt::Debug;
use std::hash::Hash;
#[cfg(feature = "asset")]
use bevy::asset::Asset;
use bevy::input::gamepad::Gamepad;
use bevy::platform::collections::HashMap;
use bevy::prelude::{Component, Deref, DerefMut, Entity, Query, Reflect, Resource, With};
use bevy::{log::error, prelude::ReflectComponent};
use bevy::{
math::{Vec2, Vec3},
prelude::ReflectResource,
};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use crate::buttonlike::ButtonValue;
use crate::clashing_inputs::ClashStrategy;
use crate::prelude::updating::CentralInputStore;
use crate::prelude::{ActionState, UserInputWrapper};
use crate::user_input::{Axislike, Buttonlike, DualAxislike, TripleAxislike};
use crate::{Actionlike, InputControlKind};
#[cfg(feature = "gamepad")]
use crate::user_input::gamepad::find_gamepad;
#[cfg(not(feature = "gamepad"))]
fn find_gamepad(_: Option<Query<Entity, With<Gamepad>>>) -> Entity {
Entity::PLACEHOLDER
}
#[derive(Resource, Component, Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
#[require(ActionState::<A>)]
#[cfg_attr(feature = "asset", derive(Asset))]
#[reflect(Resource, Component)]
pub struct InputMap<A: Actionlike> {
buttonlike_map: HashMap<A, Vec<Box<dyn Buttonlike>>>,
axislike_map: HashMap<A, Vec<Box<dyn Axislike>>>,
dual_axislike_map: HashMap<A, Vec<Box<dyn DualAxislike>>>,
triple_axislike_map: HashMap<A, Vec<Box<dyn TripleAxislike>>>,
associated_gamepad: Option<Entity>,
}
impl<A: Actionlike> Default for InputMap<A> {
fn default() -> Self {
InputMap {
buttonlike_map: HashMap::default(),
axislike_map: HashMap::default(),
dual_axislike_map: HashMap::default(),
triple_axislike_map: HashMap::default(),
associated_gamepad: None,
}
}
}
impl<A: Actionlike> InputMap<A> {
#[inline(always)]
pub fn new(bindings: impl IntoIterator<Item = (A, impl Buttonlike)>) -> Self {
bindings
.into_iter()
.fold(Self::default(), |map, (action, input)| {
map.with(action, input)
})
}
#[inline(always)]
pub fn with(mut self, action: A, button: impl Buttonlike) -> Self {
self.insert(action, button);
self
}
#[inline(always)]
pub fn with_axis(mut self, action: A, axis: impl Axislike) -> Self {
self.insert_axis(action, axis);
self
}
#[inline(always)]
pub fn with_dual_axis(mut self, action: A, dual_axis: impl DualAxislike) -> Self {
self.insert_dual_axis(action, dual_axis);
self
}
#[inline(always)]
pub fn with_triple_axis(mut self, action: A, triple_axis: impl TripleAxislike) -> Self {
self.insert_triple_axis(action, triple_axis);
self
}
#[inline(always)]
pub fn with_one_to_many(
mut self,
action: A,
inputs: impl IntoIterator<Item = impl Buttonlike>,
) -> Self {
self.insert_one_to_many(action, inputs);
self
}
#[inline(always)]
pub fn with_multiple(
mut self,
bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
) -> Self {
self.insert_multiple(bindings);
self
}
}
#[inline(always)]
fn insert_unique<K, V>(map: &mut HashMap<K, Vec<V>>, key: &K, value: V)
where
K: Clone + Eq + Hash,
V: PartialEq,
{
if let Some(list) = map.get_mut(key) {
if !list.contains(&value) {
list.push(value);
}
} else {
map.insert(key.clone(), vec![value]);
}
}
impl<A: Actionlike> InputMap<A> {
#[inline(always)]
#[track_caller]
pub fn insert(&mut self, action: A, button: impl Buttonlike) -> &mut Self {
self.insert_boxed(action, Box::new(button));
self
}
#[inline(always)]
#[track_caller]
pub fn insert_boxed(&mut self, action: A, button: Box<dyn Buttonlike>) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::Button,
"Cannot map a Buttonlike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::Button {
error!(
"Cannot map a Buttonlike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
insert_unique(&mut self.buttonlike_map, &action, button);
self
}
#[inline(always)]
#[track_caller]
pub fn insert_axis(&mut self, action: A, axis: impl Axislike) -> &mut Self {
self.insert_axis_boxed(action, Box::new(axis));
self
}
#[inline(always)]
#[track_caller]
pub fn insert_axis_boxed(&mut self, action: A, axis: Box<dyn Axislike>) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::Axis,
"Cannot map an Axislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::Axis {
error!(
"Cannot map an Axislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
insert_unique(&mut self.axislike_map, &action, axis);
self
}
#[inline(always)]
#[track_caller]
pub fn insert_dual_axis(&mut self, action: A, dual_axis: impl DualAxislike) -> &mut Self {
self.insert_dual_axis_boxed(action, Box::new(dual_axis));
self
}
#[inline(always)]
#[track_caller]
pub fn insert_dual_axis_boxed(&mut self, action: A, axis: Box<dyn DualAxislike>) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::DualAxis,
"Cannot map a DualAxislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::DualAxis {
error!(
"Cannot map a DualAxislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
insert_unique(&mut self.dual_axislike_map, &action, axis);
self
}
#[inline(always)]
#[track_caller]
pub fn insert_triple_axis(&mut self, action: A, triple_axis: impl TripleAxislike) -> &mut Self {
self.insert_triple_axis_boxed(action, Box::new(triple_axis));
self
}
#[inline(always)]
#[track_caller]
pub fn insert_triple_axis_boxed(
&mut self,
action: A,
triple_axis: Box<dyn TripleAxislike>,
) -> &mut Self {
debug_assert!(
action.input_control_kind() == InputControlKind::TripleAxis,
"Cannot map a TripleAxislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
if action.input_control_kind() != InputControlKind::TripleAxis {
error!(
"Cannot map a TripleAxislike input for action {:?} of kind {:?}",
action,
action.input_control_kind()
);
return self;
}
insert_unique(&mut self.triple_axislike_map, &action, triple_axis);
self
}
#[inline(always)]
pub fn insert_one_to_many(
&mut self,
action: A,
inputs: impl IntoIterator<Item = impl Buttonlike>,
) -> &mut Self {
let inputs = inputs
.into_iter()
.map(|input| Box::new(input) as Box<dyn Buttonlike>);
self.insert_one_to_many_boxed(action, inputs);
self
}
#[inline(always)]
pub fn insert_one_to_many_boxed(
&mut self,
action: A,
inputs: impl IntoIterator<Item = Box<dyn Buttonlike>>,
) -> &mut Self {
let inputs = inputs.into_iter();
if let Some(bindings) = self.buttonlike_map.get_mut(&action) {
for input in inputs {
if !bindings.contains(&input) {
bindings.push(input);
}
}
} else {
self.buttonlike_map
.insert(action, inputs.unique().collect());
}
self
}
#[inline(always)]
pub fn insert_multiple(
&mut self,
bindings: impl IntoIterator<Item = (A, impl Buttonlike)>,
) -> &mut Self {
for (action, input) in bindings.into_iter() {
self.insert(action, input);
}
self
}
#[inline(always)]
pub fn insert_multiple_boxed(
&mut self,
bindings: impl IntoIterator<Item = (A, Box<dyn Buttonlike>)>,
) -> &mut Self {
for (action, input) in bindings.into_iter() {
self.insert_boxed(action, input);
}
self
}
pub fn merge(&mut self, other: &InputMap<A>) -> &mut Self {
if self.associated_gamepad != other.associated_gamepad {
self.clear_gamepad();
}
for (other_action, other_inputs) in other.iter_buttonlike() {
for other_input in other_inputs.iter().cloned() {
insert_unique(&mut self.buttonlike_map, other_action, other_input);
}
}
for (other_action, other_inputs) in other.iter_axislike() {
for other_input in other_inputs.iter().cloned() {
insert_unique(&mut self.axislike_map, other_action, other_input);
}
}
for (other_action, other_inputs) in other.iter_dual_axislike() {
for other_input in other_inputs.iter().cloned() {
insert_unique(&mut self.dual_axislike_map, other_action, other_input);
}
}
for (other_action, other_inputs) in other.iter_triple_axislike() {
for other_input in other_inputs.iter().cloned() {
insert_unique(&mut self.triple_axislike_map, other_action, other_input);
}
}
self
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
#[inline]
pub const fn gamepad(&self) -> Option<Entity> {
self.associated_gamepad
}
#[inline]
pub fn with_gamepad(mut self, gamepad: Entity) -> Self {
self.set_gamepad(gamepad);
self
}
#[inline]
pub fn set_gamepad(&mut self, gamepad: Entity) -> &mut Self {
self.associated_gamepad = Some(gamepad);
self
}
#[inline]
pub fn clear_gamepad(&mut self) -> &mut Self {
self.associated_gamepad = None;
self
}
}
impl<A: Actionlike> InputMap<A> {
#[must_use]
pub fn pressed(
&self,
action: &A,
input_store: &CentralInputStore,
clash_strategy: ClashStrategy,
) -> bool {
let processed_actions = self.process_actions(None, input_store, clash_strategy);
let Some(updated_value) = processed_actions.get(action) else {
return false;
};
match updated_value {
UpdatedValue::Button(ButtonValue { pressed, value: _ }) => *pressed,
_ => false,
}
}
#[must_use]
pub fn process_actions(
&self,
gamepads: Option<Query<Entity, With<Gamepad>>>,
input_store: &CentralInputStore,
clash_strategy: ClashStrategy,
) -> UpdatedActions<A> {
let mut updated_actions = UpdatedActions::default();
let gamepad = self.associated_gamepad.unwrap_or(find_gamepad(gamepads));
for (action, input_bindings) in self.iter_buttonlike() {
let mut final_state: Option<(bool, f32)> = None;
for binding in input_bindings {
let pressed = binding.pressed(input_store, gamepad);
let value = binding.value(input_store, gamepad);
if let Some((existing_state, existing_value)) = final_state {
final_state = Some((existing_state || pressed, existing_value.max(value)));
} else {
final_state = Some((pressed, value));
}
}
if let Some((pressed, value)) = final_state {
updated_actions.insert(
action.clone(),
UpdatedValue::Button(ButtonValue { pressed, value }),
);
}
}
for (action, input_bindings) in self.iter_axislike() {
let mut final_value = None;
for binding in input_bindings {
if let Some(value) = binding.get_value(input_store, gamepad) {
final_value = Some(final_value.unwrap_or(0.0) + value);
}
}
if let Some(final_value) = final_value {
updated_actions.insert(action.clone(), UpdatedValue::Axis(final_value));
}
}
for (action, _input_bindings) in self.iter_dual_axislike() {
let mut final_value = None;
for binding in _input_bindings {
if let Some(axis_pair) = binding.get_axis_pair(input_store, gamepad) {
final_value = Some(final_value.unwrap_or(Vec2::ZERO) + axis_pair);
}
}
if let Some(final_value) = final_value {
updated_actions.insert(action.clone(), UpdatedValue::DualAxis(final_value));
}
}
for (action, _input_bindings) in self.iter_triple_axislike() {
let mut final_value = Vec3::ZERO;
for binding in _input_bindings {
final_value += binding.axis_triple(input_store, gamepad);
}
updated_actions.insert(action.clone(), UpdatedValue::TripleAxis(final_value));
}
self.handle_clashes(&mut updated_actions, input_store, clash_strategy, gamepad);
updated_actions
}
}
#[derive(Debug, Clone, PartialEq, Deref, DerefMut)]
pub struct UpdatedActions<A: Actionlike>(pub HashMap<A, UpdatedValue>);
impl<A: Actionlike> UpdatedActions<A> {
pub fn pressed(&self, action: &A) -> bool {
match self.0.get(action) {
Some(UpdatedValue::Button(ButtonValue { pressed, value: _ })) => *pressed,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UpdatedValue {
Button(ButtonValue),
Axis(f32),
DualAxis(Vec2),
TripleAxis(Vec3),
}
impl<A: Actionlike> Default for UpdatedActions<A> {
fn default() -> Self {
Self(HashMap::default())
}
}
impl<A: Actionlike> InputMap<A> {
pub fn iter_buttonlike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Buttonlike>>)> {
self.buttonlike_map.iter()
}
pub fn iter_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn Axislike>>)> {
self.axislike_map.iter()
}
pub fn iter_dual_axislike(&self) -> impl Iterator<Item = (&A, &Vec<Box<dyn DualAxislike>>)> {
self.dual_axislike_map.iter()
}
pub fn iter_triple_axislike(
&self,
) -> impl Iterator<Item = (&A, &Vec<Box<dyn TripleAxislike>>)> {
self.triple_axislike_map.iter()
}
pub fn buttonlike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Buttonlike)> {
self.buttonlike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn Axislike)> {
self.axislike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn dual_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn DualAxislike)> {
self.dual_axislike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn triple_axislike_bindings(&self) -> impl Iterator<Item = (&A, &dyn TripleAxislike)> {
self.triple_axislike_map
.iter()
.flat_map(|(action, inputs)| inputs.iter().map(move |input| (action, input.as_ref())))
}
pub fn buttonlike_actions(&self) -> impl Iterator<Item = &A> {
self.buttonlike_map.keys()
}
pub fn axislike_actions(&self) -> impl Iterator<Item = &A> {
self.axislike_map.keys()
}
pub fn dual_axislike_actions(&self) -> impl Iterator<Item = &A> {
self.dual_axislike_map.keys()
}
pub fn triple_axislike_actions(&self) -> impl Iterator<Item = &A> {
self.triple_axislike_map.keys()
}
#[must_use]
pub fn get(&self, action: &A) -> Option<Vec<UserInputWrapper>> {
match action.input_control_kind() {
InputControlKind::Button => {
let buttonlike = self.buttonlike_map.get(action)?;
Some(
buttonlike
.iter()
.map(|input| UserInputWrapper::Button(input.clone()))
.collect(),
)
}
InputControlKind::Axis => {
let axislike = self.axislike_map.get(action)?;
Some(
axislike
.iter()
.map(|input| UserInputWrapper::Axis(input.clone()))
.collect(),
)
}
InputControlKind::DualAxis => {
let dual_axislike = self.dual_axislike_map.get(action)?;
Some(
dual_axislike
.iter()
.map(|input| UserInputWrapper::DualAxis(input.clone()))
.collect(),
)
}
InputControlKind::TripleAxis => {
let triple_axislike = self.triple_axislike_map.get(action)?;
Some(
triple_axislike
.iter()
.map(|input| UserInputWrapper::TripleAxis(input.clone()))
.collect(),
)
}
}
}
#[must_use]
pub fn get_buttonlike(&self, action: &A) -> Option<&Vec<Box<dyn Buttonlike>>> {
self.buttonlike_map.get(action)
}
#[must_use]
pub fn get_buttonlike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Buttonlike>>> {
self.buttonlike_map.get_mut(action)
}
#[must_use]
pub fn get_axislike(&self, action: &A) -> Option<&Vec<Box<dyn Axislike>>> {
self.axislike_map.get(action)
}
#[must_use]
pub fn get_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn Axislike>>> {
self.axislike_map.get_mut(action)
}
#[must_use]
pub fn get_dual_axislike(&self, action: &A) -> Option<&Vec<Box<dyn DualAxislike>>> {
self.dual_axislike_map.get(action)
}
#[must_use]
pub fn get_dual_axislike_mut(&mut self, action: &A) -> Option<&mut Vec<Box<dyn DualAxislike>>> {
self.dual_axislike_map.get_mut(action)
}
#[must_use]
pub fn get_triple_axislike(&self, action: &A) -> Option<&Vec<Box<dyn TripleAxislike>>> {
self.triple_axislike_map.get(action)
}
#[must_use]
pub fn get_triple_axislike_mut(
&mut self,
action: &A,
) -> Option<&mut Vec<Box<dyn TripleAxislike>>> {
self.triple_axislike_map.get_mut(action)
}
#[must_use]
pub fn len(&self) -> usize {
self.buttonlike_map.values().map(Vec::len).sum::<usize>()
+ self.axislike_map.values().map(Vec::len).sum::<usize>()
+ self.dual_axislike_map.values().map(Vec::len).sum::<usize>()
+ self
.triple_axislike_map
.values()
.map(Vec::len)
.sum::<usize>()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
self.buttonlike_map.clear();
self.axislike_map.clear();
self.dual_axislike_map.clear();
self.triple_axislike_map.clear();
}
}
impl<A: Actionlike> InputMap<A> {
pub fn clear_action(&mut self, action: &A) {
match action.input_control_kind() {
InputControlKind::Button => {
self.buttonlike_map.remove(action);
}
InputControlKind::Axis => {
self.axislike_map.remove(action);
}
InputControlKind::DualAxis => {
self.dual_axislike_map.remove(action);
}
InputControlKind::TripleAxis => {
self.triple_axislike_map.remove(action);
}
}
}
pub fn remove_at(&mut self, action: &A, index: usize) -> Option<()> {
match action.input_control_kind() {
InputControlKind::Button => {
let input_bindings = self.buttonlike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
InputControlKind::Axis => {
let input_bindings = self.axislike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
InputControlKind::DualAxis => {
let input_bindings = self.dual_axislike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
InputControlKind::TripleAxis => {
let input_bindings = self.triple_axislike_map.get_mut(action)?;
if input_bindings.len() > index {
input_bindings.remove(index);
Some(())
} else {
None
}
}
}
}
pub fn remove(&mut self, action: &A, input: impl Buttonlike) -> Option<usize> {
let bindings = self.buttonlike_map.get_mut(action)?;
let boxed_input: Box<dyn Buttonlike> = Box::new(input);
let index = bindings.iter().position(|input| input == &boxed_input)?;
bindings.remove(index);
Some(index)
}
}
impl<A: Actionlike, U: Buttonlike> From<HashMap<A, Vec<U>>> for InputMap<A> {
fn from(raw_map: HashMap<A, Vec<U>>) -> Self {
let mut input_map = Self::default();
for (action, inputs) in raw_map.into_iter() {
input_map.insert_one_to_many(action, inputs);
}
input_map
}
}
impl<A: Actionlike, U: Buttonlike> FromIterator<(A, U)> for InputMap<A> {
fn from_iter<T: IntoIterator<Item = (A, U)>>(iter: T) -> Self {
let mut input_map = Self::default();
for (action, input) in iter.into_iter() {
input_map.insert(action, input);
}
input_map
}
}
#[cfg(test)]
#[cfg(feature = "keyboard")]
mod tests {
use bevy::prelude::Reflect;
use serde::{Deserialize, Serialize};
use super::*;
use crate as leafwing_input_manager;
use crate::prelude::*;
#[derive(Actionlike, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug, Reflect)]
enum Action {
Run,
Jump,
Hide,
#[actionlike(Axis)]
Axis,
#[actionlike(DualAxis)]
DualAxis,
#[actionlike(TripleAxis)]
TripleAxis,
}
#[test]
fn creation() {
use bevy::input::keyboard::KeyCode;
let input_map = InputMap::default()
.with(Action::Run, KeyCode::KeyW)
.with(Action::Run, KeyCode::ShiftLeft)
.with(Action::Run, KeyCode::ShiftLeft)
.with_one_to_many(Action::Run, [KeyCode::KeyR, KeyCode::ShiftRight])
.with_multiple([
(Action::Jump, KeyCode::Space),
(Action::Hide, KeyCode::ControlLeft),
(Action::Hide, KeyCode::ControlRight),
]);
let expected_bindings: HashMap<Box<dyn Buttonlike>, Action> = [
(Box::new(KeyCode::KeyW) as Box<dyn Buttonlike>, Action::Run),
(
Box::new(KeyCode::ShiftLeft) as Box<dyn Buttonlike>,
Action::Run,
),
(Box::new(KeyCode::KeyR) as Box<dyn Buttonlike>, Action::Run),
(
Box::new(KeyCode::ShiftRight) as Box<dyn Buttonlike>,
Action::Run,
),
(
Box::new(KeyCode::Space) as Box<dyn Buttonlike>,
Action::Jump,
),
(
Box::new(KeyCode::ControlLeft) as Box<dyn Buttonlike>,
Action::Hide,
),
(
Box::new(KeyCode::ControlRight) as Box<dyn Buttonlike>,
Action::Hide,
),
]
.into_iter()
.collect();
for (action, input) in input_map.buttonlike_bindings() {
let expected_action = expected_bindings.get(input).unwrap();
assert_eq!(expected_action, action);
}
}
#[test]
fn insertion_idempotency() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::Space);
let expected: Vec<Box<dyn Buttonlike>> = vec![Box::new(KeyCode::Space)];
assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
input_map.insert(Action::Run, KeyCode::Space);
assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
}
#[test]
fn multiple_insertion() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::Space);
input_map.insert(Action::Run, KeyCode::Enter);
let expected: Vec<Box<dyn Buttonlike>> =
vec![Box::new(KeyCode::Space), Box::new(KeyCode::Enter)];
assert_eq!(input_map.get_buttonlike(&Action::Run), Some(&expected));
}
#[test]
fn input_clearing() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
input_map.insert(Action::Run, KeyCode::Space);
input_map.clear_action(&Action::Run);
assert_eq!(input_map, InputMap::default());
input_map.insert(Action::Run, KeyCode::Space);
input_map.insert(Action::Run, KeyCode::ShiftLeft);
assert!(input_map.remove_at(&Action::Run, 1).is_some());
assert!(
input_map.remove_at(&Action::Run, 1).is_none(),
"Should return None on second removal at the same index"
);
assert!(input_map.remove_at(&Action::Run, 0).is_some());
assert!(
input_map.remove_at(&Action::Run, 0).is_none(),
"Should return None on second removal at the same index"
);
}
#[test]
fn merging() {
use bevy::input::keyboard::KeyCode;
let mut input_map = InputMap::default();
let mut default_keyboard_map = InputMap::default();
default_keyboard_map.insert(Action::Run, KeyCode::ShiftLeft);
default_keyboard_map.insert(
Action::Hide,
ButtonlikeChord::new([KeyCode::ControlLeft, KeyCode::KeyH]),
);
let mut default_gamepad_map = InputMap::default();
default_gamepad_map.insert(Action::Run, KeyCode::Numpad0);
default_gamepad_map.insert(Action::Hide, KeyCode::Numpad7);
input_map.merge(&default_keyboard_map);
assert_eq!(input_map, default_keyboard_map);
input_map.merge(&default_keyboard_map);
assert_eq!(input_map, default_keyboard_map);
}
#[cfg(feature = "gamepad")]
#[test]
fn gamepad_swapping() {
use bevy::ecs::world::World;
let mut input_map = InputMap::<Action>::default();
assert_eq!(input_map.gamepad(), None);
let test_entity = World::new().spawn_empty().id();
input_map.set_gamepad(test_entity);
assert_eq!(input_map.gamepad(), Some(test_entity));
input_map.clear_gamepad();
assert_eq!(input_map.gamepad(), None);
}
#[cfg(feature = "keyboard")]
#[test]
fn input_map_serde() {
use bevy::prelude::{App, KeyCode};
use serde_test::{Token, assert_tokens};
let mut app = App::new();
app.add_plugins(InputManagerPlugin::<Action>::default());
let input_map = InputMap::new([(Action::Hide, KeyCode::ControlLeft)]);
assert_tokens(
&input_map,
&[
Token::Struct {
name: "InputMap",
len: 5,
},
Token::Str("buttonlike_map"),
Token::Map { len: Some(1) },
Token::UnitVariant {
name: "Action",
variant: "Hide",
},
Token::Seq { len: Some(1) },
Token::Map { len: Some(1) },
Token::BorrowedStr("KeyCode"),
Token::UnitVariant {
name: "KeyCode",
variant: "ControlLeft",
},
Token::MapEnd,
Token::SeqEnd,
Token::MapEnd,
Token::Str("axislike_map"),
Token::Map { len: Some(0) },
Token::MapEnd,
Token::Str("dual_axislike_map"),
Token::Map { len: Some(0) },
Token::MapEnd,
Token::Str("triple_axislike_map"),
Token::Map { len: Some(0) },
Token::MapEnd,
Token::Str("associated_gamepad"),
Token::None,
Token::StructEnd,
],
);
}
}