leafwing_input_manager/user_input/
chord.rs1use bevy::math::{Vec2, Vec3};
4use bevy::prelude::{Entity, Reflect, World};
5use leafwing_input_manager_macros::serde_typetag;
6use serde::{Deserialize, Serialize};
7
8use crate as leafwing_input_manager;
9use crate::clashing_inputs::BasicInputs;
10use crate::user_input::{Buttonlike, TripleAxislike, UserInput};
11use crate::InputControlKind;
12
13use super::updating::CentralInputStore;
14use super::{Axislike, DualAxislike};
15
16#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
49#[must_use]
50pub struct ButtonlikeChord(
51 pub(crate) Vec<Box<dyn Buttonlike>>,
57);
58
59impl ButtonlikeChord {
60 #[inline]
64 pub fn new<U: Buttonlike>(inputs: impl IntoIterator<Item = U>) -> Self {
65 Self::default().with_multiple(inputs)
66 }
67
68 #[inline]
71 pub fn from_single(input: impl Buttonlike) -> Self {
72 Self::default().with(input)
73 }
74
75 #[cfg(feature = "keyboard")]
77 pub fn modified(modifier: super::keyboard::ModifierKey, input: impl Buttonlike) -> Self {
78 Self::default().with(modifier).with(input)
79 }
80
81 #[inline]
83 pub fn with(mut self, input: impl Buttonlike) -> Self {
84 self.push_boxed_unique(Box::new(input));
85 self
86 }
87
88 #[inline]
91 pub fn with_multiple<U: Buttonlike>(mut self, inputs: impl IntoIterator<Item = U>) -> Self {
92 for input in inputs.into_iter() {
93 self.push_boxed_unique(Box::new(input));
94 }
95 self
96 }
97
98 #[inline]
100 fn push_boxed_unique(&mut self, input: Box<dyn Buttonlike>) {
101 if !self.0.contains(&input) {
102 self.0.push(input);
103 }
104 }
105}
106
107impl UserInput for ButtonlikeChord {
108 #[inline]
110 fn kind(&self) -> InputControlKind {
111 InputControlKind::Button
112 }
113
114 #[inline]
118 fn decompose(&self) -> BasicInputs {
119 let inputs = self
120 .0
121 .iter()
122 .flat_map(|input| input.decompose().inputs())
123 .collect();
124 BasicInputs::Chord(inputs)
125 }
126}
127
128#[serde_typetag]
129impl Buttonlike for ButtonlikeChord {
130 #[inline]
132 fn pressed(&self, input_store: &CentralInputStore, gamepad: Entity) -> bool {
133 self.0
134 .iter()
135 .all(|input| input.pressed(input_store, gamepad))
136 }
137
138 fn press(&self, world: &mut World) {
139 for input in &self.0 {
140 input.press(world);
141 }
142 }
143
144 fn release(&self, world: &mut World) {
145 for input in &self.0 {
146 input.release(world);
147 }
148 }
149
150 fn press_as_gamepad(&self, world: &mut World, gamepad: Option<Entity>) {
151 for input in &self.0 {
152 input.press_as_gamepad(world, gamepad);
153 }
154 }
155
156 fn release_as_gamepad(&self, world: &mut World, gamepad: Option<Entity>) {
157 for input in &self.0 {
158 input.release_as_gamepad(world, gamepad);
159 }
160 }
161}
162
163impl<U: Buttonlike> FromIterator<U> for ButtonlikeChord {
164 #[inline]
168 fn from_iter<T: IntoIterator<Item = U>>(iter: T) -> Self {
169 Self::default().with_multiple(iter)
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
176#[must_use]
177pub struct AxislikeChord {
178 pub button: Box<dyn Buttonlike>,
180 pub axis: Box<dyn Axislike>,
182}
183
184impl AxislikeChord {
185 #[inline]
187 pub fn new(button: impl Buttonlike, axis: impl Axislike) -> Self {
188 Self {
189 button: Box::new(button),
190 axis: Box::new(axis),
191 }
192 }
193}
194
195impl UserInput for AxislikeChord {
196 #[inline]
198 fn kind(&self) -> InputControlKind {
199 InputControlKind::Axis
200 }
201
202 #[inline]
204 fn decompose(&self) -> BasicInputs {
205 BasicInputs::compose(self.button.decompose(), self.axis.decompose())
206 }
207}
208
209#[serde_typetag]
210impl Axislike for AxislikeChord {
211 fn value(&self, input_store: &CentralInputStore, gamepad: Entity) -> f32 {
212 if self.button.pressed(input_store, gamepad) {
213 self.axis.value(input_store, gamepad)
214 } else {
215 0.0
216 }
217 }
218
219 fn set_value(&self, world: &mut World, value: f32) {
220 self.axis.set_value(world, value);
221 }
222
223 fn set_value_as_gamepad(&self, world: &mut World, value: f32, gamepad: Option<Entity>) {
224 self.axis.set_value_as_gamepad(world, value, gamepad);
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
231#[must_use]
232pub struct DualAxislikeChord {
233 pub button: Box<dyn Buttonlike>,
235 pub dual_axis: Box<dyn DualAxislike>,
237}
238
239impl DualAxislikeChord {
240 #[inline]
242 pub fn new(button: impl Buttonlike, dual_axis: impl DualAxislike) -> Self {
243 Self {
244 button: Box::new(button),
245 dual_axis: Box::new(dual_axis),
246 }
247 }
248}
249
250impl UserInput for DualAxislikeChord {
251 #[inline]
253 fn kind(&self) -> InputControlKind {
254 InputControlKind::DualAxis
255 }
256
257 #[inline]
259 fn decompose(&self) -> BasicInputs {
260 BasicInputs::compose(self.button.decompose(), self.dual_axis.decompose())
261 }
262}
263
264#[serde_typetag]
265impl DualAxislike for DualAxislikeChord {
266 fn axis_pair(&self, input_store: &CentralInputStore, gamepad: Entity) -> Vec2 {
267 if self.button.pressed(input_store, gamepad) {
268 self.dual_axis.axis_pair(input_store, gamepad)
269 } else {
270 Vec2::ZERO
271 }
272 }
273
274 fn set_axis_pair(&self, world: &mut World, axis_pair: Vec2) {
275 self.dual_axis.set_axis_pair(world, axis_pair);
276 }
277
278 fn set_axis_pair_as_gamepad(
279 &self,
280 world: &mut World,
281 axis_pair: Vec2,
282 gamepad: Option<Entity>,
283 ) {
284 self.dual_axis
285 .set_axis_pair_as_gamepad(world, axis_pair, gamepad);
286 }
287}
288
289#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
292#[must_use]
293pub struct TripleAxislikeChord {
294 pub button: Box<dyn Buttonlike>,
296 pub triple_axis: Box<dyn TripleAxislike>,
298}
299
300impl TripleAxislikeChord {
301 #[inline]
303 pub fn new(button: impl Buttonlike, triple_axis: impl TripleAxislike) -> Self {
304 Self {
305 button: Box::new(button),
306 triple_axis: Box::new(triple_axis),
307 }
308 }
309}
310
311impl UserInput for TripleAxislikeChord {
312 #[inline]
314 fn kind(&self) -> InputControlKind {
315 InputControlKind::TripleAxis
316 }
317
318 #[inline]
320 fn decompose(&self) -> BasicInputs {
321 BasicInputs::compose(self.button.decompose(), self.triple_axis.decompose())
322 }
323}
324
325#[serde_typetag]
326impl TripleAxislike for TripleAxislikeChord {
327 fn axis_triple(&self, input_store: &CentralInputStore, gamepad: Entity) -> Vec3 {
328 if self.button.pressed(input_store, gamepad) {
329 self.triple_axis.axis_triple(input_store, gamepad)
330 } else {
331 Vec3::ZERO
332 }
333 }
334
335 fn set_axis_triple(&self, world: &mut World, axis_triple: Vec3) {
336 self.triple_axis.set_axis_triple(world, axis_triple);
337 }
338
339 fn set_axis_triple_as_gamepad(
340 &self,
341 world: &mut World,
342 axis_triple: Vec3,
343 gamepad: Option<Entity>,
344 ) {
345 self.triple_axis
346 .set_axis_triple_as_gamepad(world, axis_triple, gamepad);
347 }
348}
349
350#[cfg(feature = "keyboard")]
351#[cfg(test)]
352mod tests {
353 use super::ButtonlikeChord;
354 use crate::plugin::CentralInputStorePlugin;
355 use crate::user_input::updating::CentralInputStore;
356 use crate::user_input::Buttonlike;
357 use bevy::input::gamepad::{GamepadConnection, GamepadConnectionEvent, GamepadEvent};
358 use bevy::input::InputPlugin;
359 use bevy::prelude::*;
360
361 fn test_app() -> App {
362 let mut app = App::new();
363 app.add_plugins(MinimalPlugins)
364 .add_plugins(InputPlugin)
365 .add_plugins(CentralInputStorePlugin);
366
367 let gamepad = app.world_mut().spawn(()).id();
370 let mut gamepad_events = app.world_mut().resource_mut::<Events<GamepadEvent>>();
371 gamepad_events.send(GamepadEvent::Connection(GamepadConnectionEvent {
372 gamepad,
374 connection: GamepadConnection::Connected {
375 name: "TestController".into(),
376 vendor_id: None,
377 product_id: None,
378 },
379 }));
380
381 app.update();
383 app.update();
385 app
386 }
387
388 #[test]
389 fn test_chord_with_buttons_only() {
390 let chord = ButtonlikeChord::new([KeyCode::KeyC, KeyCode::KeyH])
391 .with(KeyCode::KeyO)
392 .with_multiple([KeyCode::KeyR, KeyCode::KeyD]);
393
394 let required_keys = [
395 KeyCode::KeyC,
396 KeyCode::KeyH,
397 KeyCode::KeyO,
398 KeyCode::KeyR,
399 KeyCode::KeyD,
400 ];
401
402 let expected_inners = required_keys
403 .iter()
404 .map(|key| Box::new(*key) as Box<dyn Buttonlike>)
405 .collect::<Vec<_>>();
406 assert_eq!(chord.0, expected_inners);
407
408 let mut app = test_app();
410 app.update();
411 let gamepad = app.world_mut().spawn(()).id();
412 let inputs = app.world().resource::<CentralInputStore>();
413 assert!(!chord.pressed(inputs, gamepad));
414
415 let mut app = test_app();
417 for key in required_keys {
418 key.press(app.world_mut());
419 }
420 app.update();
421 let inputs = app.world().resource::<CentralInputStore>();
422 assert!(chord.pressed(inputs, gamepad));
423
424 for i in 1..=4 {
427 let mut app = test_app();
428 for key in required_keys.iter().take(i) {
429 key.press(app.world_mut());
430 }
431 app.update();
432 let inputs = app.world().resource::<CentralInputStore>();
433 assert!(!chord.pressed(inputs, gamepad));
434 }
435
436 let mut app = test_app();
439 for key in required_keys.iter().take(4) {
440 key.press(app.world_mut());
441 }
442 KeyCode::KeyB.press(app.world_mut());
443 app.update();
444 let inputs = app.world().resource::<CentralInputStore>();
445 assert!(!chord.pressed(inputs, gamepad));
446 }
447}