1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! [![CI](https://github.com/JoJoJet/bevy-mouse-tracking/actions/workflows/ci.yml/badge.svg)](https://github.com/JoJoJet/bevy-mouse-tracking/workflows/ci.yml)
//! [![bevy_mouse_tracking on crates.io](https://img.shields.io/crates/v/bevy_mouse_tracking_plugin.svg)](https://crates.io/crates/bevy_mouse_tracking_plugin)
//! [![bevy_mouse_tracking docs](https://img.shields.io/badge/docs-docs.rs-orange.svg)](https://docs.rs/bevy_mouse_tracking_plugin)
//!
//! Tracking the mouse in `bevy` is kind of annoying.
//! You gotta use [`Events`], and [`EventReader`]s, and even then, they only
//! get called when the mouse actually *moves*.
//!
//! [`Events`]: bevy::ecs::event::Events
//! [`EventReader`]: bevy::ecs::event::EventReader
//!
//! This crate aims to make this as easy as possible, by providing a
//! static [resource](bevy::ecs::system::Res) that tracks the mouse position every frame.
//!
//! This crate also supports more complex use cases such as multiple cameras, which are discussed further down.
//!
//! # Basics
//!
//! First, add the plugin to your app:
//!
//! ```
//! use bevy::prelude::*;
//! use bevy_mouse_tracking_plugin::MousePosPlugin;
//!
//! App::new()
//!     .add_plugins(DefaultPlugins)
//!     .add_plugin(MousePosPlugin::SingleCamera);
//! ```
//!
//! Now, you can access the resource in your [`System`]s:
//!
//! [`System`]: bevy::ecs::system::System
//!
//! ```
//! # use bevy::prelude::*;
//! use bevy_mouse_tracking_plugin::MousePos;
//! fn dbg_mouse(mouse: Res<MousePos>) {
//!     eprintln!("{}", *mouse);
//! }
//! ```
//! ...and don't forget to add the system to your app:
//! ```
//! # use bevy::prelude::*;
//! # use bevy_mouse_tracking_plugin::MousePosPlugin;
//! # App::new()
//! #   .add_plugins(DefaultPlugins)
//!     .add_plugin(MousePosPlugin::SingleCamera)
//!     .add_system(dbg_mouse);
//!
//! # fn dbg_mouse() { }
//! ```
//!
//! This will print the screen-space location of the mouse on every frame.
//!
//! However, we can do better than just screen-space: we support automatic
//! transformation to world-space coordinates via the [`MousePosWorld`] resource.
//!
//! ```
//! # use bevy::prelude::*;
//! use bevy_mouse_tracking_plugin::MousePosWorld;
//! fn dbg_world(mouse: Res<MousePosWorld>) {
//!     eprintln!("{}", *mouse);
//! }
//! ```
//!
//! This will print the world-space location of the mouse on every frame.  
//! Note that this is only supported for two-dimensional, orthographic camera,
//! but pull requests for 3D support are welcome!
//!
//! # Multiple cameras
//!
//! You may notice that if you try to use this plugin in an app that has multiple cameras, it crashes!
//!
//! ```should_panic
//! # use bevy::prelude::*;
//! # use bevy_mouse_tracking_plugin::MousePosPlugin;
//!
//! App::new()
//!     .add_plugins(DefaultPlugins)
//!     .add_plugin(MousePosPlugin::SingleCamera)
//!     .add_startup_system(setup)
//!     .run();
//!
//! fn setup(mut commands: Commands) {
//!     commands.spawn_bundle(Camera2dBundle::default());
//!     commands.spawn_bundle(Camera3dBundle::default());
//! }
//! ```
//!
//! This panics with the following output:
//!
//! ```text
//! thread 'main' panicked at 'cannot identify main camera -- consider adding the MainCamera component to one of the cameras', src\mouse_pos.rs:163:13
//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
//! ```
//!
//! This is because the plugin doesn't know which of the two cameras to use when figuring out
//! the values of the `MousePos` and `MousePosWorld` resources. Let's take the panic message's advice.
//!
//! ```
//! # use bevy::prelude::*;
//! # use bevy_mouse_tracking_plugin::{MousePosPlugin, MainCamera};
//! # App::new()
//! #   .add_plugins(DefaultPlugins)
//! #   .add_plugin(MousePosPlugin::SingleCamera)
//! #   .add_startup_system(setup)
//! #   .update();
//! # fn setup(mut commands: Commands) {
//!     commands.spawn_bundle(Camera2dBundle::default())
//!         .insert(MainCamera); // added this line
//!     commands.spawn_bundle(Camera3dBundle::default());
//! # }
//! ```
//!
//! ## Queries
//!
//! If you want to get mouse tracking information relative to each camera individually,
//! simply [query](bevy::ecs::system::Query) for a `MousePos` or `MousePosWorld` as a
//! _component_ instead of as a resource.
//!
//! ```
//! # use bevy::prelude::*;
//! # use bevy_mouse_tracking_plugin::{MousePosPlugin, MainCamera, MousePosWorld};
//!
//! App::new()
//!     // plugins omitted...
//! #   .add_plugins(DefaultPlugins)
//! #   .add_plugin(MousePosPlugin::SingleCamera)
//!     .add_system(dbg_for_each);
//!
//! fn dbg_for_each(mouse_pos: Query<&MousePosWorld>) {
//!     for pos in mouse_pos.iter() {
//!         // This prints the mouse position twice per frame:
//!         // once relative to the UI camera, and once relative to the physical camera.
//!         eprintln!("{}", *pos);
//!     }
//! }
//! ```
//!
//! If you want the mouse position for a specific camera, you can add query filters as always.  
//! Note that as of `bevy 0.6`, the only way to tell the difference between a UI camera and
//! an orthographic camera is by checking for the [`Frustum`] component.
//!
//! [`Frustum`]: bevy::render::primitives::Frustum
//!
//! ```
//! # use bevy::prelude::*;
//! # use bevy_mouse_tracking_plugin::{MousePosPlugin, MainCamera, MousePosWorld};
//!
//! # App::new()
//! #   .add_plugins(DefaultPlugins)
//! #   .add_plugin(MousePosPlugin::SingleCamera)
//! #   .add_system(dbg_ui_pos);
//!
//! use bevy::render::primitives::Frustum;
//! fn dbg_ui_pos(mouse_pos: Query<&MousePosWorld, Without<Frustum>>) {
//!     // query for the UI camera, which doesn't have a Frustum component.
//!     let pos = mouse_pos.single();
//!     eprintln!("{}", *pos);
//! }
//! ```
//!
//! ## No main camera
//!
//! Let's say you have multiple cameras in your app, and you want to treat them all equally,
//! without declaring any one of them as the main camera.  
//! Change the plugin to this:
//!
//! ```
//! # use bevy::prelude::*;
//! # use bevy_mouse_tracking_plugin::{MousePosPlugin};
//! App::new()
//!     .add_plugins(DefaultPlugins)
//!     .add_plugin(MousePosPlugin::MultiCamera) // SingleCamera -> MultiCamera
//!     .add_startup_system(setup)
//!     // ...
//! #   .update();
//!
//! # fn setup(mut commands: Commands) {
//! #   commands.spawn_bundle(Camera2dBundle::default());
//! #   commands.spawn_bundle(Camera2dBundle::default());
//! # }
//! ```
//!
//! Now, you can add as many cameras as you want, without having to worry about marking any
//! of them as the main camera.  
//! Note that `MousePos` and `MousePosWorld` will no longer be accessible as global resources
//! -- you can only access them by `Query`ing camera entities.
//!
//! # Mouse motion
//!
//! This crate supports a resource that tracks mouse motion, via [`MouseMotionPlugin`].
//! The motion can be accessed from any system in a [`MouseMotion`] resource.
//!
//! [`Res`]: bevy::ecs::system::Res
//!
//! # Crate name
//!
//! As a final aside: the name of this crate is intentionally verbose.
//! This is because I didn't want to steal a crate name, especially since
//! it is very likely that this crate will eventually be made redundant by
//! future updates to `bevy`.  
//! I recommend renaming the crate in your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! mouse_tracking = { package = "bevy_mouse_tracking_plugin", version = "..." }
//! ```

mod mouse_pos;
pub use mouse_pos::{MainCamera, MousePos, MousePosPlugin, MousePosWorld};

mod mouse_motion;
pub use mouse_motion::{MouseMotion, MouseMotionPlugin};