Skip to main content

bevy_input/
mouse.rs

1//! The mouse input functionality.
2
3use crate::{touch::TouchPhase, ButtonInput, ButtonState};
4#[cfg(feature = "bevy_reflect")]
5use bevy_ecs::prelude::ReflectMessage;
6use bevy_ecs::{
7    change_detection::DetectChangesMut,
8    entity::Entity,
9    message::{Message, MessageReader},
10    resource::Resource,
11    system::ResMut,
12};
13use bevy_math::Vec2;
14#[cfg(feature = "bevy_reflect")]
15use {
16    bevy_ecs::reflect::ReflectResource,
17    bevy_reflect::{std_traits::ReflectDefault, Reflect},
18};
19
20#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
21use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
22
23/// A mouse button input event.
24///
25/// This event is the translated version of the `WindowEvent::MouseInput` from the `winit` crate.
26///
27/// ## Usage
28///
29/// The event is read inside of the [`mouse_button_input_system`]
30/// to update the [`ButtonInput<MouseButton>`] resource.
31#[derive(Message, Debug, Clone, Copy, PartialEq, Eq)]
32#[cfg_attr(
33    feature = "bevy_reflect",
34    derive(Reflect),
35    reflect(Debug, PartialEq, Clone, Message)
36)]
37#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
38#[cfg_attr(
39    all(feature = "serialize", feature = "bevy_reflect"),
40    reflect(Serialize, Deserialize)
41)]
42pub struct MouseButtonInput {
43    /// The mouse button assigned to the event.
44    pub button: MouseButton,
45    /// The pressed state of the button.
46    pub state: ButtonState,
47    /// Window that received the input.
48    pub window: Entity,
49}
50
51/// A button on a mouse device.
52///
53/// ## Usage
54///
55/// It is used as the generic `T` value of an [`ButtonInput`] to create a `bevy`
56/// resource.
57///
58/// ## Updating
59///
60/// The resource is updated inside of the [`mouse_button_input_system`].
61#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
62#[cfg_attr(
63    feature = "bevy_reflect",
64    derive(Reflect),
65    reflect(Debug, Hash, PartialEq, Clone)
66)]
67#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
68#[cfg_attr(
69    all(feature = "serialize", feature = "bevy_reflect"),
70    reflect(Serialize, Deserialize)
71)]
72pub enum MouseButton {
73    /// The left mouse button.
74    Left,
75    /// The right mouse button.
76    Right,
77    /// The middle mouse button.
78    Middle,
79    /// The back mouse button.
80    Back,
81    /// The forward mouse button.
82    Forward,
83    /// Another mouse button with the associated number.
84    Other(u16),
85}
86
87/// An event reporting the change in physical position of a pointing device.
88///
89/// This represents raw, unfiltered physical motion.
90/// It is the translated version of [`DeviceEvent::MouseMotion`] from the `winit` crate.
91///
92/// All pointing devices connected to a single machine at the same time can emit the event independently.
93/// However, the event data does not make it possible to distinguish which device it is referring to.
94///
95/// [`DeviceEvent::MouseMotion`]: https://docs.rs/winit/latest/winit/event/enum.DeviceEvent.html#variant.MouseMotion
96#[derive(Message, Debug, Clone, Copy, PartialEq)]
97#[cfg_attr(
98    feature = "bevy_reflect",
99    derive(Reflect),
100    reflect(Debug, PartialEq, Clone, Message)
101)]
102#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
103#[cfg_attr(
104    all(feature = "serialize", feature = "bevy_reflect"),
105    reflect(Serialize, Deserialize)
106)]
107pub struct MouseMotion {
108    /// The change in the position of the pointing device since the last event was sent.
109    pub delta: Vec2,
110}
111
112/// The scroll unit.
113///
114/// Describes how a value of a [`MouseWheel`] event has to be interpreted.
115///
116/// The value of the event can either be interpreted as the amount of lines or the amount of pixels
117/// to scroll.
118#[derive(Debug, Hash, Clone, Copy, Eq, PartialEq)]
119#[cfg_attr(
120    feature = "bevy_reflect",
121    derive(Reflect),
122    reflect(Debug, PartialEq, Clone)
123)]
124#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
125#[cfg_attr(
126    all(feature = "serialize", feature = "bevy_reflect"),
127    reflect(Serialize, Deserialize)
128)]
129pub enum MouseScrollUnit {
130    /// The line scroll unit.
131    ///
132    /// The delta of the associated [`MouseWheel`] event corresponds
133    /// to the amount of lines or rows to scroll.
134    Line,
135    /// The pixel scroll unit.
136    ///
137    /// The delta of the associated [`MouseWheel`] event corresponds
138    /// to the amount of pixels to scroll.
139    Pixel,
140}
141
142impl MouseScrollUnit {
143    /// An approximate conversion factor to account for the difference between
144    /// [`MouseScrollUnit::Line`] and [`MouseScrollUnit::Pixel`].
145    ///
146    /// Each line corresponds to many pixels; this must be corrected for in order to ensure that
147    /// mouse wheel controls are scaled properly regardless of the provided input events for the end user.
148    ///
149    /// This value is correct for Microsoft Edge, but its validity has not been broadly tested.
150    /// Please file an issue if you find that this differs on certain platforms or hardware!
151    pub const SCROLL_UNIT_CONVERSION_FACTOR: f32 = 100.;
152}
153
154/// A mouse wheel event.
155///
156/// This event is the translated version of the `WindowEvent::MouseWheel` from the `winit` crate.
157#[derive(Message, Debug, Clone, Copy, PartialEq)]
158#[cfg_attr(
159    feature = "bevy_reflect",
160    derive(Reflect),
161    reflect(Debug, PartialEq, Clone, Message)
162)]
163#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
164#[cfg_attr(
165    all(feature = "serialize", feature = "bevy_reflect"),
166    reflect(Serialize, Deserialize)
167)]
168pub struct MouseWheel {
169    /// The mouse scroll unit.
170    pub unit: MouseScrollUnit,
171    /// The horizontal scroll value.
172    pub x: f32,
173    /// The vertical scroll value.
174    pub y: f32,
175    /// Window that received the input.
176    pub window: Entity,
177    /// Touch phase of the input.
178    ///
179    /// When using a mouse, this will always be [`TouchPhase::Moved`].
180    pub phase: TouchPhase,
181}
182
183/// Updates the [`ButtonInput<MouseButton>`] resource with the latest [`MouseButtonInput`] events.
184///
185/// ## Differences
186///
187/// The main difference between the [`MouseButtonInput`] event and the [`ButtonInput<MouseButton>`] resource is that
188/// the latter has convenient functions like [`ButtonInput::pressed`], [`ButtonInput::just_pressed`] and [`ButtonInput::just_released`].
189pub fn mouse_button_input_system(
190    mut mouse_button_input: ResMut<ButtonInput<MouseButton>>,
191    mut mouse_button_input_events: MessageReader<MouseButtonInput>,
192) {
193    mouse_button_input.bypass_change_detection().clear();
194    for event in mouse_button_input_events.read() {
195        match event.state {
196            ButtonState::Pressed => mouse_button_input.press(event.button),
197            ButtonState::Released => mouse_button_input.release(event.button),
198        }
199    }
200}
201
202/// Tracks how much the mouse has moved every frame.
203///
204/// This resource is reset to zero every frame.
205///
206/// This resource sums the total [`MouseMotion`] events received this frame.
207#[derive(Resource, Debug, Clone, Copy, PartialEq, Default)]
208#[cfg_attr(
209    feature = "bevy_reflect",
210    derive(Reflect),
211    reflect(Debug, Default, Resource, PartialEq, Clone)
212)]
213#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
214#[cfg_attr(
215    all(feature = "serialize", feature = "bevy_reflect"),
216    reflect(Serialize, Deserialize)
217)]
218pub struct AccumulatedMouseMotion {
219    /// The change in mouse position.
220    pub delta: Vec2,
221}
222
223/// Tracks how much the mouse has scrolled every frame.
224///
225/// This resource is reset to zero every frame.
226///
227/// This resource sums the total [`MouseWheel`] events received this frame.
228#[derive(Resource, Debug, Clone, Copy, PartialEq)]
229#[cfg_attr(
230    feature = "bevy_reflect",
231    derive(Reflect),
232    reflect(Debug, Default, Resource, PartialEq, Clone)
233)]
234#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
235#[cfg_attr(
236    all(feature = "serialize", feature = "bevy_reflect"),
237    reflect(Serialize, Deserialize)
238)]
239pub struct AccumulatedMouseScroll {
240    /// The mouse scroll unit.
241    /// If this value changes while scrolling, then the
242    /// result of the accumulation could be incorrect
243    pub unit: MouseScrollUnit,
244    /// The change in scroll position.
245    pub delta: Vec2,
246}
247
248impl Default for AccumulatedMouseScroll {
249    fn default() -> Self {
250        Self {
251            unit: MouseScrollUnit::Line,
252            delta: Vec2::ZERO,
253        }
254    }
255}
256
257/// Updates the [`AccumulatedMouseMotion`] resource using the [`MouseMotion`] event.
258/// The value of [`AccumulatedMouseMotion`] is reset to zero every frame
259pub fn accumulate_mouse_motion_system(
260    mut mouse_motion_event: MessageReader<MouseMotion>,
261    mut accumulated_mouse_motion: ResMut<AccumulatedMouseMotion>,
262) {
263    let mut delta = Vec2::ZERO;
264    for event in mouse_motion_event.read() {
265        delta += event.delta;
266    }
267    accumulated_mouse_motion.delta = delta;
268}
269
270/// Updates the [`AccumulatedMouseScroll`] resource using the [`MouseWheel`] event.
271/// The value of [`AccumulatedMouseScroll`] is reset to zero every frame
272pub fn accumulate_mouse_scroll_system(
273    mut mouse_scroll_event: MessageReader<MouseWheel>,
274    mut accumulated_mouse_scroll: ResMut<AccumulatedMouseScroll>,
275) {
276    let mut delta = Vec2::ZERO;
277    let mut unit = MouseScrollUnit::Line;
278    for event in mouse_scroll_event.read() {
279        if event.unit != unit {
280            unit = event.unit;
281        }
282        delta += Vec2::new(event.x, event.y);
283    }
284    accumulated_mouse_scroll.delta = delta;
285    accumulated_mouse_scroll.unit = unit;
286}