diff --git a/crate/bevy_controls/src/resource.rs b/crate/bevy_controls/src/resource.rs index ca0e98f..69bbf8c 100755 --- a/crate/bevy_controls/src/resource.rs +++ b/crate/bevy_controls/src/resource.rs @@ -495,10 +495,16 @@ impl Default for Bindings { impl Bindings { pub fn new(bindings: Vec>) -> Self { - Self { - list: bindings, + let mut result = Self { + list: Vec::with_capacity(bindings.len()), ..Default::default() + }; + + // To avoid duplicates + for binding in bindings { + result.force_push(binding); } + result } pub fn customizable(mut self) -> Self { @@ -517,6 +523,9 @@ impl Bindings { } pub fn force_push(&mut self, binding: Binding) { + if self.list.contains(&binding) { + return; + } self.list.push(binding); } @@ -525,7 +534,7 @@ impl Bindings { warn!("You try to push binding to immutable bindings"); return; } - self.list.push(binding); + self.force_push(binding); } pub fn clear(&mut self) { @@ -716,9 +725,16 @@ impl Controls { } } -#[cfg(all(test, feature = "logical-keyboard"))] +#[cfg(all( + test, + feature = "logical-keyboard", + not(feature = "serialize"), + not(feature = "reflect"), + not(feature = "inspector-egui") +))] mod tests { use super::*; + use bevy_state::state::States; #[test] fn keyboard_variants_are_distinct() { @@ -727,4 +743,24 @@ mod tests { assert_ne!(physical, logical); } + + #[derive(States, PartialEq, Eq, Clone, Hash, Debug, Default)] + enum TestState { + #[default] + Playing, + } + + impl crate::contract::GameState for TestState {} + + #[test] + fn bindings_reject_duplicates() { + let binding: Binding = + Binding::from_single(InputType::Keyboard(Keyboard::KeyCode(KeyCode::KeyH))); + let mut bindings = Bindings::new(vec![binding.clone(), binding.clone()]).customizable(); + + bindings.push(binding.clone()); + bindings.force_push(binding); + + assert_eq!(bindings.iter().count(), 1); + } }