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::StaticSystemParam;
11use bevy::ecs::system::lifetimeless::SRes;
12use bevy::input::mouse::{
13 AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
14 MouseScrollUnit, MouseWheel,
15};
16use bevy::input::touch::TouchPhase;
17use bevy::input::{ButtonInput, ButtonState};
18use bevy::math::FloatOrd;
19use bevy::prelude::{Entity, Reflect, ResMut, Vec2, World};
20use leafwing_input_manager_macros::serde_typetag;
21use serde::{Deserialize, Serialize};
22use std::hash::{Hash, Hasher};
23
24use super::updating::{CentralInputStore, UpdatableInput};
25use super::{Axislike, Buttonlike, DualAxislike};
26
27impl UserInput for MouseButton {
29 #[inline]
31 fn kind(&self) -> InputControlKind {
32 InputControlKind::Button
33 }
34
35 #[inline]
38 fn decompose(&self) -> BasicInputs {
39 BasicInputs::Simple(Box::new(*self))
40 }
41}
42
43impl UpdatableInput for MouseButton {
44 type SourceData = SRes<ButtonInput<MouseButton>>;
45
46 fn compute(
47 mut central_input_store: ResMut<CentralInputStore>,
48 source_data: StaticSystemParam<Self::SourceData>,
49 ) {
50 for button in source_data.get_pressed() {
51 central_input_store.update_buttonlike(*button, ButtonValue::from_pressed(true));
52 }
53
54 for button in source_data.get_just_released() {
55 central_input_store.update_buttonlike(*button, ButtonValue::from_pressed(false));
56 }
57 }
58}
59
60#[serde_typetag]
61impl Buttonlike for MouseButton {
62 #[inline]
64 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
65 input_store.pressed(self)
66 }
67
68 fn press(&self, world: &mut World) {
74 let mut messages = world.resource_mut::<Messages<MouseButtonInput>>();
75 messages.write(MouseButtonInput {
76 button: *self,
77 state: ButtonState::Pressed,
78 window: Entity::PLACEHOLDER,
79 });
80 }
81
82 fn release(&self, world: &mut World) {
88 let mut messages = world.resource_mut::<Messages<MouseButtonInput>>();
89 messages.write(MouseButtonInput {
90 button: *self,
91 state: ButtonState::Released,
92 window: Entity::PLACEHOLDER,
93 });
94 }
95
96 fn set_value(&self, world: &mut World, value: f32) {
98 if value > 0.0 {
99 self.press(world);
100 } else {
101 self.release(world);
102 }
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
132#[must_use]
133pub struct MouseMoveDirection {
134 pub direction: DualAxisDirection,
136
137 pub threshold: f32,
140}
141
142impl MouseMoveDirection {
143 #[inline]
153 pub fn threshold(mut self, threshold: f32) -> Self {
154 assert!(threshold >= 0.0);
155 self.threshold = threshold;
156 self
157 }
158
159 pub const UP: Self = Self {
161 direction: DualAxisDirection::Up,
162 threshold: 0.0,
163 };
164
165 pub const DOWN: Self = Self {
167 direction: DualAxisDirection::Down,
168 threshold: 0.0,
169 };
170
171 pub const LEFT: Self = Self {
173 direction: DualAxisDirection::Left,
174 threshold: 0.0,
175 };
176
177 pub const RIGHT: Self = Self {
179 direction: DualAxisDirection::Right,
180 threshold: 0.0,
181 };
182}
183
184impl UserInput for MouseMoveDirection {
185 #[inline]
187 fn kind(&self) -> InputControlKind {
188 InputControlKind::Button
189 }
190
191 #[inline]
193 fn decompose(&self) -> BasicInputs {
194 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
195 }
196}
197
198#[serde_typetag]
199impl Buttonlike for MouseMoveDirection {
200 #[inline]
202 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
203 let mouse_movement = input_store.pair(&MouseMove::default());
204 mouse_movement
205 .map(|mouse_movement| self.direction.is_active(mouse_movement, self.threshold))
206 }
207
208 fn press(&self, world: &mut World) {
210 world
211 .resource_mut::<Messages<MouseMotion>>()
212 .write(MouseMotion {
213 delta: self.direction.full_active_value(),
214 });
215 }
216
217 fn release(&self, _world: &mut World) {}
222}
223
224impl Eq for MouseMoveDirection {}
225
226impl Hash for MouseMoveDirection {
227 fn hash<H: Hasher>(&self, state: &mut H) {
228 self.direction.hash(state);
229 FloatOrd(self.threshold).hash(state);
230 }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
263#[must_use]
264pub struct MouseMoveAxis {
265 pub axis: DualAxisType,
267
268 pub processors: Vec<AxisProcessor>,
270}
271
272impl MouseMoveAxis {
273 pub const X: Self = Self {
275 axis: DualAxisType::X,
276 processors: Vec::new(),
277 };
278
279 pub const Y: Self = Self {
281 axis: DualAxisType::Y,
282 processors: Vec::new(),
283 };
284}
285
286impl UserInput for MouseMoveAxis {
287 #[inline]
289 fn kind(&self) -> InputControlKind {
290 InputControlKind::Axis
291 }
292
293 #[inline]
295 fn decompose(&self) -> BasicInputs {
296 BasicInputs::Composite(vec![
297 Box::new(MouseMoveDirection {
298 direction: self.axis.negative(),
299 threshold: 0.0,
300 }),
301 Box::new(MouseMoveDirection {
302 direction: self.axis.positive(),
303 threshold: 0.0,
304 }),
305 ])
306 }
307}
308
309#[serde_typetag]
310impl Axislike for MouseMoveAxis {
311 #[inline]
314 fn get_value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<f32> {
315 input_store.pair(&MouseMove::default()).map(|movement| {
316 let value = self.axis.get_value(movement);
317
318 self.processors
319 .iter()
320 .fold(value, |value, processor| processor.process(value))
321 })
322 }
323
324 fn set_value(&self, world: &mut World, value: f32) {
326 let message = MouseMotion {
327 delta: match self.axis {
328 DualAxisType::X => Vec2::new(value, 0.0),
329 DualAxisType::Y => Vec2::new(0.0, value),
330 },
331 };
332 world.resource_mut::<Messages<MouseMotion>>().write(message);
333 }
334}
335
336impl WithAxisProcessingPipelineExt for MouseMoveAxis {
337 #[inline]
338 fn reset_processing_pipeline(mut self) -> Self {
339 self.processors.clear();
340 self
341 }
342
343 #[inline]
344 fn replace_processing_pipeline(
345 mut self,
346 processors: impl IntoIterator<Item = AxisProcessor>,
347 ) -> Self {
348 self.processors = processors.into_iter().collect();
349 self
350 }
351
352 #[inline]
353 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
354 self.processors.push(processor.into());
355 self
356 }
357}
358
359#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
388#[must_use]
389pub struct MouseMove {
390 pub processors: Vec<DualAxisProcessor>,
392}
393
394impl UpdatableInput for MouseMove {
395 type SourceData = SRes<AccumulatedMouseMotion>;
396
397 fn compute(
398 mut central_input_store: ResMut<CentralInputStore>,
399 source_data: StaticSystemParam<Self::SourceData>,
400 ) {
401 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
402 }
403}
404
405impl UserInput for MouseMove {
406 #[inline]
408 fn kind(&self) -> InputControlKind {
409 InputControlKind::DualAxis
410 }
411
412 #[inline]
414 fn decompose(&self) -> BasicInputs {
415 BasicInputs::Composite(vec![
416 Box::new(MouseMoveDirection::UP),
417 Box::new(MouseMoveDirection::DOWN),
418 Box::new(MouseMoveDirection::LEFT),
419 Box::new(MouseMoveDirection::RIGHT),
420 ])
421 }
422}
423
424#[serde_typetag]
425impl DualAxislike for MouseMove {
426 #[inline]
428 fn get_axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<Vec2> {
429 input_store.pair(&MouseMove::default()).map(|movement| {
430 self.processors
431 .iter()
432 .fold(movement, |value, processor| processor.process(value))
433 })
434 }
435
436 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
438 world
439 .resource_mut::<Messages<MouseMotion>>()
440 .write(MouseMotion { delta: value });
441 }
442}
443
444impl WithDualAxisProcessingPipelineExt for MouseMove {
445 #[inline]
446 fn reset_processing_pipeline(mut self) -> Self {
447 self.processors.clear();
448 self
449 }
450
451 #[inline]
452 fn replace_processing_pipeline(
453 mut self,
454 processor: impl IntoIterator<Item = DualAxisProcessor>,
455 ) -> Self {
456 self.processors = processor.into_iter().collect();
457 self
458 }
459
460 #[inline]
461 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
462 self.processors.push(processor.into());
463 self
464 }
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Reflect, Serialize, Deserialize)]
493#[must_use]
494pub struct MouseScrollDirection {
495 pub direction: DualAxisDirection,
497
498 pub threshold: f32,
501}
502
503impl MouseScrollDirection {
504 #[inline]
514 pub fn threshold(mut self, threshold: f32) -> Self {
515 assert!(threshold >= 0.0);
516 self.threshold = threshold;
517 self
518 }
519
520 pub const UP: Self = Self {
522 direction: DualAxisDirection::Up,
523 threshold: 0.0,
524 };
525
526 pub const DOWN: Self = Self {
528 direction: DualAxisDirection::Down,
529 threshold: 0.0,
530 };
531
532 pub const LEFT: Self = Self {
534 direction: DualAxisDirection::Left,
535 threshold: 0.0,
536 };
537
538 pub const RIGHT: Self = Self {
540 direction: DualAxisDirection::Right,
541 threshold: 0.0,
542 };
543}
544
545impl UserInput for MouseScrollDirection {
546 #[inline]
548 fn kind(&self) -> InputControlKind {
549 InputControlKind::Button
550 }
551
552 #[inline]
554 fn decompose(&self) -> BasicInputs {
555 BasicInputs::Simple(Box::new((*self).threshold(0.0)))
556 }
557}
558
559#[serde_typetag]
560impl Buttonlike for MouseScrollDirection {
561 #[inline]
563 fn get_pressed(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<bool> {
564 input_store
565 .pair(&MouseScroll::default())
566 .map(|movement| self.direction.is_active(movement, self.threshold))
567 }
568
569 fn press(&self, world: &mut World) {
575 let vec = self.direction.full_active_value();
576
577 world
578 .resource_mut::<Messages<MouseWheel>>()
579 .write(MouseWheel {
580 unit: bevy::input::mouse::MouseScrollUnit::Pixel,
581 x: vec.x,
582 y: vec.y,
583 window: Entity::PLACEHOLDER,
584 phase: TouchPhase::Moved,
585 });
586 }
587
588 fn release(&self, _world: &mut World) {}
593}
594
595impl Eq for MouseScrollDirection {}
596
597impl Hash for MouseScrollDirection {
598 fn hash<H: Hasher>(&self, state: &mut H) {
599 self.direction.hash(state);
600 FloatOrd(self.threshold).hash(state);
601 }
602}
603
604#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
640#[must_use]
641pub struct MouseScrollAxis {
642 pub axis: DualAxisType,
644
645 pub mouse_scroll_unit: MouseScrollUnit,
647
648 pub processors: Vec<AxisProcessor>,
650}
651
652impl MouseScrollAxis {
653 pub const X: Self = Self {
655 axis: DualAxisType::X,
656 processors: Vec::new(),
657 mouse_scroll_unit: MouseScrollUnit::Pixel,
658 };
659
660 pub const Y: Self = Self {
662 axis: DualAxisType::Y,
663 processors: Vec::new(),
664 mouse_scroll_unit: MouseScrollUnit::Pixel,
665 };
666}
667
668impl UserInput for MouseScrollAxis {
669 #[inline]
671 fn kind(&self) -> InputControlKind {
672 InputControlKind::Axis
673 }
674
675 #[inline]
677 fn decompose(&self) -> BasicInputs {
678 BasicInputs::Composite(vec![
679 Box::new(MouseScrollDirection {
680 direction: self.axis.negative(),
681 threshold: 0.0,
682 }),
683 Box::new(MouseScrollDirection {
684 direction: self.axis.positive(),
685 threshold: 0.0,
686 }),
687 ])
688 }
689}
690
691#[serde_typetag]
692impl Axislike for MouseScrollAxis {
693 #[inline]
696 fn get_value(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<f32> {
697 input_store.pair(&MouseScroll::default()).map(|movement| {
698 let value = self.axis.get_value(movement);
699
700 self.processors
701 .iter()
702 .fold(value, |value, processor| processor.process(value))
703 })
704 }
705
706 fn set_value(&self, world: &mut World, value: f32) {
712 let message = MouseWheel {
713 unit: self.mouse_scroll_unit,
714 x: if self.axis == DualAxisType::X {
715 value
716 } else {
717 0.0
718 },
719 y: if self.axis == DualAxisType::Y {
720 value
721 } else {
722 0.0
723 },
724 window: Entity::PLACEHOLDER,
725 phase: TouchPhase::Moved,
726 };
727 world.resource_mut::<Messages<MouseWheel>>().write(message);
728 }
729}
730
731impl WithAxisProcessingPipelineExt for MouseScrollAxis {
732 #[inline]
733 fn reset_processing_pipeline(mut self) -> Self {
734 self.processors.clear();
735 self
736 }
737
738 #[inline]
739 fn replace_processing_pipeline(
740 mut self,
741 processors: impl IntoIterator<Item = AxisProcessor>,
742 ) -> Self {
743 self.processors = processors.into_iter().collect();
744 self
745 }
746
747 #[inline]
748 fn with_processor(mut self, processor: impl Into<AxisProcessor>) -> Self {
749 self.processors.push(processor.into());
750 self
751 }
752}
753
754#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
789#[must_use]
790pub struct MouseScroll {
791 pub mouse_scroll_unit: MouseScrollUnit,
793
794 pub processors: Vec<DualAxisProcessor>,
796}
797
798impl Default for MouseScroll {
799 fn default() -> Self {
800 Self {
801 mouse_scroll_unit: MouseScrollUnit::Pixel,
802 processors: Vec::new(),
803 }
804 }
805}
806
807impl UpdatableInput for MouseScroll {
808 type SourceData = SRes<AccumulatedMouseScroll>;
809
810 fn compute(
811 mut central_input_store: ResMut<CentralInputStore>,
812 source_data: StaticSystemParam<Self::SourceData>,
813 ) {
814 central_input_store.update_dualaxislike(Self::default(), source_data.delta);
815 }
816}
817
818impl UserInput for MouseScroll {
819 #[inline]
821 fn kind(&self) -> InputControlKind {
822 InputControlKind::DualAxis
823 }
824
825 #[inline]
827 fn decompose(&self) -> BasicInputs {
828 BasicInputs::Composite(vec![
829 Box::new(MouseScrollDirection::UP),
830 Box::new(MouseScrollDirection::DOWN),
831 Box::new(MouseScrollDirection::LEFT),
832 Box::new(MouseScrollDirection::RIGHT),
833 ])
834 }
835}
836
837#[serde_typetag]
838impl DualAxislike for MouseScroll {
839 #[inline]
841 fn get_axis_pair(&self, input_store: &CentralInputStore, _gamepad: Entity) -> Option<Vec2> {
842 input_store.pair(&MouseScroll::default()).map(|movement| {
843 self.processors
844 .iter()
845 .fold(movement, |value, processor| processor.process(value))
846 })
847 }
848
849 fn set_axis_pair(&self, world: &mut World, value: Vec2) {
854 world
855 .resource_mut::<Messages<MouseWheel>>()
856 .write(MouseWheel {
857 unit: self.mouse_scroll_unit,
858 x: value.x,
859 y: value.y,
860 window: Entity::PLACEHOLDER,
861 phase: TouchPhase::Moved,
862 });
863 }
864}
865
866impl WithDualAxisProcessingPipelineExt for MouseScroll {
867 #[inline]
868 fn reset_processing_pipeline(mut self) -> Self {
869 self.processors.clear();
870 self
871 }
872
873 #[inline]
874 fn replace_processing_pipeline(
875 mut self,
876 processors: impl IntoIterator<Item = DualAxisProcessor>,
877 ) -> Self {
878 self.processors = processors.into_iter().collect();
879 self
880 }
881
882 #[inline]
883 fn with_processor(mut self, processor: impl Into<DualAxisProcessor>) -> Self {
884 self.processors.push(processor.into());
885 self
886 }
887}
888
889#[cfg(test)]
890mod tests {
891 use super::*;
892 use crate::plugin::CentralInputStorePlugin;
893 use bevy::input::InputPlugin;
894 use bevy::prelude::*;
895
896 fn test_app() -> App {
897 let mut app = App::new();
898 app.add_plugins(InputPlugin)
899 .add_plugins(CentralInputStorePlugin);
900 app
901 }
902
903 #[test]
904 fn test_mouse_button() {
905 let left = MouseButton::Left;
906 assert_eq!(left.kind(), InputControlKind::Button);
907
908 let middle = MouseButton::Middle;
909 assert_eq!(middle.kind(), InputControlKind::Button);
910
911 let right = MouseButton::Right;
912 assert_eq!(right.kind(), InputControlKind::Button);
913
914 let mut app = test_app();
916 app.update();
917 let gamepad = app.world_mut().spawn(()).id();
918 let inputs = app.world().resource::<CentralInputStore>();
919
920 assert!(!left.pressed(inputs, gamepad));
921 assert!(!middle.pressed(inputs, gamepad));
922 assert!(!right.pressed(inputs, gamepad));
923
924 let mut app = test_app();
926 MouseButton::Left.press(app.world_mut());
927 app.update();
928 let inputs = app.world().resource::<CentralInputStore>();
929
930 assert!(left.pressed(inputs, gamepad));
931 assert!(!middle.pressed(inputs, gamepad));
932 assert!(!right.pressed(inputs, gamepad));
933
934 let mut app = test_app();
936 MouseButton::Middle.press(app.world_mut());
937 app.update();
938 let inputs = app.world().resource::<CentralInputStore>();
939
940 assert!(!left.pressed(inputs, gamepad));
941 assert!(middle.pressed(inputs, gamepad));
942 assert!(!right.pressed(inputs, gamepad));
943
944 let mut app = test_app();
946 MouseButton::Right.press(app.world_mut());
947 app.update();
948 let inputs = app.world().resource::<CentralInputStore>();
949
950 assert!(!left.pressed(inputs, gamepad));
951 assert!(!middle.pressed(inputs, gamepad));
952 assert!(right.pressed(inputs, gamepad));
953 }
954
955 #[test]
956 fn test_user_input_mouse_button_kind() {
957 let mouse_button = MouseButton::Left;
958 assert_eq!(mouse_button.kind(), InputControlKind::Button);
959 }
960
961 #[test]
962 fn test_mouse_button_set_value_press() {
963 let mut app = test_app();
964 let mouse_button = MouseButton::Left;
965 mouse_button.set_value(app.world_mut(), 1.0);
966
967 let mut mouse_motion_messages = app
968 .world_mut()
969 .get_resource_mut::<Messages<MouseButtonInput>>()
970 .unwrap();
971
972 assert_eq!(mouse_motion_messages.len(), 1);
973 assert_eq!(
974 mouse_motion_messages.drain().next().unwrap(),
975 MouseButtonInput {
976 button: MouseButton::Left,
977 state: ButtonState::Pressed,
978 window: Entity::PLACEHOLDER,
979 }
980 );
981 }
982
983 #[test]
984 fn test_mouse_button_set_value_release() {
985 let mut app = test_app();
986 let mouse_button = MouseButton::Left;
987 mouse_button.set_value(app.world_mut(), 0.0);
988
989 let mut mouse_motion_messages = app
990 .world_mut()
991 .get_resource_mut::<Messages<MouseButtonInput>>()
992 .unwrap();
993
994 assert_eq!(mouse_motion_messages.len(), 1);
995 assert_eq!(
996 mouse_motion_messages.drain().next().unwrap(),
997 MouseButtonInput {
998 button: MouseButton::Left,
999 state: ButtonState::Released,
1000 window: Entity::PLACEHOLDER,
1001 }
1002 );
1003 }
1004
1005 #[test]
1006 fn test_mouse_move() {
1007 let mouse_move_up = MouseMoveDirection::UP;
1008 assert_eq!(mouse_move_up.kind(), InputControlKind::Button);
1009
1010 let mouse_move_y = MouseMoveAxis::Y;
1011 assert_eq!(mouse_move_y.kind(), InputControlKind::Axis);
1012
1013 let mouse_move = MouseMove::default();
1014 assert_eq!(mouse_move.kind(), InputControlKind::DualAxis);
1015
1016 let mut app = test_app();
1018 app.update();
1019 let gamepad = app.world_mut().spawn(()).id();
1020 let inputs = app.world().resource::<CentralInputStore>();
1021
1022 assert!(!mouse_move_up.pressed(inputs, gamepad));
1023 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
1024 assert_eq!(mouse_move.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
1025
1026 let data = Vec2::new(-1.0, 0.0);
1028 let mut app = test_app();
1029 MouseMoveDirection::LEFT.press(app.world_mut());
1030 app.update();
1031 let inputs = app.world().resource::<CentralInputStore>();
1032
1033 assert!(!mouse_move_up.pressed(inputs, gamepad));
1034 assert_eq!(mouse_move_y.value(inputs, gamepad), 0.0);
1035 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
1036
1037 let data = Vec2::new(0.0, 1.0);
1039 let mut app = test_app();
1040 MouseMoveDirection::UP.press(app.world_mut());
1041 app.update();
1042 let inputs = app.world().resource::<CentralInputStore>();
1043
1044 assert!(mouse_move_up.pressed(inputs, gamepad));
1045 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
1046 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
1047
1048 let data = Vec2::new(0.0, -1.0);
1050 let mut app = test_app();
1051 MouseMoveDirection::DOWN.press(app.world_mut());
1052 app.update();
1053 let inputs = app.world().resource::<CentralInputStore>();
1054
1055 assert!(!mouse_move_up.pressed(inputs, gamepad));
1056 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
1057 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
1058
1059 let data = Vec2::new(0.0, 3.0);
1061 let mut app = test_app();
1062 MouseMoveAxis::Y.set_value(app.world_mut(), data.y);
1063 app.update();
1064 let inputs = app.world().resource::<CentralInputStore>();
1065
1066 assert!(mouse_move_up.pressed(inputs, gamepad));
1067 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
1068 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
1069
1070 let data = Vec2::new(2.0, 3.0);
1072 let mut app = test_app();
1073 MouseMove::default().set_axis_pair(app.world_mut(), data);
1074 app.update();
1075 let inputs = app.world().resource::<CentralInputStore>();
1076
1077 assert!(mouse_move_up.pressed(inputs, gamepad));
1078 assert_eq!(mouse_move_y.value(inputs, gamepad), data.y);
1079 assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
1080 }
1081
1082 #[test]
1083 fn test_mouse_move_direction_set_valid_threshold() {
1084 let mouse_move_direction = MouseMoveDirection::UP.threshold(0.5);
1085
1086 assert_eq!(mouse_move_direction.threshold, 0.5);
1087 }
1088
1089 #[test]
1090 #[should_panic = "assertion failed: threshold >= 0.0"]
1091 fn test_mouse_move_direction_set_invalid_threshold() {
1092 let _ = MouseMoveDirection::UP.threshold(-0.5);
1093 }
1094
1095 #[test]
1096 fn test_mouse_move_direction_kind() {
1097 let mouse_move_direction = MouseMoveDirection::UP;
1098
1099 assert_eq!(mouse_move_direction.kind(), InputControlKind::Button);
1100 }
1101
1102 #[test]
1103 fn test_mouse_move_axis_processing_pipeline() {
1104 let mouse_move_axis = MouseMoveAxis::X.with_processor(AxisProcessor::Inverted);
1105
1106 assert_eq!(mouse_move_axis.processors.len(), 1);
1107 assert_eq!(
1108 mouse_move_axis.processors.first().unwrap(),
1109 &AxisProcessor::Inverted
1110 );
1111
1112 let mouse_move_axis =
1113 mouse_move_axis.replace_processing_pipeline(vec![AxisProcessor::Digital]);
1114
1115 assert_eq!(mouse_move_axis.processors.len(), 1);
1116 assert_eq!(
1117 mouse_move_axis.processors.first().unwrap(),
1118 &AxisProcessor::Digital
1119 );
1120
1121 let mouse_move_axis = mouse_move_axis.reset_processing_pipeline();
1122
1123 assert_eq!(mouse_move_axis.processors.len(), 0);
1124 }
1125
1126 #[test]
1127 fn test_mouse_move_processing_pipeline() {
1128 let mouse_move =
1129 MouseMove::default().with_processor(DualAxisProcessor::Inverted(DualAxisInverted::ALL));
1130
1131 assert_eq!(mouse_move.processors.len(), 1);
1132 assert_eq!(
1133 mouse_move.processors.first().unwrap(),
1134 &DualAxisProcessor::Inverted(DualAxisInverted::ALL),
1135 );
1136
1137 let mouse_move = mouse_move.replace_processing_pipeline(vec![DualAxisProcessor::Digital]);
1138
1139 assert_eq!(mouse_move.processors.len(), 1);
1140 assert_eq!(
1141 mouse_move.processors.first().unwrap(),
1142 &DualAxisProcessor::Digital
1143 );
1144
1145 let mouse_move = mouse_move.reset_processing_pipeline();
1146
1147 assert_eq!(mouse_move.processors.len(), 0);
1148 }
1149
1150 #[test]
1151 fn test_mouse_scroll_processing_pipeline() {
1152 let mouse_scroll = MouseScroll::default()
1153 .with_processor(DualAxisProcessor::Inverted(DualAxisInverted::ALL));
1154
1155 assert_eq!(mouse_scroll.processors.len(), 1);
1156 assert_eq!(
1157 mouse_scroll.processors.first().unwrap(),
1158 &DualAxisProcessor::Inverted(DualAxisInverted::ALL),
1159 );
1160
1161 let mouse_scroll =
1162 mouse_scroll.replace_processing_pipeline(vec![DualAxisProcessor::Digital]);
1163
1164 assert_eq!(mouse_scroll.processors.len(), 1);
1165 assert_eq!(
1166 mouse_scroll.processors.first().unwrap(),
1167 &DualAxisProcessor::Digital
1168 );
1169
1170 let mouse_scroll = mouse_scroll.reset_processing_pipeline();
1171
1172 assert_eq!(mouse_scroll.processors.len(), 0);
1173 }
1174
1175 #[test]
1176 fn test_mouse_scroll_axis_processing_pipeline() {
1177 let mouse_scroll_axis = MouseScrollAxis::X.with_processor(AxisProcessor::Inverted);
1178
1179 assert_eq!(mouse_scroll_axis.processors.len(), 1);
1180 assert_eq!(
1181 mouse_scroll_axis.processors.first().unwrap(),
1182 &AxisProcessor::Inverted,
1183 );
1184
1185 let mouse_scroll_axis =
1186 mouse_scroll_axis.replace_processing_pipeline(vec![AxisProcessor::Digital]);
1187
1188 assert_eq!(mouse_scroll_axis.processors.len(), 1);
1189 assert_eq!(
1190 mouse_scroll_axis.processors.first().unwrap(),
1191 &AxisProcessor::Digital
1192 );
1193
1194 let mouse_scroll_axis = mouse_scroll_axis.reset_processing_pipeline();
1195
1196 assert_eq!(mouse_scroll_axis.processors.len(), 0);
1197 }
1198
1199 #[test]
1200 fn test_mouse_scroll() {
1201 let mouse_scroll_up = MouseScrollDirection::UP;
1202 assert_eq!(mouse_scroll_up.kind(), InputControlKind::Button);
1203
1204 let mouse_scroll_y = MouseScrollAxis::Y;
1205 assert_eq!(mouse_scroll_y.kind(), InputControlKind::Axis);
1206 let mouse_scroll = MouseScroll::default();
1207 assert_eq!(mouse_scroll.kind(), InputControlKind::DualAxis);
1208
1209 let mut app = test_app();
1211 app.update();
1212 let gamepad = app.world_mut().spawn(()).id();
1213 let inputs = app.world().resource::<CentralInputStore>();
1214
1215 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1216 assert_eq!(mouse_scroll_y.value(inputs, gamepad), 0.0);
1217 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), Vec2::new(0.0, 0.0));
1218
1219 let data = Vec2::new(0.0, 1.0);
1221 let mut app = test_app();
1222 MouseScrollDirection::UP.press(app.world_mut());
1223 app.update();
1224 let inputs = app.world().resource::<CentralInputStore>();
1225
1226 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1227 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1228 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1229
1230 let data = Vec2::new(0.0, -1.0);
1232 let mut app = test_app();
1233 MouseScrollDirection::DOWN.press(app.world_mut());
1234 app.update();
1235 let inputs = app.world().resource::<CentralInputStore>();
1236
1237 assert!(!mouse_scroll_up.pressed(inputs, gamepad));
1238 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1239 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1240
1241 let data = Vec2::new(0.0, 3.0);
1243 let mut app = test_app();
1244 MouseScrollAxis::Y.set_value(app.world_mut(), data.y);
1245 app.update();
1246 let inputs = app.world().resource::<CentralInputStore>();
1247
1248 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1249 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1250 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1251
1252 let data = Vec2::new(2.0, 3.0);
1254 let mut app = test_app();
1255 MouseScroll::default().set_axis_pair(app.world_mut(), data);
1256 app.update();
1257 let inputs = app.world().resource::<CentralInputStore>();
1258
1259 assert!(mouse_scroll_up.pressed(inputs, gamepad));
1260 assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
1261 assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
1262 }
1263
1264 #[test]
1265 fn test_mouse_scroll_direction_set_valid_threshold() {
1266 let mouse_scroll_direction = MouseScrollDirection::UP.threshold(0.5);
1267
1268 assert_eq!(mouse_scroll_direction.threshold, 0.5);
1269 }
1270
1271 #[test]
1272 #[should_panic = "assertion failed: threshold >= 0.0"]
1273 fn test_mouse_scroll_direction_set_invalid_threshold() {
1274 let _ = MouseScrollDirection::UP.threshold(-0.5);
1275 }
1276
1277 #[test]
1278 fn one_frame_accumulate_mouse_movement() {
1279 let mut app = test_app();
1280 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1281 MouseMoveAxis::Y.set_value(app.world_mut(), 2.0);
1282
1283 let mouse_motion_messages = app.world().get_resource::<Messages<MouseMotion>>().unwrap();
1284 for message in mouse_motion_messages.iter_current_update_messages() {
1285 dbg!("Message sent: {:?}", message);
1286 }
1287
1288 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1290 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 0.0));
1291
1292 app.update();
1293
1294 let accumulated_mouse_movement = app.world().resource::<AccumulatedMouseMotion>();
1296 assert_eq!(accumulated_mouse_movement.delta, Vec2::new(0.0, 5.0));
1297
1298 let inputs = app.world().resource::<CentralInputStore>();
1299 assert_eq!(
1300 inputs.pair(&MouseMove::default()).unwrap(),
1301 Vec2::new(0.0, 5.0)
1302 );
1303 }
1304
1305 #[test]
1306 fn multiple_frames_accumulate_mouse_movement() {
1307 let mut app = test_app();
1308 let inputs = app.world().resource::<CentralInputStore>();
1309 assert_eq!(
1311 inputs.pair(&MouseMove::default()),
1312 None,
1313 "Initial movement is not None."
1314 );
1315
1316 MouseMoveAxis::Y.set_value(app.world_mut(), 3.0);
1318 app.update();
1320
1321 let inputs = app.world().resource::<CentralInputStore>();
1322 assert_eq!(
1324 inputs.pair(&MouseMove::default()).unwrap(),
1325 Vec2::new(0.0, 3.0),
1326 "Movement sent was not read correctly."
1327 );
1328
1329 app.update();
1331 let inputs = app.world().resource::<CentralInputStore>();
1332 assert_eq!(
1334 inputs.pair(&MouseMove::default()).unwrap(),
1335 Vec2::ZERO,
1336 "No movement was expected. Is the position in the message stream being cleared properly?"
1337 );
1338 }
1339}