good_web_game/event.rs
1use crate::context::Context;
2
3#[cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android",)))]
4use crate::input::gamepad::GamepadId;
5pub use crate::input::keyboard::KeyMods;
6pub use crate::input::MouseButton;
7use crate::GameError;
8#[cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android",)))]
9use gilrs::{Axis, Button};
10pub use miniquad::{graphics::GraphicsContext, KeyCode, TouchPhase};
11
12/// Used in [`EventHandler`](trait.EventHandler.html)
13/// to specify where an error originated
14#[derive(Debug, Copy, Clone, Eq, PartialEq)]
15pub enum ErrorOrigin {
16 /// error originated in `update()`
17 Update,
18 /// error originated in `draw()`
19 Draw,
20}
21
22/// A trait defining event callbacks. This is your primary interface with
23/// `ggez`'s event loop. Implement this trait for a type and
24/// override at least the [`update()`](#tymethod.update) and
25/// [`draw()`](#tymethod.draw) methods, then pass it to
26/// [`event::run()`](fn.run.html) to run the game's mainloop.
27///
28/// The default event handlers do nothing, apart from
29/// [`key_down_event()`](#tymethod.key_down_event), which will by
30/// default exit the game if the escape key is pressed. Just
31/// override the methods you want to use.
32pub trait EventHandler<E = GameError>
33where
34 E: std::error::Error,
35{
36 /// Called upon each logic update to the game.
37 /// This should be where the game's logic takes place.
38 fn update(&mut self, _ctx: &mut Context, _quad_ctx: &mut GraphicsContext) -> Result<(), E>;
39 /// Called to do the drawing of your game.
40 /// You probably want to start this with
41 /// [`graphics::clear()`](../graphics/fn.clear.html) and end it
42 /// with [`graphics::present()`](../graphics/fn.present.html).
43 fn draw(&mut self, _ctx: &mut Context, _quad_ctx: &mut GraphicsContext) -> Result<(), E>;
44 /// Called when the user resizes the window, or when it is resized
45 /// via [`graphics::set_drawable_size()`](../graphics/fn.set_drawable_size.html).
46 fn resize_event(
47 &mut self,
48 _ctx: &mut Context,
49 _quad_ctx: &mut GraphicsContext,
50 _width: f32,
51 _height: f32,
52 ) {
53 }
54 /// The mouse was moved; it provides both absolute x and y coordinates in the window,
55 /// and relative x and y coordinates compared to its last position.
56 fn mouse_motion_event(
57 &mut self,
58 _ctx: &mut Context,
59 _quad_ctx: &mut GraphicsContext,
60 _x: f32,
61 _y: f32,
62 _dx: f32,
63 _dy: f32,
64 ) {
65 }
66 fn touch_event(
67 &mut self,
68 ctx: &mut Context,
69 _quad_ctx: &mut GraphicsContext,
70 phase: TouchPhase,
71 _id: u64,
72 x: f32,
73 y: f32,
74 ) {
75 ctx.mouse_context.input_handler.handle_mouse_move(x, y);
76 // TODO: this is ugly code duplication; I'll fix it later when getting rid of the `InputHandler`
77 let old_pos = crate::mouse::last_position(ctx);
78 let dx = x - old_pos.x;
79 let dy = y - old_pos.y;
80 // update the frame delta value
81 let old_delta = crate::mouse::delta(ctx);
82 ctx.mouse_context
83 .set_delta((old_delta.x + dx, old_delta.y + dy).into());
84 ctx.mouse_context.set_last_position((x, y).into());
85 match phase {
86 TouchPhase::Started => {
87 ctx.mouse_context
88 .input_handler
89 .handle_mouse_down(miniquad::MouseButton::Left);
90 self.mouse_button_down_event(ctx, _quad_ctx, MouseButton::Left, x, y);
91 }
92 TouchPhase::Moved => {
93 self.mouse_motion_event(ctx, _quad_ctx, x, y, dx, dy);
94 }
95 TouchPhase::Ended | TouchPhase::Cancelled => {
96 ctx.mouse_context
97 .input_handler
98 .handle_mouse_up(miniquad::MouseButton::Left);
99 self.mouse_button_up_event(ctx, _quad_ctx, MouseButton::Left, x, y);
100 }
101 }
102 }
103 /// The mousewheel was scrolled, vertically (y, positive away from and negative toward the user)
104 /// or horizontally (x, positive to the right and negative to the left).
105 fn mouse_wheel_event(
106 &mut self,
107 _ctx: &mut Context,
108 _quad_ctx: &mut GraphicsContext,
109 _x: f32,
110 _y: f32,
111 ) {
112 }
113 /// A mouse button was pressed.
114 fn mouse_button_down_event(
115 &mut self,
116 _ctx: &mut Context,
117 _quad_ctx: &mut GraphicsContext,
118 _button: MouseButton,
119 _x: f32,
120 _y: f32,
121 ) {
122 }
123 /// A mouse button was released.
124 fn mouse_button_up_event(
125 &mut self,
126 _ctx: &mut Context,
127 _quad_ctx: &mut GraphicsContext,
128 _button: MouseButton,
129 _x: f32,
130 _y: f32,
131 ) {
132 }
133
134 /// A keyboard button was pressed.
135 ///
136 /// The default implementation of this will call `ggez::event::quit()`
137 /// when the escape key is pressed. If you override this with
138 /// your own event handler you have to re-implment that
139 /// functionality yourself.
140 fn key_down_event(
141 &mut self,
142 ctx: &mut Context,
143 _quad_ctx: &mut GraphicsContext,
144 keycode: KeyCode,
145 _keymods: KeyMods,
146 _repeat: bool,
147 ) {
148 if keycode == KeyCode::Escape {
149 quit(ctx);
150 }
151 }
152
153 /// A keyboard button was released.
154 fn key_up_event(
155 &mut self,
156 _ctx: &mut Context,
157 _quad_ctx: &mut GraphicsContext,
158 _keycode: KeyCode,
159 _keymods: KeyMods,
160 ) {
161 }
162
163 /// A unicode character was received, usually from keyboard input.
164 /// This is the intended way of facilitating text input.
165 fn text_input_event(
166 &mut self,
167 _ctx: &mut Context,
168 _quad_ctx: &mut GraphicsContext,
169 _character: char,
170 ) {
171 }
172
173 #[cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android",)))]
174 /// A gamepad button was pressed; `id` identifies which gamepad.
175 /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
176 /// the gamepad.
177 fn gamepad_button_down_event(
178 &mut self,
179 _ctx: &mut Context,
180 _quad_ctx: &mut GraphicsContext,
181 _btn: Button,
182 _id: GamepadId,
183 ) {
184 }
185
186 #[cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android",)))]
187 /// A gamepad button was released; `id` identifies which gamepad.
188 /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
189 /// the gamepad.
190 fn gamepad_button_up_event(
191 &mut self,
192 _ctx: &mut Context,
193 _quad_ctx: &mut GraphicsContext,
194 _btn: Button,
195 _id: GamepadId,
196 ) {
197 }
198
199 #[cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android",)))]
200 /// A gamepad axis moved; `id` identifies which gamepad.
201 /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
202 /// the gamepad.
203 fn gamepad_axis_event(
204 &mut self,
205 _ctx: &mut Context,
206 _quad_ctx: &mut GraphicsContext,
207 _axis: Axis,
208 _value: f32,
209 _id: GamepadId,
210 ) {
211 }
212
213 /// Something went wrong, causing a `GameError`.
214 /// If this returns true, the error was fatal, so the event loop ends, aborting the game.
215 fn on_error(
216 &mut self,
217 _ctx: &mut Context,
218 _quad_ctx: &mut GraphicsContext,
219 _origin: ErrorOrigin,
220 _e: E,
221 ) -> bool {
222 true
223 }
224}
225
226/// Terminates the [`ggez::event::run()`](fn.run.html) loop by setting
227/// [`Context.continuing`](struct.Context.html#structfield.continuing)
228/// to `false`.
229///
230/// NOTE: Doesn't end the application on Wasm, as that's not really possible,
231/// but stops the `update` function from running.
232pub fn quit(ctx: &mut Context) {
233 ctx.continuing = false;
234}