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 pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> 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 pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> bool {
202 let mouse_movement = input_store.pair(&MouseMove::default());
203 self.direction.is_active(mouse_movement, self.threshold)
204 }
205
206 fn press(&self, world: &mut World) {
208 world
209 .resource_mut::<Messages<MouseMotion>>()
210 .write(MouseMotion {
211 delta: self.direction.full_active_value(),
212 });
213 }
214
215 fn release(&self, _world: &mut World) {}
220}
221
222impl Eq for MouseMoveDirection {}
223
224impl Hash for MouseMoveDirection {
225 fn hash<H: Hasher>(&self, state: &mut H) {
226 self.direction.hash(state);
227 FloatOrd(self.threshold).hash(state);
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
261#[must_use]
262pub struct MouseMoveAxis {
263 pub axis: DualAxisType,
265
266 pub processors: Vec<AxisProcessor>,
268}
269
270impl MouseMoveAxis {
271 pub const X: Self = Self {
273 axis: DualAxisType::X,
274 processors: Vec::new(),
275 };
276
277 pub const Y: Self = Self {
279 axis: DualAxisType::Y,
280 processors: Vec::new(),
281 };
282}
283
284impl UserInput for MouseMoveAxis {
285 #[inline]
287 fn kind(&self) -> InputControlKind {
288 InputControlKind::Axis
289 }
290
291 #[inline]
293 fn decompose(&self) -> BasicInputs {
294 BasicInputs::Composite(vec![
295 Box::new(MouseMoveDirection {
296 direction: self.axis.negative(),
297 threshold: 0.0,
298 }),
299 Box::new(MouseMoveDirection {
300 direction: self.axis.positive(),
301 threshold: 0.0,
302 }),
303 ])
304 }
305}
306
307#[serde_typetag]
308impl Axislike for MouseMoveAxis {
309 #[inline]
312 fn value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> f32 {
313 let movement = input_store.pair(&MouseMove::default());
314 let value = self.axis.get_value(movement);
315 self.processors
316 .iter()
317 .fold(value, |value, processor| processor.process(value))
318 }
319
320 fn set_value(&self, world: &mut World, value: f32) {
322 let message = MouseMotion {
323 delta: match self.axis {
324 DualAxisType::X => Vec2::new(value, 0.0),
325 DualAxisType::Y => Vec2::new(0.0, value),
326 },
327 };
328 world.resource_mut::<Messages<MouseMotion>>().write(message);
329 }
330}
331
332impl WithAxisProcessingPipelineExt for MouseMoveAxis {
333 #[inline]
334 fn reset_processing_pipeline(mut self) -> Self {
335 self.processors.clear();
336 self
337 }
338
339 #[inline]
340 fn replace_processing_pipeline(
341 mut self,
342 processors: impl IntoIterator<Item = AxisProcessor>,
343 ) -> Self {
344 self.processors = processors.into_iter().collect();
345 self
346 }
347
348 #[inline]
349 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
350 self.processors.push(processor.into());
351 self
352 }
353}
354
355#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
384#[must_use]
385pub struct MouseMove {
386 pub processors: Vec<DualAxisProcessor>,
388}
389
390impl UpdatableInput for MouseMove {
391 type SourceData = SRes<AccumulatedMouseMotion>;
392
393 fn compute(
394 mut central_input_store: ResMut<CentralInputStore>,
395 source_data: StaticSystemParam<Self::SourceData>,
396 ) {
397 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
398 }
399}
400
401impl UserInput for MouseMove {
402 #[inline]
404 fn kind(&self) -> InputControlKind {
405 InputControlKind::DualAxis
406 }
407
408 #[inline]
410 fn decompose(&self) -> BasicInputs {
411 BasicInputs::Composite(vec![
412 Box::new(MouseMoveDirection::UP),
413 Box::new(MouseMoveDirection::DOWN),
414 Box::new(MouseMoveDirection::LEFT),
415 Box::new(MouseMoveDirection::RIGHT),
416 ])
417 }
418}
419
420#[serde_typetag]
421impl DualAxislike for MouseMove {
422 #[inline]
424 fn axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Vec2 {
425 let movement = input_store.pair(&MouseMove::default());
426 self.processors
427 .iter()
428 .fold(movement, |value, processor| processor.process(value))
429 }
430
431 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
433 world
434 .resource_mut::<Messages<MouseMotion>>()
435 .write(MouseMotion { delta: value });
436 }
437}
438
439impl WithDualAxisProcessingPipelineExt for MouseMove {
440 #[inline]
441 fn reset_processing_pipeline(mut self) -> Self {
442 self.processors.clear();
443 self
444 }
445
446 #[inline]
447 fn replace_processing_pipeline(
448 mut self,
449 processor: impl IntoIterator<Item = DualAxisProcessor>,
450 ) -> Self {
451 self.processors = processor.into_iter().collect();
452 self
453 }
454
455 #[inline]
456 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
457 self.processors.push(processor.into());
458 self
459 }
460}
461
462#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
488#[must_use]
489pub struct MouseScrollDirection {
490 pub direction: DualAxisDirection,
492
493 pub threshold: f32,
496}
497
498impl MouseScrollDirection {
499 #[inline]
509 pub fn threshold(mut self, threshold: f32) -> Self {
510 assert!(threshold >= 0.0);
511 self.threshold = threshold;
512 self
513 }
514
515 pub const UP: Self = Self {
517 direction: DualAxisDirection::Up,
518 threshold: 0.0,
519 };
520
521 pub const DOWN: Self = Self {
523 direction: DualAxisDirection::Down,
524 threshold: 0.0,
525 };
526
527 pub const LEFT: Self = Self {
529 direction: DualAxisDirection::Left,
530 threshold: 0.0,
531 };
532
533 pub const RIGHT: Self = Self {
535 direction: DualAxisDirection::Right,
536 threshold: 0.0,
537 };
538}
539
540impl UserInput for MouseScrollDirection {
541 #[inline]
543 fn kind(&self) -> InputControlKind {
544 InputControlKind::Button
545 }
546
547 #[inline]
549 fn decompose(&self) -> BasicInputs {
550 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
551 }
552}
553
554#[serde_typetag]
555impl Buttonlike for MouseScrollDirection {
556 #[inline]
558 fn pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> bool {
559 let movement = input_store.pair(&MouseScroll::default());
560 self.direction.is_active(movement, self.threshold)
561 }
562
563 fn press(&self, world: &mut World) {
569 let vec = self.direction.full_active_value();
570
571 world
572 .resource_mut::<Messages<MouseWheel>>()
573 .write(MouseWheel {
574 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
575 x: vec.x,
576 y: vec.y,
577 window: Entity::PLACEHOLDER,
578 });
579 }
580
581 fn release(&self, _world: &mut World) {}
586}
587
588impl Eq for MouseScrollDirection {}
589
590impl Hash for MouseScrollDirection {
591 fn hash<H: Hasher>(&self, state: &mut H) {
592 self.direction.hash(state);
593 FloatOrd(self.threshold).hash(state);
594 }
595}
596
597#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
627#[must_use]
628pub struct MouseScrollAxis {
629 pub axis: DualAxisType,
631
632 pub processors: Vec<AxisProcessor>,
634}
635
636impl MouseScrollAxis {
637 pub const X: Self = Self {
639 axis: DualAxisType::X,
640 processors: Vec::new(),
641 };
642
643 pub const Y: Self = Self {
645 axis: DualAxisType::Y,
646 processors: Vec::new(),
647 };
648}
649
650impl UserInput for MouseScrollAxis {
651 #[inline]
653 fn kind(&self) -> InputControlKind {
654 InputControlKind::Axis
655 }
656
657 #[inline]
659 fn decompose(&self) -> BasicInputs {
660 BasicInputs::Composite(vec![
661 Box::new(MouseScrollDirection {
662 direction: self.axis.negative(),
663 threshold: 0.0,
664 }),
665 Box::new(MouseScrollDirection {
666 direction: self.axis.positive(),
667 threshold: 0.0,
668 }),
669 ])
670 }
671}
672
673#[serde_typetag]
674impl Axislike for MouseScrollAxis {
675 #[inline]
678 fn value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> f32 {
679 let movement = input_store.pair(&MouseScroll::default());
680 let value = self.axis.get_value(movement);
681 self.processors
682 .iter()
683 .fold(value, |value, processor| processor.process(value))
684 }
685
686 fn set_value(&self, world: &mut World, value: f32) {
692 let message = MouseWheel {
693 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
694 x: if self.axis == DualAxisType::X {
695 value
696 } else {
697 0.0
698 },
699 y: if self.axis == DualAxisType::Y {
700 value
701 } else {
702 0.0
703 },
704 window: Entity::PLACEHOLDER,
705 };
706 world.resource_mut::<Messages<MouseWheel>>().write(message);
707 }
708}
709
710impl WithAxisProcessingPipelineExt for MouseScrollAxis {
711 #[inline]
712 fn reset_processing_pipeline(mut self) -> Self {
713 self.processors.clear();
714 self
715 }
716
717 #[inline]
718 fn replace_processing_pipeline(
719 mut self,
720 processors: impl IntoIterator<Item = AxisProcessor>,
721 ) -> Self {
722 self.processors = processors.into_iter().collect();
723 self
724 }
725
726 #[inline]
727 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
728 self.processors.push(processor.into());
729 self
730 }
731}
732
733#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
762#[must_use]
763pub struct MouseScroll {
764 pub processors: Vec<DualAxisProcessor>,
766}
767
768impl UpdatableInput for MouseScroll {
769 type SourceData = SRes<AccumulatedMouseScroll>;
770
771 fn compute(
772 mut central_input_store: ResMut<CentralInputStore>,
773 source_data: StaticSystemParam<Self::SourceData>,
774 ) {
775 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
776 }
777}
778
779impl UserInput for MouseScroll {
780 #[inline]
782 fn kind(&self) -> InputControlKind {
783 InputControlKind::DualAxis
784 }
785
786 #[inline]
788 fn decompose(&self) -> BasicInputs {
789 BasicInputs::Composite(vec![
790 Box::new(MouseScrollDirection::UP),
791 Box::new(MouseScrollDirection::DOWN),
792 Box::new(MouseScrollDirection::LEFT),
793 Box::new(MouseScrollDirection::RIGHT),
794 ])
795 }
796}
797
798#[serde_typetag]
799impl DualAxislike for MouseScroll {
800 #[inline]
802 fn axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Vec2 {
803 let movement = input_store.pair(&MouseScroll::default());
804 self.processors
805 .iter()
806 .fold(movement, |value, processor| processor.process(value))
807 }
808
809 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
814 world
815 .resource_mut::<Messages<MouseWheel>>()
816 .write(MouseWheel {
817 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
818 x: value.x,
819 y: value.y,
820 window: Entity::PLACEHOLDER,
821 });
822 }
823}
824
825impl WithDualAxisProcessingPipelineExt for MouseScroll {
826 #[inline]
827 fn reset_processing_pipeline(mut self) -> Self {
828 self.processors.clear();
829 self
830 }
831
832 #[inline]
833 fn replace_processing_pipeline(
834 mut self,
835 processors: impl IntoIterator<Item = DualAxisProcessor>,
836 ) -> Self {
837 self.processors = processors.into_iter().collect();
838 self
839 }
840
841 #[inline]
842 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
843 self.processors.push(processor.into());
844 self
845 }
846}
847
848#[cfg(test)]
849mod tests {
850 use super::*;
851 use crate::plugin::CentralInputStorePlugin;
852 use bevy::input::InputPlugin;
853 use bevy::prelude::*;
854
855 fn test_app() -> App {
856 let mut app = App::new();
857 app.add_plugins(InputPlugin)
858 .add_plugins(CentralInputStorePlugin);
859 app
860 }
861
862 #[test]
863 fn test_mouse_button() {
864 let left = MouseButton::Left;
865 assert_eq!(left.kind(), InputControlKind::Button);
866
867 let middle = MouseButton::Middle;
868 assert_eq!(middle.kind(), InputControlKind::Button);
869
870 let right = MouseButton::Right;
871 assert_eq!(right.kind(), InputControlKind::Button);
872
873 let mut app = test_app();
875 app.update();
876 let gamepad = app.world_mut().spawn(()).id();
877 let inputs = app.world().resource::<CentralInputStore>();
878
879 assert!(!left.pressed(inputs, gamepad));
880 assert!(!middle.pressed(inputs, gamepad));
881 assert!(!right.pressed(inputs, gamepad));
882
883 let mut app = test_app();
885 MouseButton::Left.press(app.world_mut());
886 app.update();
887 let inputs = app.world().resource::<CentralInputStore>();
888
889 assert!(left.pressed(inputs, gamepad));
890 assert!(!middle.pressed(inputs, gamepad));
891 assert!(!right.pressed(inputs, gamepad));
892
893 let mut app = test_app();
895 MouseButton::Middle.press(app.world_mut());
896 app.update();
897 let inputs = app.world().resource::<CentralInputStore>();
898
899 assert!(!left.pressed(inputs, gamepad));
900 assert!(middle.pressed(inputs, gamepad));
901 assert!(!right.pressed(inputs, gamepad));
902
903 let mut app = test_app();
905 MouseButton::Right.press(app.world_mut());
906 app.update();
907 let inputs = app.world().resource::<CentralInputStore>();
908
909 assert!(!left.pressed(inputs, gamepad));
910 assert!(!middle.pressed(inputs, gamepad));
911 assert!(right.pressed(inputs, gamepad));
912 }
913
914 #[test]
915 fn test_mouse_move() {
916 let mouse_move_up = MouseMoveDirection::UP;
917 assert_eq!(mouse_move_up.kind(), InputControlKind::Button);
918
919 let mouse_move_y = MouseMoveAxis::Y;
920 assert_eq!(mouse_move_y.kind(), InputControlKind::Axis);
921
922 let mouse_move = MouseMove::default();
923 assert_eq!(mouse_move.kind(), InputControlKind::DualAxis);
924
925 let mut app = test_app();
927 app.update();
928 let gamepad = app.world_mut().spawn(()).id();
929 let inputs = app.world().resource::<CentralInputStore>();
930
931 assert!(!mouse_move_up.pressed(inputs, gamepad));
932 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
933 assert_eq!(mouse_move.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
934
935 let data = Vec2::new(-1.0, 0.0);
937 let mut app = test_app();
938 MouseMoveDirection::LEFT.press(app.world_mut());
939 app.update();
940 let inputs = app.world().resource::<CentralInputStore>();
941
942 assert!(!mouse_move_up.pressed(inputs, gamepad));
943 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
944 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
945
946 let data = Vec2::new(0.0, 1.0);
948 let mut app = test_app();
949 MouseMoveDirection::UP.press(app.world_mut());
950 app.update();
951 let inputs = app.world().resource::<CentralInputStore>();
952
953 assert!(mouse_move_up.pressed(inputs, gamepad));
954 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
955 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
956
957 let data = Vec2::new(0.0, -1.0);
959 let mut app = test_app();
960 MouseMoveDirection::DOWN.press(app.world_mut());
961 app.update();
962 let inputs = app.world().resource::<CentralInputStore>();
963
964 assert!(!mouse_move_up.pressed(inputs, gamepad));
965 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
966 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
967
968 let data = Vec2::new(0.0, 3.0);
970 let mut app = test_app();
971 MouseMoveAxis::Y.set_value(app.world_mut(), data.y);
972 app.update();
973 let inputs = app.world().resource::<CentralInputStore>();
974
975 assert!(mouse_move_up.pressed(inputs, gamepad));
976 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
977 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
978
979 let data = Vec2::new(2.0, 3.0);
981 let mut app = test_app();
982 MouseMove::default().set_axis_pair(app.world_mut(), data);
983 app.update();
984 let inputs = app.world().resource::<CentralInputStore>();
985
986 assert!(mouse_move_up.pressed(inputs, gamepad));
987 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
988 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
989 }
990
991 #[test]
992 fn test_mouse_scroll() {
993 let mouse_scroll_up = MouseScrollDirection::UP;
994 assert_eq!(mouse_scroll_up.kind(), InputControlKind::Button);
995
996 let mouse_scroll_y = MouseScrollAxis::Y;
997 assert_eq!(mouse_scroll_y.kind(), InputControlKind::Axis);
998 let mouse_scroll = MouseScroll::default();
999 assert_eq!(mouse_scroll.kind(), InputControlKind::DualAxis);
1000
1001 let mut app = test_app();
1003 app.update();
1004 let gamepad = app.world_mut().spawn(()).id();
1005 let inputs = app.world().resource::<CentralInputStore>();
1006
1007 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1008 assert_eq!(mouse_scroll_y.value(inputs, gamepad), 0.0);
1009 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
1010
1011 let data = Vec2::new(0.0, 1.0);
1013 let mut app = test_app();
1014 MouseScrollDirection::UP.press(app.world_mut());
1015 app.update();
1016 let inputs = app.world().resource::<CentralInputStore>();
1017
1018 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1019 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1020 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1021
1022 let data = Vec2::new(0.0, -1.0);
1024 let mut app = test_app();
1025 MouseScrollDirection::DOWN.press(app.world_mut());
1026 app.update();
1027 let inputs = app.world().resource::<CentralInputStore>();
1028
1029 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1030 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1031 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1032
1033 let data = Vec2::new(0.0, 3.0);
1035 let mut app = test_app();
1036 MouseScrollAxis::Y.set_value(app.world_mut(), data.y);
1037 app.update();
1038 let inputs = app.world().resource::<CentralInputStore>();
1039
1040 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1041 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1042 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1043
1044 let data = Vec2::new(2.0, 3.0);
1046 let mut app = test_app();
1047 MouseScroll::default().set_axis_pair(app.world_mut(), data);
1048 app.update();
1049 let inputs = app.world().resource::<CentralInputStore>();
1050
1051 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1052 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1053 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1054 }
1055
1056 #[test]
1057 fn one_frame_accumulate_mouse_movement() {
1058 let mut app = test_app();
1059 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1060 MouseMoveAxis::Y.set_value(app.world_mut(), 2.0);
1061
1062 let mouse_motion_messages = app.world().get_resource::<Messages<MouseMotion>>().unwrap();
1063 for message in mouse_motion_messages.iter_current_update_messages() {
1064 dbg!("Message sent: {:?}", message);
1065 }
1066
1067 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1069 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 0.0));
1070
1071 app.update();
1072
1073 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1075 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 5.0));
1076
1077 let inputs = app.world().resource::<CentralInputStore>();
1078 assert_eq!(inputs.pair(&MouseMove::default()), Vec2::new(0.0, 5.0));
1079 }
1080
1081 #[test]
1082 fn multiple_frames_accumulate_mouse_movement() {
1083 let mut app = test_app();
1084 let inputs = app.world().resource::<CentralInputStore>();
1085 assert_eq!(
1087 inputs.pair(&MouseMove::default()),
1088 Vec2::ZERO,
1089 "Initial movement is not zero."
1090 );
1091
1092 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1094 app.update();
1096
1097 let inputs = app.world().resource::<CentralInputStore>();
1098 assert_eq!(
1100 inputs.pair(&MouseMove::default()),
1101 Vec2::new(0.0, 3.0),
1102 "Movement sent was not read correctly."
1103 );
1104
1105 app.update();
1107 let inputs = app.world().resource::<CentralInputStore>();
1108 assert_eq!(
1110 inputs.pair(&MouseMove::default()),
1111 Vec2::ZERO,
1112 "No movement was expected. Is the position in the message stream being cleared properly?"
1113 );
1114 }
1115}