1use crate as leafwing_input_manager;
4use crate::axislike::{DualAxisDirection, DualAxisType};
5use crate::buttonlike::ButtonValue;
6use crate::clashing_inputs::BasicInputs;
7use crate::input_processing::*;
8use crate::user_input::{InputControlKind, UserInput};
9use bevy::ecs::system::lifetimeless::SRes;
10use bevy::ecs::system::StaticSystemParam;
11use bevy::input::mouse::{
12 AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
13 MouseWheel,
14};
15use bevy::input::{ButtonInput, ButtonState};
16use bevy::math::FloatOrd;
17use bevy::prelude::{Entity, Events, Reflect, ResMut, Vec2, World};
18use leafwing_input_manager_macros::serde_typetag;
19use serde::{Deserialize, Serialize};
20use std::hash::{Hash, Hasher};
21
22use super::updating::{CentralInputStore, UpdatableInput};
23use super::{Axislike, Buttonlike, DualAxislike};
24
25impl UserInput for MouseButton {
27 #[inline]
29 fn kind(&self) -> InputControlKind {
30 InputControlKind::Button
31 }
32
33 #[inline]
36 fn decompose(&self) -> BasicInputs {
37 BasicInputs::Simple(Box::new(*self))
38 }
39}
40
41impl UpdatableInput for MouseButton {
42 type SourceData = SRes<ButtonInput<MouseButton>>;
43
44 fn compute(
45 mut central_input_store: ResMut<CentralInputStore>,
46 source_data: StaticSystemParam<Self::SourceData>,
47 ) {
48 for button in source_data.get_pressed() {
49 central_input_store.update_buttonlike(*button, ButtonValue::from_pressed(true));
50 }
51
52 for button in source_data.get_just_released() {
53 central_input_store.update_buttonlike(*button, ButtonValue::from_pressed(false));
54 }
55 }
56}
57
58#[serde_typetag]
59impl Buttonlike for MouseButton {
60 #[inline]
62 fn pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> bool {
63 input_store.pressed(self)
64 }
65
66 fn press(&self, world: &mut World) {
72 let mut events = world.resource_mut::<Events<MouseButtonInput>>();
73 events.send(MouseButtonInput {
74 button: *self,
75 state: ButtonState::Pressed,
76 window: Entity::PLACEHOLDER,
77 });
78 }
79
80 fn release(&self, world: &mut World) {
86 let mut events = world.resource_mut::<Events<MouseButtonInput>>();
87 events.send(MouseButtonInput {
88 button: *self,
89 state: ButtonState::Released,
90 window: Entity::PLACEHOLDER,
91 });
92 }
93
94 fn set_value(&self, world: &mut World, value: f32) {
96 if value > 0.0 {
97 self.press(world);
98 } else {
99 self.release(world);
100 }
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
130#[must_use]
131pub struct MouseMoveDirection {
132 pub direction: DualAxisDirection,
134
135 pub threshold: f32,
138}
139
140impl MouseMoveDirection {
141 #[inline]
151 pub fn threshold(mut self, threshold: f32) -> Self {
152 assert!(threshold >= 0.0);
153 self.threshold = threshold;
154 self
155 }
156
157 pub const UP: Self = Self {
159 direction: DualAxisDirection::Up,
160 threshold: 0.0,
161 };
162
163 pub const DOWN: Self = Self {
165 direction: DualAxisDirection::Down,
166 threshold: 0.0,
167 };
168
169 pub const LEFT: Self = Self {
171 direction: DualAxisDirection::Left,
172 threshold: 0.0,
173 };
174
175 pub const RIGHT: Self = Self {
177 direction: DualAxisDirection::Right,
178 threshold: 0.0,
179 };
180}
181
182impl UserInput for MouseMoveDirection {
183 #[inline]
185 fn kind(&self) -> InputControlKind {
186 InputControlKind::Button
187 }
188
189 #[inline]
191 fn decompose(&self) -> BasicInputs {
192 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
193 }
194}
195
196#[serde_typetag]
197impl Buttonlike for MouseMoveDirection {
198 #[inline]
200 fn pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> bool {
201 let mouse_movement = input_store.pair(&MouseMove::default());
202 self.direction.is_active(mouse_movement, self.threshold)
203 }
204
205 fn press(&self, world: &mut World) {
207 world
208 .resource_mut::<Events<MouseMotion>>()
209 .send(MouseMotion {
210 delta: self.direction.full_active_value(),
211 });
212 }
213
214 fn release(&self, _world: &mut World) {}
219}
220
221impl Eq for MouseMoveDirection {}
222
223impl Hash for MouseMoveDirection {
224 fn hash<H: Hasher>(&self, state: &mut H) {
225 self.direction.hash(state);
226 FloatOrd(self.threshold).hash(state);
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
260#[must_use]
261pub struct MouseMoveAxis {
262 pub axis: DualAxisType,
264
265 pub processors: Vec<AxisProcessor>,
267}
268
269impl MouseMoveAxis {
270 pub const X: Self = Self {
272 axis: DualAxisType::X,
273 processors: Vec::new(),
274 };
275
276 pub const Y: Self = Self {
278 axis: DualAxisType::Y,
279 processors: Vec::new(),
280 };
281}
282
283impl UserInput for MouseMoveAxis {
284 #[inline]
286 fn kind(&self) -> InputControlKind {
287 InputControlKind::Axis
288 }
289
290 #[inline]
292 fn decompose(&self) -> BasicInputs {
293 BasicInputs::Composite(vec![
294 Box::new(MouseMoveDirection {
295 direction: self.axis.negative(),
296 threshold: 0.0,
297 }),
298 Box::new(MouseMoveDirection {
299 direction: self.axis.positive(),
300 threshold: 0.0,
301 }),
302 ])
303 }
304}
305
306#[serde_typetag]
307impl Axislike for MouseMoveAxis {
308 #[inline]
311 fn value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> f32 {
312 let movement = input_store.pair(&MouseMove::default());
313 let value = self.axis.get_value(movement);
314 self.processors
315 .iter()
316 .fold(value, |value, processor| processor.process(value))
317 }
318
319 fn set_value(&self, world: &mut World, value: f32) {
321 let event = MouseMotion {
322 delta: match self.axis {
323 DualAxisType::X => Vec2::new(value, 0.0),
324 DualAxisType::Y => Vec2::new(0.0, value),
325 },
326 };
327 world.resource_mut::<Events<MouseMotion>>().send(event);
328 }
329}
330
331impl WithAxisProcessingPipelineExt for MouseMoveAxis {
332 #[inline]
333 fn reset_processing_pipeline(mut self) -> Self {
334 self.processors.clear();
335 self
336 }
337
338 #[inline]
339 fn replace_processing_pipeline(
340 mut self,
341 processors: impl IntoIterator<Item = AxisProcessor>,
342 ) -> Self {
343 self.processors = processors.into_iter().collect();
344 self
345 }
346
347 #[inline]
348 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
349 self.processors.push(processor.into());
350 self
351 }
352}
353
354#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
383#[must_use]
384pub struct MouseMove {
385 pub processors: Vec<DualAxisProcessor>,
387}
388
389impl UpdatableInput for MouseMove {
390 type SourceData = SRes<AccumulatedMouseMotion>;
391
392 fn compute(
393 mut central_input_store: ResMut<CentralInputStore>,
394 source_data: StaticSystemParam<Self::SourceData>,
395 ) {
396 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
397 }
398}
399
400impl UserInput for MouseMove {
401 #[inline]
403 fn kind(&self) -> InputControlKind {
404 InputControlKind::DualAxis
405 }
406
407 #[inline]
409 fn decompose(&self) -> BasicInputs {
410 BasicInputs::Composite(vec![
411 Box::new(MouseMoveDirection::UP),
412 Box::new(MouseMoveDirection::DOWN),
413 Box::new(MouseMoveDirection::LEFT),
414 Box::new(MouseMoveDirection::RIGHT),
415 ])
416 }
417}
418
419#[serde_typetag]
420impl DualAxislike for MouseMove {
421 #[inline]
423 fn axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Vec2 {
424 let movement = input_store.pair(&MouseMove::default());
425 self.processors
426 .iter()
427 .fold(movement, |value, processor| processor.process(value))
428 }
429
430 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
432 world
433 .resource_mut::<Events<MouseMotion>>()
434 .send(MouseMotion { delta: value });
435 }
436}
437
438impl WithDualAxisProcessingPipelineExt for MouseMove {
439 #[inline]
440 fn reset_processing_pipeline(mut self) -> Self {
441 self.processors.clear();
442 self
443 }
444
445 #[inline]
446 fn replace_processing_pipeline(
447 mut self,
448 processor: impl IntoIterator<Item = DualAxisProcessor>,
449 ) -> Self {
450 self.processors = processor.into_iter().collect();
451 self
452 }
453
454 #[inline]
455 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
456 self.processors.push(processor.into());
457 self
458 }
459}
460
461#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
487#[must_use]
488pub struct MouseScrollDirection {
489 pub direction: DualAxisDirection,
491
492 pub threshold: f32,
495}
496
497impl MouseScrollDirection {
498 #[inline]
508 pub fn threshold(mut self, threshold: f32) -> Self {
509 assert!(threshold >= 0.0);
510 self.threshold = threshold;
511 self
512 }
513
514 pub const UP: Self = Self {
516 direction: DualAxisDirection::Up,
517 threshold: 0.0,
518 };
519
520 pub const DOWN: Self = Self {
522 direction: DualAxisDirection::Down,
523 threshold: 0.0,
524 };
525
526 pub const LEFT: Self = Self {
528 direction: DualAxisDirection::Left,
529 threshold: 0.0,
530 };
531
532 pub const RIGHT: Self = Self {
534 direction: DualAxisDirection::Right,
535 threshold: 0.0,
536 };
537}
538
539impl UserInput for MouseScrollDirection {
540 #[inline]
542 fn kind(&self) -> InputControlKind {
543 InputControlKind::Button
544 }
545
546 #[inline]
548 fn decompose(&self) -> BasicInputs {
549 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
550 }
551}
552
553#[serde_typetag]
554impl Buttonlike for MouseScrollDirection {
555 #[inline]
557 fn pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> bool {
558 let movement = input_store.pair(&MouseScroll::default());
559 self.direction.is_active(movement, self.threshold)
560 }
561
562 fn press(&self, world: &mut World) {
568 let vec = self.direction.full_active_value();
569
570 world.resource_mut::<Events<MouseWheel>>().send(MouseWheel {
571 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
572 x: vec.x,
573 y: vec.y,
574 window: Entity::PLACEHOLDER,
575 });
576 }
577
578 fn release(&self, _world: &mut World) {}
583}
584
585impl Eq for MouseScrollDirection {}
586
587impl Hash for MouseScrollDirection {
588 fn hash<H: Hasher>(&self, state: &mut H) {
589 self.direction.hash(state);
590 FloatOrd(self.threshold).hash(state);
591 }
592}
593
594#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
624#[must_use]
625pub struct MouseScrollAxis {
626 pub axis: DualAxisType,
628
629 pub processors: Vec<AxisProcessor>,
631}
632
633impl MouseScrollAxis {
634 pub const X: Self = Self {
636 axis: DualAxisType::X,
637 processors: Vec::new(),
638 };
639
640 pub const Y: Self = Self {
642 axis: DualAxisType::Y,
643 processors: Vec::new(),
644 };
645}
646
647impl UserInput for MouseScrollAxis {
648 #[inline]
650 fn kind(&self) -> InputControlKind {
651 InputControlKind::Axis
652 }
653
654 #[inline]
656 fn decompose(&self) -> BasicInputs {
657 BasicInputs::Composite(vec![
658 Box::new(MouseScrollDirection {
659 direction: self.axis.negative(),
660 threshold: 0.0,
661 }),
662 Box::new(MouseScrollDirection {
663 direction: self.axis.positive(),
664 threshold: 0.0,
665 }),
666 ])
667 }
668}
669
670#[serde_typetag]
671impl Axislike for MouseScrollAxis {
672 #[inline]
675 fn value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> f32 {
676 let movement = input_store.pair(&MouseScroll::default());
677 let value = self.axis.get_value(movement);
678 self.processors
679 .iter()
680 .fold(value, |value, processor| processor.process(value))
681 }
682
683 fn set_value(&self, world: &mut World, value: f32) {
689 let event = MouseWheel {
690 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
691 x: if self.axis == DualAxisType::X {
692 value
693 } else {
694 0.0
695 },
696 y: if self.axis == DualAxisType::Y {
697 value
698 } else {
699 0.0
700 },
701 window: Entity::PLACEHOLDER,
702 };
703 world.resource_mut::<Events<MouseWheel>>().send(event);
704 }
705}
706
707impl WithAxisProcessingPipelineExt for MouseScrollAxis {
708 #[inline]
709 fn reset_processing_pipeline(mut self) -> Self {
710 self.processors.clear();
711 self
712 }
713
714 #[inline]
715 fn replace_processing_pipeline(
716 mut self,
717 processors: impl IntoIterator<Item = AxisProcessor>,
718 ) -> Self {
719 self.processors = processors.into_iter().collect();
720 self
721 }
722
723 #[inline]
724 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
725 self.processors.push(processor.into());
726 self
727 }
728}
729
730#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
759#[must_use]
760pub struct MouseScroll {
761 pub processors: Vec<DualAxisProcessor>,
763}
764
765impl UpdatableInput for MouseScroll {
766 type SourceData = SRes<AccumulatedMouseScroll>;
767
768 fn compute(
769 mut central_input_store: ResMut<CentralInputStore>,
770 source_data: StaticSystemParam<Self::SourceData>,
771 ) {
772 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
773 }
774}
775
776impl UserInput for MouseScroll {
777 #[inline]
779 fn kind(&self) -> InputControlKind {
780 InputControlKind::DualAxis
781 }
782
783 #[inline]
785 fn decompose(&self) -> BasicInputs {
786 BasicInputs::Composite(vec![
787 Box::new(MouseScrollDirection::UP),
788 Box::new(MouseScrollDirection::DOWN),
789 Box::new(MouseScrollDirection::LEFT),
790 Box::new(MouseScrollDirection::RIGHT),
791 ])
792 }
793}
794
795#[serde_typetag]
796impl DualAxislike for MouseScroll {
797 #[inline]
799 fn axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Vec2 {
800 let movement = input_store.pair(&MouseScroll::default());
801 self.processors
802 .iter()
803 .fold(movement, |value, processor| processor.process(value))
804 }
805
806 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
811 world.resource_mut::<Events<MouseWheel>>().send(MouseWheel {
812 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
813 x: value.x,
814 y: value.y,
815 window: Entity::PLACEHOLDER,
816 });
817 }
818}
819
820impl WithDualAxisProcessingPipelineExt for MouseScroll {
821 #[inline]
822 fn reset_processing_pipeline(mut self) -> Self {
823 self.processors.clear();
824 self
825 }
826
827 #[inline]
828 fn replace_processing_pipeline(
829 mut self,
830 processors: impl IntoIterator<Item = DualAxisProcessor>,
831 ) -> Self {
832 self.processors = processors.into_iter().collect();
833 self
834 }
835
836 #[inline]
837 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
838 self.processors.push(processor.into());
839 self
840 }
841}
842
843#[cfg(test)]
844mod tests {
845 use super::*;
846 use crate::plugin::CentralInputStorePlugin;
847 use bevy::input::InputPlugin;
848 use bevy::prelude::*;
849
850 fn test_app() -> App {
851 let mut app = App::new();
852 app.add_plugins(InputPlugin)
853 .add_plugins(CentralInputStorePlugin);
854 app
855 }
856
857 #[test]
858 fn test_mouse_button() {
859 let left = MouseButton::Left;
860 assert_eq!(left.kind(), InputControlKind::Button);
861
862 let middle = MouseButton::Middle;
863 assert_eq!(middle.kind(), InputControlKind::Button);
864
865 let right = MouseButton::Right;
866 assert_eq!(right.kind(), InputControlKind::Button);
867
868 let mut app = test_app();
870 app.update();
871 let gamepad = app.world_mut().spawn(()).id();
872 let inputs = app.world().resource::<CentralInputStore>();
873
874 assert!(!left.pressed(inputs, gamepad));
875 assert!(!middle.pressed(inputs, gamepad));
876 assert!(!right.pressed(inputs, gamepad));
877
878 let mut app = test_app();
880 MouseButton::Left.press(app.world_mut());
881 app.update();
882 let inputs = app.world().resource::<CentralInputStore>();
883
884 assert!(left.pressed(inputs, gamepad));
885 assert!(!middle.pressed(inputs, gamepad));
886 assert!(!right.pressed(inputs, gamepad));
887
888 let mut app = test_app();
890 MouseButton::Middle.press(app.world_mut());
891 app.update();
892 let inputs = app.world().resource::<CentralInputStore>();
893
894 assert!(!left.pressed(inputs, gamepad));
895 assert!(middle.pressed(inputs, gamepad));
896 assert!(!right.pressed(inputs, gamepad));
897
898 let mut app = test_app();
900 MouseButton::Right.press(app.world_mut());
901 app.update();
902 let inputs = app.world().resource::<CentralInputStore>();
903
904 assert!(!left.pressed(inputs, gamepad));
905 assert!(!middle.pressed(inputs, gamepad));
906 assert!(right.pressed(inputs, gamepad));
907 }
908
909 #[test]
910 fn test_mouse_move() {
911 let mouse_move_up = MouseMoveDirection::UP;
912 assert_eq!(mouse_move_up.kind(), InputControlKind::Button);
913
914 let mouse_move_y = MouseMoveAxis::Y;
915 assert_eq!(mouse_move_y.kind(), InputControlKind::Axis);
916
917 let mouse_move = MouseMove::default();
918 assert_eq!(mouse_move.kind(), InputControlKind::DualAxis);
919
920 let mut app = test_app();
922 app.update();
923 let gamepad = app.world_mut().spawn(()).id();
924 let inputs = app.world().resource::<CentralInputStore>();
925
926 assert!(!mouse_move_up.pressed(inputs, gamepad));
927 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
928 assert_eq!(mouse_move.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
929
930 let data = Vec2::new(-1.0, 0.0);
932 let mut app = test_app();
933 MouseMoveDirection::LEFT.press(app.world_mut());
934 app.update();
935 let inputs = app.world().resource::<CentralInputStore>();
936
937 assert!(!mouse_move_up.pressed(inputs, gamepad));
938 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
939 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
940
941 let data = Vec2::new(0.0, 1.0);
943 let mut app = test_app();
944 MouseMoveDirection::UP.press(app.world_mut());
945 app.update();
946 let inputs = app.world().resource::<CentralInputStore>();
947
948 assert!(mouse_move_up.pressed(inputs, gamepad));
949 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
950 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
951
952 let data = Vec2::new(0.0, -1.0);
954 let mut app = test_app();
955 MouseMoveDirection::DOWN.press(app.world_mut());
956 app.update();
957 let inputs = app.world().resource::<CentralInputStore>();
958
959 assert!(!mouse_move_up.pressed(inputs, gamepad));
960 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
961 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
962
963 let data = Vec2::new(0.0, 3.0);
965 let mut app = test_app();
966 MouseMoveAxis::Y.set_value(app.world_mut(), data.y);
967 app.update();
968 let inputs = app.world().resource::<CentralInputStore>();
969
970 assert!(mouse_move_up.pressed(inputs, gamepad));
971 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
972 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
973
974 let data = Vec2::new(2.0, 3.0);
976 let mut app = test_app();
977 MouseMove::default().set_axis_pair(app.world_mut(), data);
978 app.update();
979 let inputs = app.world().resource::<CentralInputStore>();
980
981 assert!(mouse_move_up.pressed(inputs, gamepad));
982 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
983 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
984 }
985
986 #[test]
987 fn test_mouse_scroll() {
988 let mouse_scroll_up = MouseScrollDirection::UP;
989 assert_eq!(mouse_scroll_up.kind(), InputControlKind::Button);
990
991 let mouse_scroll_y = MouseScrollAxis::Y;
992 assert_eq!(mouse_scroll_y.kind(), InputControlKind::Axis);
993 let mouse_scroll = MouseScroll::default();
994 assert_eq!(mouse_scroll.kind(), InputControlKind::DualAxis);
995
996 let mut app = test_app();
998 app.update();
999 let gamepad = app.world_mut().spawn(()).id();
1000 let inputs = app.world().resource::<CentralInputStore>();
1001
1002 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1003 assert_eq!(mouse_scroll_y.value(inputs, gamepad), 0.0);
1004 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
1005
1006 let data = Vec2::new(0.0, 1.0);
1008 let mut app = test_app();
1009 MouseScrollDirection::UP.press(app.world_mut());
1010 app.update();
1011 let inputs = app.world().resource::<CentralInputStore>();
1012
1013 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1014 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1015 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1016
1017 let data = Vec2::new(0.0, -1.0);
1019 let mut app = test_app();
1020 MouseScrollDirection::DOWN.press(app.world_mut());
1021 app.update();
1022 let inputs = app.world().resource::<CentralInputStore>();
1023
1024 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1025 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1026 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1027
1028 let data = Vec2::new(0.0, 3.0);
1030 let mut app = test_app();
1031 MouseScrollAxis::Y.set_value(app.world_mut(), data.y);
1032 app.update();
1033 let inputs = app.world().resource::<CentralInputStore>();
1034
1035 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1036 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1037 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1038
1039 let data = Vec2::new(2.0, 3.0);
1041 let mut app = test_app();
1042 MouseScroll::default().set_axis_pair(app.world_mut(), data);
1043 app.update();
1044 let inputs = app.world().resource::<CentralInputStore>();
1045
1046 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1047 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1048 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1049 }
1050
1051 #[test]
1052 fn one_frame_accumulate_mouse_movement() {
1053 let mut app = test_app();
1054 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1055 MouseMoveAxis::Y.set_value(app.world_mut(), 2.0);
1056
1057 let mouse_motion_events = app.world().get_resource::<Events<MouseMotion>>().unwrap();
1058 for event in mouse_motion_events.iter_current_update_events() {
1059 dbg!("Event sent: {:?}", event);
1060 }
1061
1062 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1064 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 0.0));
1065
1066 app.update();
1067
1068 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1070 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 5.0));
1071
1072 let inputs = app.world().resource::<CentralInputStore>();
1073 assert_eq!(inputs.pair(&MouseMove::default()), Vec2::new(0.0, 5.0));
1074 }
1075
1076 #[test]
1077 fn multiple_frames_accumulate_mouse_movement() {
1078 let mut app = test_app();
1079 let inputs = app.world().resource::<CentralInputStore>();
1080 assert_eq!(
1082 inputs.pair(&MouseMove::default()),
1083 Vec2::ZERO,
1084 "Initial movement is not zero."
1085 );
1086
1087 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1089 app.update();
1091
1092 let inputs = app.world().resource::<CentralInputStore>();
1093 assert_eq!(
1095 inputs.pair(&MouseMove::default()),
1096 Vec2::new(0.0, 3.0),
1097 "Movement sent was not read correctly."
1098 );
1099
1100 app.update();
1102 let inputs = app.world().resource::<CentralInputStore>();
1103 assert_eq!(
1105 inputs.pair(&MouseMove::default()),
1106 Vec2::ZERO,
1107 "No movement was expected. Is the position in the event stream being cleared properly?"
1108 );
1109 }
1110}