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::message::Messages;
10use bevy::ecs::system::lifetimeless::SRes;
11use bevy::ecs::system::StaticSystemParam;
12use bevy::input::mouse::{
13 AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
14 MouseWheel,
15};
16use bevy::input::{ButtonInput, ButtonState};
17use bevy::math::FloatOrd;
18use bevy::prelude::{Entity, Reflect, ResMut, Vec2, World};
19use leafwing_input_manager_macros::serde_typetag;
20use serde::{Deserialize, Serialize};
21use std::hash::{Hash, Hasher};
22
23use super::updating::{CentralInputStore, UpdatableInput};
24use super::{Axislike, Buttonlike, DualAxislike};
25
26impl UserInput for MouseButton {
28 #[inline]
30 fn kind(&self) -> InputControlKind {
31 InputControlKind::Button
32 }
33
34 #[inline]
37 fn decompose(&self) -> BasicInputs {
38 BasicInputs::Simple(Box::new(*self))
39 }
40}
41
42impl UpdatableInput for MouseButton {
43 type SourceData = SRes<ButtonInput<MouseButton>>;
44
45 fn compute(
46 mut central_input_store: ResMut<CentralInputStore>,
47 source_data: StaticSystemParam<Self::SourceData>,
48 ) {
49 for button in source_data.get_pressed() {
50 central_input_store.update_buttonlike(*button, ButtonValue::from_pressed(true));
51 }
52
53 for button in source_data.get_just_released() {
54 central_input_store.update_buttonlike(*button, ButtonValue::from_pressed(false));
55 }
56 }
57}
58
59#[serde_typetag]
60impl Buttonlike for MouseButton {
61 #[inline]
63 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
64 input_store.pressed(self)
65 }
66
67 fn press(&self, world: &mut World) {
73 let mut messages = world.resource_mut::<Messages<MouseButtonInput>>();
74 messages.write(MouseButtonInput {
75 button: *self,
76 state: ButtonState::Pressed,
77 window: Entity::PLACEHOLDER,
78 });
79 }
80
81 fn release(&self, world: &mut World) {
87 let mut messages = world.resource_mut::<Messages<MouseButtonInput>>();
88 messages.write(MouseButtonInput {
89 button: *self,
90 state: ButtonState::Released,
91 window: Entity::PLACEHOLDER,
92 });
93 }
94
95 fn set_value(&self, world: &mut World, value: f32) {
97 if value > 0.0 {
98 self.press(world);
99 } else {
100 self.release(world);
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
131#[must_use]
132pub struct MouseMoveDirection {
133 pub direction: DualAxisDirection,
135
136 pub threshold: f32,
139}
140
141impl MouseMoveDirection {
142 #[inline]
152 pub fn threshold(mut self, threshold: f32) -> Self {
153 assert!(threshold >= 0.0);
154 self.threshold = threshold;
155 self
156 }
157
158 pub const UP: Self = Self {
160 direction: DualAxisDirection::Up,
161 threshold: 0.0,
162 };
163
164 pub const DOWN: Self = Self {
166 direction: DualAxisDirection::Down,
167 threshold: 0.0,
168 };
169
170 pub const LEFT: Self = Self {
172 direction: DualAxisDirection::Left,
173 threshold: 0.0,
174 };
175
176 pub const RIGHT: Self = Self {
178 direction: DualAxisDirection::Right,
179 threshold: 0.0,
180 };
181}
182
183impl UserInput for MouseMoveDirection {
184 #[inline]
186 fn kind(&self) -> InputControlKind {
187 InputControlKind::Button
188 }
189
190 #[inline]
192 fn decompose(&self) -> BasicInputs {
193 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
194 }
195}
196
197#[serde_typetag]
198impl Buttonlike for MouseMoveDirection {
199 #[inline]
201 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
202 let mouse_movement = input_store.pair(&MouseMove::default());
203 mouse_movement
204 .map(|mouse_movement| self.direction.is_active(mouse_movement, self.threshold))
205 }
206
207 fn press(&self, world: &mut World) {
209 world
210 .resource_mut::<Messages<MouseMotion>>()
211 .write(MouseMotion {
212 delta: self.direction.full_active_value(),
213 });
214 }
215
216 fn release(&self, _world: &mut World) {}
221}
222
223impl Eq for MouseMoveDirection {}
224
225impl Hash for MouseMoveDirection {
226 fn hash<H: Hasher>(&self, state: &mut H) {
227 self.direction.hash(state);
228 FloatOrd(self.threshold).hash(state);
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
262#[must_use]
263pub struct MouseMoveAxis {
264 pub axis: DualAxisType,
266
267 pub processors: Vec<AxisProcessor>,
269}
270
271impl MouseMoveAxis {
272 pub const X: Self = Self {
274 axis: DualAxisType::X,
275 processors: Vec::new(),
276 };
277
278 pub const Y: Self = Self {
280 axis: DualAxisType::Y,
281 processors: Vec::new(),
282 };
283}
284
285impl UserInput for MouseMoveAxis {
286 #[inline]
288 fn kind(&self) -> InputControlKind {
289 InputControlKind::Axis
290 }
291
292 #[inline]
294 fn decompose(&self) -> BasicInputs {
295 BasicInputs::Composite(vec![
296 Box::new(MouseMoveDirection {
297 direction: self.axis.negative(),
298 threshold: 0.0,
299 }),
300 Box::new(MouseMoveDirection {
301 direction: self.axis.positive(),
302 threshold: 0.0,
303 }),
304 ])
305 }
306}
307
308#[serde_typetag]
309impl Axislike for MouseMoveAxis {
310 #[inline]
313 fn get_value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<f32> {
314 input_store.pair(&MouseMove::default()).map(|movement| {
315 let value = self.axis.get_value(movement);
316
317 self.processors
318 .iter()
319 .fold(value, |value, processor| processor.process(value))
320 })
321 }
322
323 fn set_value(&self, world: &mut World, value: f32) {
325 let message = MouseMotion {
326 delta: match self.axis {
327 DualAxisType::X => Vec2::new(value, 0.0),
328 DualAxisType::Y => Vec2::new(0.0, value),
329 },
330 };
331 world.resource_mut::<Messages<MouseMotion>>().write(message);
332 }
333}
334
335impl WithAxisProcessingPipelineExt for MouseMoveAxis {
336 #[inline]
337 fn reset_processing_pipeline(mut self) -> Self {
338 self.processors.clear();
339 self
340 }
341
342 #[inline]
343 fn replace_processing_pipeline(
344 mut self,
345 processors: impl IntoIterator<Item = AxisProcessor>,
346 ) -> Self {
347 self.processors = processors.into_iter().collect();
348 self
349 }
350
351 #[inline]
352 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
353 self.processors.push(processor.into());
354 self
355 }
356}
357
358#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
387#[must_use]
388pub struct MouseMove {
389 pub processors: Vec<DualAxisProcessor>,
391}
392
393impl UpdatableInput for MouseMove {
394 type SourceData = SRes<AccumulatedMouseMotion>;
395
396 fn compute(
397 mut central_input_store: ResMut<CentralInputStore>,
398 source_data: StaticSystemParam<Self::SourceData>,
399 ) {
400 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
401 }
402}
403
404impl UserInput for MouseMove {
405 #[inline]
407 fn kind(&self) -> InputControlKind {
408 InputControlKind::DualAxis
409 }
410
411 #[inline]
413 fn decompose(&self) -> BasicInputs {
414 BasicInputs::Composite(vec![
415 Box::new(MouseMoveDirection::UP),
416 Box::new(MouseMoveDirection::DOWN),
417 Box::new(MouseMoveDirection::LEFT),
418 Box::new(MouseMoveDirection::RIGHT),
419 ])
420 }
421}
422
423#[serde_typetag]
424impl DualAxislike for MouseMove {
425 #[inline]
427 fn get_axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<Vec2> {
428 input_store.pair(&MouseMove::default()).map(|movement| {
429 self.processors
430 .iter()
431 .fold(movement, |value, processor| processor.process(value))
432 })
433 }
434
435 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
437 world
438 .resource_mut::<Messages<MouseMotion>>()
439 .write(MouseMotion { delta: value });
440 }
441}
442
443impl WithDualAxisProcessingPipelineExt for MouseMove {
444 #[inline]
445 fn reset_processing_pipeline(mut self) -> Self {
446 self.processors.clear();
447 self
448 }
449
450 #[inline]
451 fn replace_processing_pipeline(
452 mut self,
453 processor: impl IntoIterator<Item = DualAxisProcessor>,
454 ) -> Self {
455 self.processors = processor.into_iter().collect();
456 self
457 }
458
459 #[inline]
460 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
461 self.processors.push(processor.into());
462 self
463 }
464}
465
466#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
492#[must_use]
493pub struct MouseScrollDirection {
494 pub direction: DualAxisDirection,
496
497 pub threshold: f32,
500}
501
502impl MouseScrollDirection {
503 #[inline]
513 pub fn threshold(mut self, threshold: f32) -> Self {
514 assert!(threshold >= 0.0);
515 self.threshold = threshold;
516 self
517 }
518
519 pub const UP: Self = Self {
521 direction: DualAxisDirection::Up,
522 threshold: 0.0,
523 };
524
525 pub const DOWN: Self = Self {
527 direction: DualAxisDirection::Down,
528 threshold: 0.0,
529 };
530
531 pub const LEFT: Self = Self {
533 direction: DualAxisDirection::Left,
534 threshold: 0.0,
535 };
536
537 pub const RIGHT: Self = Self {
539 direction: DualAxisDirection::Right,
540 threshold: 0.0,
541 };
542}
543
544impl UserInput for MouseScrollDirection {
545 #[inline]
547 fn kind(&self) -> InputControlKind {
548 InputControlKind::Button
549 }
550
551 #[inline]
553 fn decompose(&self) -> BasicInputs {
554 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
555 }
556}
557
558#[serde_typetag]
559impl Buttonlike for MouseScrollDirection {
560 #[inline]
562 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
563 input_store
564 .pair(&MouseScroll::default())
565 .map(|movement| self.direction.is_active(movement, self.threshold))
566 }
567
568 fn press(&self, world: &mut World) {
574 let vec = self.direction.full_active_value();
575
576 world
577 .resource_mut::<Messages<MouseWheel>>()
578 .write(MouseWheel {
579 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
580 x: vec.x,
581 y: vec.y,
582 window: Entity::PLACEHOLDER,
583 });
584 }
585
586 fn release(&self, _world: &mut World) {}
591}
592
593impl Eq for MouseScrollDirection {}
594
595impl Hash for MouseScrollDirection {
596 fn hash<H: Hasher>(&self, state: &mut H) {
597 self.direction.hash(state);
598 FloatOrd(self.threshold).hash(state);
599 }
600}
601
602#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
632#[must_use]
633pub struct MouseScrollAxis {
634 pub axis: DualAxisType,
636
637 pub processors: Vec<AxisProcessor>,
639}
640
641impl MouseScrollAxis {
642 pub const X: Self = Self {
644 axis: DualAxisType::X,
645 processors: Vec::new(),
646 };
647
648 pub const Y: Self = Self {
650 axis: DualAxisType::Y,
651 processors: Vec::new(),
652 };
653}
654
655impl UserInput for MouseScrollAxis {
656 #[inline]
658 fn kind(&self) -> InputControlKind {
659 InputControlKind::Axis
660 }
661
662 #[inline]
664 fn decompose(&self) -> BasicInputs {
665 BasicInputs::Composite(vec![
666 Box::new(MouseScrollDirection {
667 direction: self.axis.negative(),
668 threshold: 0.0,
669 }),
670 Box::new(MouseScrollDirection {
671 direction: self.axis.positive(),
672 threshold: 0.0,
673 }),
674 ])
675 }
676}
677
678#[serde_typetag]
679impl Axislike for MouseScrollAxis {
680 #[inline]
683 fn get_value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<f32> {
684 input_store.pair(&MouseScroll::default()).map(|movement| {
685 let value = self.axis.get_value(movement);
686
687 self.processors
688 .iter()
689 .fold(value, |value, processor| processor.process(value))
690 })
691 }
692
693 fn set_value(&self, world: &mut World, value: f32) {
699 let message = MouseWheel {
700 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
701 x: if self.axis == DualAxisType::X {
702 value
703 } else {
704 0.0
705 },
706 y: if self.axis == DualAxisType::Y {
707 value
708 } else {
709 0.0
710 },
711 window: Entity::PLACEHOLDER,
712 };
713 world.resource_mut::<Messages<MouseWheel>>().write(message);
714 }
715}
716
717impl WithAxisProcessingPipelineExt for MouseScrollAxis {
718 #[inline]
719 fn reset_processing_pipeline(mut self) -> Self {
720 self.processors.clear();
721 self
722 }
723
724 #[inline]
725 fn replace_processing_pipeline(
726 mut self,
727 processors: impl IntoIterator<Item = AxisProcessor>,
728 ) -> Self {
729 self.processors = processors.into_iter().collect();
730 self
731 }
732
733 #[inline]
734 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
735 self.processors.push(processor.into());
736 self
737 }
738}
739
740#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
769#[must_use]
770pub struct MouseScroll {
771 pub processors: Vec<DualAxisProcessor>,
773}
774
775impl UpdatableInput for MouseScroll {
776 type SourceData = SRes<AccumulatedMouseScroll>;
777
778 fn compute(
779 mut central_input_store: ResMut<CentralInputStore>,
780 source_data: StaticSystemParam<Self::SourceData>,
781 ) {
782 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
783 }
784}
785
786impl UserInput for MouseScroll {
787 #[inline]
789 fn kind(&self) -> InputControlKind {
790 InputControlKind::DualAxis
791 }
792
793 #[inline]
795 fn decompose(&self) -> BasicInputs {
796 BasicInputs::Composite(vec![
797 Box::new(MouseScrollDirection::UP),
798 Box::new(MouseScrollDirection::DOWN),
799 Box::new(MouseScrollDirection::LEFT),
800 Box::new(MouseScrollDirection::RIGHT),
801 ])
802 }
803}
804
805#[serde_typetag]
806impl DualAxislike for MouseScroll {
807 #[inline]
809 fn get_axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<Vec2> {
810 input_store.pair(&MouseScroll::default()).map(|movement| {
811 self.processors
812 .iter()
813 .fold(movement, |value, processor| processor.process(value))
814 })
815 }
816
817 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
822 world
823 .resource_mut::<Messages<MouseWheel>>()
824 .write(MouseWheel {
825 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
826 x: value.x,
827 y: value.y,
828 window: Entity::PLACEHOLDER,
829 });
830 }
831}
832
833impl WithDualAxisProcessingPipelineExt for MouseScroll {
834 #[inline]
835 fn reset_processing_pipeline(mut self) -> Self {
836 self.processors.clear();
837 self
838 }
839
840 #[inline]
841 fn replace_processing_pipeline(
842 mut self,
843 processors: impl IntoIterator<Item = DualAxisProcessor>,
844 ) -> Self {
845 self.processors = processors.into_iter().collect();
846 self
847 }
848
849 #[inline]
850 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
851 self.processors.push(processor.into());
852 self
853 }
854}
855
856#[cfg(test)]
857mod tests {
858 use super::*;
859 use crate::plugin::CentralInputStorePlugin;
860 use bevy::input::InputPlugin;
861 use bevy::prelude::*;
862
863 fn test_app() -> App {
864 let mut app = App::new();
865 app.add_plugins(InputPlugin)
866 .add_plugins(CentralInputStorePlugin);
867 app
868 }
869
870 #[test]
871 fn test_mouse_button() {
872 let left = MouseButton::Left;
873 assert_eq!(left.kind(), InputControlKind::Button);
874
875 let middle = MouseButton::Middle;
876 assert_eq!(middle.kind(), InputControlKind::Button);
877
878 let right = MouseButton::Right;
879 assert_eq!(right.kind(), InputControlKind::Button);
880
881 let mut app = test_app();
883 app.update();
884 let gamepad = app.world_mut().spawn(()).id();
885 let inputs = app.world().resource::<CentralInputStore>();
886
887 assert!(!left.pressed(inputs, gamepad));
888 assert!(!middle.pressed(inputs, gamepad));
889 assert!(!right.pressed(inputs, gamepad));
890
891 let mut app = test_app();
893 MouseButton::Left.press(app.world_mut());
894 app.update();
895 let inputs = app.world().resource::<CentralInputStore>();
896
897 assert!(left.pressed(inputs, gamepad));
898 assert!(!middle.pressed(inputs, gamepad));
899 assert!(!right.pressed(inputs, gamepad));
900
901 let mut app = test_app();
903 MouseButton::Middle.press(app.world_mut());
904 app.update();
905 let inputs = app.world().resource::<CentralInputStore>();
906
907 assert!(!left.pressed(inputs, gamepad));
908 assert!(middle.pressed(inputs, gamepad));
909 assert!(!right.pressed(inputs, gamepad));
910
911 let mut app = test_app();
913 MouseButton::Right.press(app.world_mut());
914 app.update();
915 let inputs = app.world().resource::<CentralInputStore>();
916
917 assert!(!left.pressed(inputs, gamepad));
918 assert!(!middle.pressed(inputs, gamepad));
919 assert!(right.pressed(inputs, gamepad));
920 }
921
922 #[test]
923 fn test_mouse_move() {
924 let mouse_move_up = MouseMoveDirection::UP;
925 assert_eq!(mouse_move_up.kind(), InputControlKind::Button);
926
927 let mouse_move_y = MouseMoveAxis::Y;
928 assert_eq!(mouse_move_y.kind(), InputControlKind::Axis);
929
930 let mouse_move = MouseMove::default();
931 assert_eq!(mouse_move.kind(), InputControlKind::DualAxis);
932
933 let mut app = test_app();
935 app.update();
936 let gamepad = app.world_mut().spawn(()).id();
937 let inputs = app.world().resource::<CentralInputStore>();
938
939 assert!(!mouse_move_up.pressed(inputs, gamepad));
940 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
941 assert_eq!(mouse_move.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
942
943 let data = Vec2::new(-1.0, 0.0);
945 let mut app = test_app();
946 MouseMoveDirection::LEFT.press(app.world_mut());
947 app.update();
948 let inputs = app.world().resource::<CentralInputStore>();
949
950 assert!(!mouse_move_up.pressed(inputs, gamepad));
951 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
952 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
953
954 let data = Vec2::new(0.0, 1.0);
956 let mut app = test_app();
957 MouseMoveDirection::UP.press(app.world_mut());
958 app.update();
959 let inputs = app.world().resource::<CentralInputStore>();
960
961 assert!(mouse_move_up.pressed(inputs, gamepad));
962 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
963 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
964
965 let data = Vec2::new(0.0, -1.0);
967 let mut app = test_app();
968 MouseMoveDirection::DOWN.press(app.world_mut());
969 app.update();
970 let inputs = app.world().resource::<CentralInputStore>();
971
972 assert!(!mouse_move_up.pressed(inputs, gamepad));
973 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
974 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
975
976 let data = Vec2::new(0.0, 3.0);
978 let mut app = test_app();
979 MouseMoveAxis::Y.set_value(app.world_mut(), data.y);
980 app.update();
981 let inputs = app.world().resource::<CentralInputStore>();
982
983 assert!(mouse_move_up.pressed(inputs, gamepad));
984 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
985 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
986
987 let data = Vec2::new(2.0, 3.0);
989 let mut app = test_app();
990 MouseMove::default().set_axis_pair(app.world_mut(), data);
991 app.update();
992 let inputs = app.world().resource::<CentralInputStore>();
993
994 assert!(mouse_move_up.pressed(inputs, gamepad));
995 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
996 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
997 }
998
999 #[test]
1000 fn test_mouse_scroll() {
1001 let mouse_scroll_up = MouseScrollDirection::UP;
1002 assert_eq!(mouse_scroll_up.kind(), InputControlKind::Button);
1003
1004 let mouse_scroll_y = MouseScrollAxis::Y;
1005 assert_eq!(mouse_scroll_y.kind(), InputControlKind::Axis);
1006 let mouse_scroll = MouseScroll::default();
1007 assert_eq!(mouse_scroll.kind(), InputControlKind::DualAxis);
1008
1009 let mut app = test_app();
1011 app.update();
1012 let gamepad = app.world_mut().spawn(()).id();
1013 let inputs = app.world().resource::<CentralInputStore>();
1014
1015 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1016 assert_eq!(mouse_scroll_y.value(inputs, gamepad), 0.0);
1017 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
1018
1019 let data = Vec2::new(0.0, 1.0);
1021 let mut app = test_app();
1022 MouseScrollDirection::UP.press(app.world_mut());
1023 app.update();
1024 let inputs = app.world().resource::<CentralInputStore>();
1025
1026 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1027 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1028 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1029
1030 let data = Vec2::new(0.0, -1.0);
1032 let mut app = test_app();
1033 MouseScrollDirection::DOWN.press(app.world_mut());
1034 app.update();
1035 let inputs = app.world().resource::<CentralInputStore>();
1036
1037 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1038 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1039 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1040
1041 let data = Vec2::new(0.0, 3.0);
1043 let mut app = test_app();
1044 MouseScrollAxis::Y.set_value(app.world_mut(), data.y);
1045 app.update();
1046 let inputs = app.world().resource::<CentralInputStore>();
1047
1048 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1049 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1050 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1051
1052 let data = Vec2::new(2.0, 3.0);
1054 let mut app = test_app();
1055 MouseScroll::default().set_axis_pair(app.world_mut(), data);
1056 app.update();
1057 let inputs = app.world().resource::<CentralInputStore>();
1058
1059 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1060 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1061 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1062 }
1063
1064 #[test]
1065 fn one_frame_accumulate_mouse_movement() {
1066 let mut app = test_app();
1067 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1068 MouseMoveAxis::Y.set_value(app.world_mut(), 2.0);
1069
1070 let mouse_motion_messages = app.world().get_resource::<Messages<MouseMotion>>().unwrap();
1071 for message in mouse_motion_messages.iter_current_update_messages() {
1072 dbg!("Message sent: {:?}", message);
1073 }
1074
1075 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1077 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 0.0));
1078
1079 app.update();
1080
1081 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1083 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 5.0));
1084
1085 let inputs = app.world().resource::<CentralInputStore>();
1086 assert_eq!(
1087 inputs.pair(&MouseMove::default()).unwrap(),
1088 Vec2::new(0.0, 5.0)
1089 );
1090 }
1091
1092 #[test]
1093 fn multiple_frames_accumulate_mouse_movement() {
1094 let mut app = test_app();
1095 let inputs = app.world().resource::<CentralInputStore>();
1096 assert_eq!(
1098 inputs.pair(&MouseMove::default()),
1099 None,
1100 "Initial movement is not None."
1101 );
1102
1103 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1105 app.update();
1107
1108 let inputs = app.world().resource::<CentralInputStore>();
1109 assert_eq!(
1111 inputs.pair(&MouseMove::default()).unwrap(),
1112 Vec2::new(0.0, 3.0),
1113 "Movement sent was not read correctly."
1114 );
1115
1116 app.update();
1118 let inputs = app.world().resource::<CentralInputStore>();
1119 assert_eq!(
1121 inputs.pair(&MouseMove::default()).unwrap(),
1122 Vec2::ZERO,
1123 "No movement was expected. Is the position in the message stream being cleared properly?"
1124 );
1125 }
1126}