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