Skip to content

Commit 63b65e8

Browse files
authored
add MouseScrollUnit to MouseWheel controls (#738)
1 parent ff2cbfc commit 63b65e8

File tree

3 files changed

+225
-9
lines changed

3 files changed

+225
-9
lines changed

RELEASES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
### Bugs (0.21.0)
66

77
- `ActionDiff`s generated will no longer include entries from disabled `ActionState`s or disabled `Action`s
8-
8+
- `MouseScroll` and `MouseScrollAxis` now support specifying `MouseScrollUnit::Pixel` or `MouseScrollUnit::Line`
9+
910
## Version 0.20.0
1011

1112
### Dependencies (0.20.0)

src/user_input/mouse.rs

Lines changed: 216 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use bevy::ecs::system::StaticSystemParam;
1111
use bevy::ecs::system::lifetimeless::SRes;
1212
use bevy::input::mouse::{
1313
AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
14-
MouseWheel,
14+
MouseScrollUnit, MouseWheel,
1515
};
1616
use bevy::input::{ButtonInput, ButtonState};
1717
use bevy::math::FloatOrd;
@@ -609,6 +609,7 @@ impl Hash for MouseScrollDirection {
609609
/// ```rust
610610
/// use bevy::prelude::*;
611611
/// use bevy::input::InputPlugin;
612+
/// use bevy::input::mouse::MouseScrollUnit;
612613
/// use leafwing_input_manager::plugin::CentralInputStorePlugin;
613614
/// use leafwing_input_manager::prelude::*;
614615
/// use leafwing_input_manager::user_input::testing_utils::FetchUserInput;
@@ -617,7 +618,12 @@ impl Hash for MouseScrollDirection {
617618
/// app.add_plugins((InputPlugin, CentralInputStorePlugin));
618619
///
619620
/// // Y-axis movement
620-
/// let input = MouseScrollAxis::Y;
621+
/// let mut input = MouseScrollAxis::Y;
622+
///
623+
/// // You can configure which [`MouseScrollUnit`] this Axis uses.
624+
/// // The default is [`MouseScrollUnit::Pixel`]
625+
/// input.mouse_scroll_unit = MouseScrollUnit::Line;
626+
/// assert_eq!(input.mouse_scroll_unit, MouseScrollUnit::Line);
621627
///
622628
/// // Scrolling on the chosen axis activates the input
623629
/// MouseScrollAxis::Y.set_value(app.world_mut(), 1.0);
@@ -634,6 +640,9 @@ pub struct MouseScrollAxis {
634640
/// The axis that this input tracks.
635641
pub axis: DualAxisType,
636642

643+
/// The [`MouseScrollUnit`] this axis uses
644+
pub mouse_scroll_unit: MouseScrollUnit,
645+
637646
/// A processing pipeline that handles input values.
638647
pub processors: Vec<AxisProcessor>,
639648
}
@@ -643,12 +652,14 @@ impl MouseScrollAxis {
643652
pub const X: Self = Self {
644653
axis: DualAxisType::X,
645654
processors: Vec::new(),
655+
mouse_scroll_unit: MouseScrollUnit::Pixel,
646656
};
647657

648658
/// Vertical scrolling of the mouse wheel. No processing is applied to raw data from the mouse.
649659
pub const Y: Self = Self {
650660
axis: DualAxisType::Y,
651661
processors: Vec::new(),
662+
mouse_scroll_unit: MouseScrollUnit::Pixel,
652663
};
653664
}
654665

@@ -697,7 +708,7 @@ impl Axislike for MouseScrollAxis {
697708
/// The `window` field will be filled with a placeholder value.
698709
fn set_value(&self, world: &mut World, value: f32) {
699710
let message = MouseWheel {
700-
unit: bevy::input::mouse::MouseScrollUnit::Pixel,
711+
unit: self.mouse_scroll_unit,
701712
x: if self.axis == DualAxisType::X {
702713
value
703714
} else {
@@ -747,14 +758,20 @@ impl WithAxisProcessingPipelineExt for MouseScrollAxis {
747758
/// ```rust
748759
/// use bevy::prelude::*;
749760
/// use bevy::input::InputPlugin;
761+
/// use bevy::input::mouse::MouseScrollUnit;
750762
/// use leafwing_input_manager::plugin::CentralInputStorePlugin;
751763
/// use leafwing_input_manager::prelude::*;
752764
/// use leafwing_input_manager::user_input::testing_utils::FetchUserInput;
753765
///
754766
/// let mut app = App::new();
755767
/// app.add_plugins((InputPlugin, CentralInputStorePlugin));
756768
///
757-
/// let input = MouseScroll::default();
769+
/// let mut input = MouseScroll::default();
770+
///
771+
/// // You can configure which [`MouseScrollUnit`] this Axis uses.
772+
/// // The default is [`MouseScrollUnit::Pixel`]
773+
/// input.mouse_scroll_unit = MouseScrollUnit::Line;
774+
/// assert_eq!(input.mouse_scroll_unit, MouseScrollUnit::Line);
758775
///
759776
/// // Scrolling on either axis activates the input
760777
/// MouseScrollAxis::Y.set_value(app.world_mut(), 3.0);
@@ -765,13 +782,25 @@ impl WithAxisProcessingPipelineExt for MouseScrollAxis {
765782
/// let doubled = MouseScroll::default().sensitivity_y(2.0);
766783
/// assert_eq!(app.read_dual_axis_values(doubled), Vec2::new(0.0, 6.0));
767784
/// ```
768-
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
785+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
769786
#[must_use]
770787
pub struct MouseScroll {
788+
/// The [`MouseScrollUnit`] this axis uses
789+
pub mouse_scroll_unit: MouseScrollUnit,
790+
771791
/// A processing pipeline that handles input values.
772792
pub processors: Vec<DualAxisProcessor>,
773793
}
774794

795+
impl Default for MouseScroll {
796+
fn default() -> Self {
797+
Self {
798+
mouse_scroll_unit: MouseScrollUnit::Pixel,
799+
processors: Vec::new(),
800+
}
801+
}
802+
}
803+
775804
impl UpdatableInput for MouseScroll {
776805
type SourceData = SRes<AccumulatedMouseScroll>;
777806

@@ -822,7 +851,7 @@ impl DualAxislike for MouseScroll {
822851
world
823852
.resource_mut::<Messages<MouseWheel>>()
824853
.write(MouseWheel {
825-
unit: bevy::input::mouse::MouseScrollUnit::Pixel,
854+
unit: self.mouse_scroll_unit,
826855
x: value.x,
827856
y: value.y,
828857
window: Entity::PLACEHOLDER,
@@ -919,6 +948,56 @@ mod tests {
919948
assert!(right.pressed(inputs, gamepad));
920949
}
921950

951+
#[test]
952+
fn test_user_input_mouse_button_kind() {
953+
let mouse_button = MouseButton::Left;
954+
assert_eq!(mouse_button.kind(), InputControlKind::Button);
955+
}
956+
957+
#[test]
958+
fn test_mouse_button_set_value_press() {
959+
let mut app = test_app();
960+
let mouse_button = MouseButton::Left;
961+
mouse_button.set_value(app.world_mut(), 1.0);
962+
963+
let mut mouse_motion_messages = app
964+
.world_mut()
965+
.get_resource_mut::<Messages<MouseButtonInput>>()
966+
.unwrap();
967+
968+
assert_eq!(mouse_motion_messages.len(), 1);
969+
assert_eq!(
970+
mouse_motion_messages.drain().next().unwrap(),
971+
MouseButtonInput {
972+
button: MouseButton::Left,
973+
state: ButtonState::Pressed,
974+
window: Entity::PLACEHOLDER,
975+
}
976+
);
977+
}
978+
979+
#[test]
980+
fn test_mouse_button_set_value_release() {
981+
let mut app = test_app();
982+
let mouse_button = MouseButton::Left;
983+
mouse_button.set_value(app.world_mut(), 0.0);
984+
985+
let mut mouse_motion_messages = app
986+
.world_mut()
987+
.get_resource_mut::<Messages<MouseButtonInput>>()
988+
.unwrap();
989+
990+
assert_eq!(mouse_motion_messages.len(), 1);
991+
assert_eq!(
992+
mouse_motion_messages.drain().next().unwrap(),
993+
MouseButtonInput {
994+
button: MouseButton::Left,
995+
state: ButtonState::Released,
996+
window: Entity::PLACEHOLDER,
997+
}
998+
);
999+
}
1000+
9221001
#[test]
9231002
fn test_mouse_move() {
9241003
let mouse_move_up = MouseMoveDirection::UP;
@@ -996,6 +1075,123 @@ mod tests {
9961075
assert_eq!(mouse_move.axis_pair(inputs, gamepad), data);
9971076
}
9981077

1078+
#[test]
1079+
fn test_mouse_move_direction_set_valid_threshold() {
1080+
let mouse_move_direction = MouseMoveDirection::UP.threshold(0.5);
1081+
1082+
assert_eq!(mouse_move_direction.threshold, 0.5);
1083+
}
1084+
1085+
#[test]
1086+
#[should_panic = "assertion failed: threshold >= 0.0"]
1087+
fn test_mouse_move_direction_set_invalid_threshold() {
1088+
let _ = MouseMoveDirection::UP.threshold(-0.5);
1089+
}
1090+
1091+
#[test]
1092+
fn test_mouse_move_direction_kind() {
1093+
let mouse_move_direction = MouseMoveDirection::UP;
1094+
1095+
assert_eq!(mouse_move_direction.kind(), InputControlKind::Button);
1096+
}
1097+
1098+
#[test]
1099+
fn test_mouse_move_axis_processing_pipeline() {
1100+
let mouse_move_axis = MouseMoveAxis::X.with_processor(AxisProcessor::Inverted);
1101+
1102+
assert_eq!(mouse_move_axis.processors.len(), 1);
1103+
assert_eq!(
1104+
mouse_move_axis.processors.first().unwrap(),
1105+
&AxisProcessor::Inverted
1106+
);
1107+
1108+
let mouse_move_axis =
1109+
mouse_move_axis.replace_processing_pipeline(vec![AxisProcessor::Digital]);
1110+
1111+
assert_eq!(mouse_move_axis.processors.len(), 1);
1112+
assert_eq!(
1113+
mouse_move_axis.processors.first().unwrap(),
1114+
&AxisProcessor::Digital
1115+
);
1116+
1117+
let mouse_move_axis = mouse_move_axis.reset_processing_pipeline();
1118+
1119+
assert_eq!(mouse_move_axis.processors.len(), 0);
1120+
}
1121+
1122+
#[test]
1123+
fn test_mouse_move_processing_pipeline() {
1124+
let mouse_move =
1125+
MouseMove::default().with_processor(DualAxisProcessor::Inverted(DualAxisInverted::ALL));
1126+
1127+
assert_eq!(mouse_move.processors.len(), 1);
1128+
assert_eq!(
1129+
mouse_move.processors.first().unwrap(),
1130+
&DualAxisProcessor::Inverted(DualAxisInverted::ALL),
1131+
);
1132+
1133+
let mouse_move = mouse_move.replace_processing_pipeline(vec![DualAxisProcessor::Digital]);
1134+
1135+
assert_eq!(mouse_move.processors.len(), 1);
1136+
assert_eq!(
1137+
mouse_move.processors.first().unwrap(),
1138+
&DualAxisProcessor::Digital
1139+
);
1140+
1141+
let mouse_move = mouse_move.reset_processing_pipeline();
1142+
1143+
assert_eq!(mouse_move.processors.len(), 0);
1144+
}
1145+
1146+
#[test]
1147+
fn test_mouse_scroll_processing_pipeline() {
1148+
let mouse_scroll = MouseScroll::default()
1149+
.with_processor(DualAxisProcessor::Inverted(DualAxisInverted::ALL));
1150+
1151+
assert_eq!(mouse_scroll.processors.len(), 1);
1152+
assert_eq!(
1153+
mouse_scroll.processors.first().unwrap(),
1154+
&DualAxisProcessor::Inverted(DualAxisInverted::ALL),
1155+
);
1156+
1157+
let mouse_scroll =
1158+
mouse_scroll.replace_processing_pipeline(vec![DualAxisProcessor::Digital]);
1159+
1160+
assert_eq!(mouse_scroll.processors.len(), 1);
1161+
assert_eq!(
1162+
mouse_scroll.processors.first().unwrap(),
1163+
&DualAxisProcessor::Digital
1164+
);
1165+
1166+
let mouse_scroll = mouse_scroll.reset_processing_pipeline();
1167+
1168+
assert_eq!(mouse_scroll.processors.len(), 0);
1169+
}
1170+
1171+
#[test]
1172+
fn test_mouse_scroll_axis_processing_pipeline() {
1173+
let mouse_scroll_axis = MouseScrollAxis::X.with_processor(AxisProcessor::Inverted);
1174+
1175+
assert_eq!(mouse_scroll_axis.processors.len(), 1);
1176+
assert_eq!(
1177+
mouse_scroll_axis.processors.first().unwrap(),
1178+
&AxisProcessor::Inverted,
1179+
);
1180+
1181+
let mouse_scroll_axis =
1182+
mouse_scroll_axis.replace_processing_pipeline(vec![AxisProcessor::Digital]);
1183+
1184+
assert_eq!(mouse_scroll_axis.processors.len(), 1);
1185+
assert_eq!(
1186+
mouse_scroll_axis.processors.first().unwrap(),
1187+
&AxisProcessor::Digital
1188+
);
1189+
1190+
let mouse_scroll_axis = mouse_scroll_axis.reset_processing_pipeline();
1191+
1192+
assert_eq!(mouse_scroll_axis.processors.len(), 0);
1193+
}
1194+
9991195
#[test]
10001196
fn test_mouse_scroll() {
10011197
let mouse_scroll_up = MouseScrollDirection::UP;
@@ -1049,7 +1245,7 @@ mod tests {
10491245
assert_eq!(mouse_scroll_y.value(inputs, gamepad), data.y);
10501246
assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
10511247

1052-
// Set changes in scrolling to (2.0, 3.0)
1248+
// Set changes in scrolling `to (2.0, 3.0)
10531249
let data = Vec2::new(2.0, 3.0);
10541250
let mut app = test_app();
10551251
MouseScroll::default().set_axis_pair(app.world_mut(), data);
@@ -1061,6 +1257,19 @@ mod tests {
10611257
assert_eq!(mouse_scroll.axis_pair(inputs, gamepad), data);
10621258
}
10631259

1260+
#[test]
1261+
fn test_mouse_scroll_direction_set_valid_threshold() {
1262+
let mouse_scroll_direction = MouseScrollDirection::UP.threshold(0.5);
1263+
1264+
assert_eq!(mouse_scroll_direction.threshold, 0.5);
1265+
}
1266+
1267+
#[test]
1268+
#[should_panic = "assertion failed: threshold >= 0.0"]
1269+
fn test_mouse_scroll_direction_set_invalid_threshold() {
1270+
let _ = MouseScrollDirection::UP.threshold(-0.5);
1271+
}
1272+
10641273
#[test]
10651274
fn one_frame_accumulate_mouse_movement() {
10661275
let mut app = test_app();

src/user_input/trait_serde.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,20 @@ mod tests {
297297
Token::BorrowedStr("MouseScrollAxis"),
298298
Token::Struct {
299299
name: "MouseScrollAxis",
300-
len: 2,
300+
len: 3,
301301
},
302302
Token::BorrowedStr("axis"),
303303
Token::Enum {
304304
name: "DualAxisType",
305305
},
306306
Token::Str("Y"),
307307
Token::Unit,
308+
Token::BorrowedStr("mouse_scroll_unit"),
309+
Token::Enum {
310+
name: "MouseScrollUnit",
311+
},
312+
Token::Str("Pixel"),
313+
Token::Unit,
308314
Token::BorrowedStr("processors"),
309315
Token::Seq { len: Some(0) },
310316
Token::SeqEnd,

0 commit comments

Comments
 (0)