pix_engine/state.rs
1//! [`PixState`] methods for the [`Engine`] and [`PixEngine`].
2//!
3//! `PixState` is the global engine state and API for any application using `pix-engine`. A mutable
4//! reference is passed to most [`PixEngine`] methods and allows you to modify settings, query engine
5//! and input state, as well as drawing to the current render target.
6//!
7//! The most common use of `PixState` is in the [`PixEngine::on_update`] method.
8//!
9//! See the [Getting Started](crate#getting-started) section and the [`PixState`] page for the list
10//! of available methods.
11//!
12//! Provided [`PixState`] methods:
13//!
14//! - [`PixState::title`]: Current window title.
15//! - [`PixState::set_title`]: Set new window title.
16//! - [`PixState::mouse_pos`]: [Mouse] position this frame.
17//! - [`PixState::pmouse_pos`]: [Mouse] position previous frame.
18//! - [`PixState::mouse_pressed`]: Whether any [Mouse] button was pressed this frame.
19//! - [`PixState::mouse_clicked`]: Whether a given [Mouse] button was clicked this frame.
20//! - [`PixState::mouse_down`]: Whether a given [Mouse] button was pressed this frame.
21//! - [`PixState::mouse_buttons`]: A [`HashSet`] of [Mouse] buttons pressed this frame.
22//! - [`PixState::key_pressed`]: Whether a given [Key] was pressed this frame.
23//! - [`PixState::key_down`]: Whether a given [Key] was pressed this frame.
24//! - [`PixState::keys`]: Whether any [Key] was pressed this frame.
25//! - [`PixState::keymod_down`]: Whether a given [key modifier][`KeyMod`] was pressed this frame.
26//! - [`PixState::keymod`]: The [`KeyMod`]s pressed this frame.
27//!
28//! # Example
29//!
30//! ```
31//! # use pix_engine::prelude::*;
32//! # struct App { checkbox: bool, text_field: String };
33//! # impl PixEngine for App {
34//! fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
35//! s.fill(s.theme().colors.primary);
36//! s.rect([100, 0, 100, 100])?;
37//! if s.button("Click me")? {
38//! s.text("I was clicked!");
39//! }
40//! Ok(())
41//! }
42//! # }
43//! ```
44
45use crate::{
46 gui::state::UiState,
47 prelude::*,
48 renderer::{Renderer, RendererSettings, Rendering, WindowRenderer},
49 texture::TextureRenderer,
50};
51use environment::Environment;
52use settings::Settings;
53use std::{collections::HashSet, mem, time::Instant};
54
55pub mod environment;
56pub mod settings;
57
58/// Represents all state and methods for updating and interacting with the [`Engine`].
59#[non_exhaustive]
60#[derive(Debug)]
61pub struct PixState {
62 pub(crate) renderer: Renderer,
63 pub(crate) env: Environment,
64 pub(crate) ui: UiState,
65 pub(crate) settings: Settings,
66 pub(crate) setting_stack: Vec<Settings>,
67 pub(crate) theme: Theme,
68}
69
70impl PixState {
71 /// Get the current window title.
72 ///
73 /// # Example
74 ///
75 /// ```
76 /// # use pix_engine::prelude::*;
77 /// # struct App;
78 /// # impl PixEngine for App {
79 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
80 /// s.text(format!("Window title: {}", s.title()))?;
81 /// Ok(())
82 /// }
83 /// # }
84 /// ```
85 #[inline]
86 #[must_use]
87 pub fn title(&self) -> &str {
88 self.renderer.title()
89 }
90
91 /// Set the current window title.
92 ///
93 /// # Errors
94 ///
95 /// If the current window target is closed or invalid or the string contains a `nul` byte, then
96 /// an error is returned.
97 ///
98 /// # Example
99 ///
100 /// ```
101 /// # use pix_engine::prelude::*;
102 /// # struct App;
103 /// # impl PixEngine for App {
104 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
105 /// if s.button("Change title")? {
106 /// s.set_title("Title changed!")?;
107 /// }
108 /// Ok(())
109 /// }
110 /// # }
111 /// ```
112 #[inline]
113 pub fn set_title<S: AsRef<str>>(&mut self, title: S) -> PixResult<()> {
114 self.renderer.set_title(title.as_ref())
115 }
116
117 /// Returns the current mouse position coordinates this frame as `(x, y)`.
118 ///
119 /// # Example
120 ///
121 /// ```
122 /// # use pix_engine::prelude::*;
123 /// # struct App;
124 /// # impl PixEngine for App {
125 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
126 /// // Draw 100x100 rectangle that follows the mouse
127 /// s.rect(rect![s.mouse_pos(), 100, 100])?;
128 /// Ok(())
129 /// }
130 /// # }
131 /// ```
132 #[inline]
133 pub fn mouse_pos(&self) -> Point<i32> {
134 self.ui.mouse_pos()
135 }
136
137 /// Returns the previous mouse position coordinates last frame as `(x, y)`.
138 ///
139 /// # Example
140 ///
141 /// ```
142 /// # use pix_engine::prelude::*;
143 /// # struct App;
144 /// # impl PixEngine for App {
145 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
146 /// // Draw 100x100 rectangle that follows the mouse last frame
147 /// // Creates a yoyo-like effect
148 /// s.rect(rect![s.pmouse_pos(), 100, 100])?;
149 /// Ok(())
150 /// }
151 /// # }
152 /// ```
153 #[inline]
154 pub fn pmouse_pos(&self) -> Point<i32> {
155 self.ui.pmouse_pos()
156 }
157
158 /// Returns if any [Mouse] button was pressed this frame.
159 ///
160 /// # Example
161 ///
162 /// ```
163 /// # use pix_engine::prelude::*;
164 /// # struct App;
165 /// # impl PixEngine for App {
166 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
167 /// if s.mouse_pressed() {
168 /// s.background(Color::random());
169 /// }
170 /// Ok(())
171 /// }
172 /// # }
173 /// ```
174 #[inline]
175 #[must_use]
176 pub fn mouse_pressed(&self) -> bool {
177 self.ui.mouse_pressed()
178 }
179
180 /// Returns if the [Mouse] was clicked (pressed and released) this frame.
181 ///
182 /// # Example
183 ///
184 /// ```
185 /// # use pix_engine::prelude::*;
186 /// # struct App;
187 /// # impl PixEngine for App {
188 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
189 /// if s.mouse_clicked(Mouse::Left) {
190 /// s.background(Color::random());
191 /// }
192 /// Ok(())
193 /// }
194 /// # }
195 /// ```
196 #[inline]
197 #[must_use]
198 pub fn mouse_clicked(&self, btn: Mouse) -> bool {
199 self.ui.mouse_clicked(btn)
200 }
201
202 /// Returns if the [Mouse] was double clicked (pressed and released) this frame.
203 ///
204 /// # Example
205 ///
206 /// ```
207 /// # use pix_engine::prelude::*;
208 /// # struct App;
209 /// # impl PixEngine for App {
210 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
211 /// if s.mouse_dbl_clicked(Mouse::Left) {
212 /// s.background(Color::random());
213 /// }
214 /// Ok(())
215 /// }
216 /// # }
217 /// ```
218 #[inline]
219 #[must_use]
220 pub fn mouse_dbl_clicked(&self, btn: Mouse) -> bool {
221 self.ui.mouse_dbl_clicked(btn)
222 }
223
224 /// Returns if a specific [Mouse] button was pressed this frame.
225 ///
226 /// # Example
227 ///
228 /// ```
229 /// # use pix_engine::prelude::*;
230 /// # struct App;
231 /// # impl PixEngine for App {
232 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
233 /// if s.mouse_down(Mouse::Left) {
234 /// s.background(Color::random());
235 /// }
236 /// Ok(())
237 /// }
238 /// # }
239 /// ```
240 #[inline]
241 #[must_use]
242 pub fn mouse_down(&self, btn: Mouse) -> bool {
243 self.ui.mouse_down(btn)
244 }
245
246 /// Returns a list of the current mouse buttons pressed this frame.
247 ///
248 /// # Example
249 ///
250 /// ```
251 /// # use pix_engine::prelude::*;
252 /// # struct App;
253 /// # impl PixEngine for App {
254 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
255 /// // Only trigger if both buttons are pressed
256 /// if s.mouse_buttons().contains(&Mouse::Left)
257 /// && s.mouse_buttons().contains(&Mouse::Right)
258 /// {
259 /// s.background(Color::random());
260 /// }
261 /// Ok(())
262 /// }
263 /// # }
264 /// ```
265 #[inline]
266 #[must_use]
267 pub const fn mouse_buttons(&self) -> &HashSet<Mouse> {
268 self.ui.mouse_buttons()
269 }
270
271 /// Returns if any [Key] was pressed this frame.
272 ///
273 /// # Example
274 ///
275 /// ```
276 /// # use pix_engine::prelude::*;
277 /// # struct App;
278 /// # impl PixEngine for App {
279 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
280 /// if s.key_pressed() {
281 /// s.background(Color::random());
282 /// }
283 /// Ok(())
284 /// }
285 /// # }
286 /// ```
287 #[inline]
288 #[must_use]
289 pub fn key_pressed(&self) -> bool {
290 self.ui.key_pressed()
291 }
292
293 /// Returns if a specific [Key] was pressed this frame.
294 ///
295 /// # Example
296 ///
297 /// ```
298 /// # use pix_engine::prelude::*;
299 /// # struct App;
300 /// # impl PixEngine for App {
301 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
302 /// if s.key_down(Key::Space) {
303 /// s.background(Color::random());
304 /// }
305 /// Ok(())
306 /// }
307 /// # }
308 /// ```
309 #[inline]
310 #[must_use]
311 pub fn key_down(&self, key: Key) -> bool {
312 self.ui.key_down(key)
313 }
314
315 /// Returns a list of the current keys pressed this frame.
316 ///
317 /// # Example
318 ///
319 /// ```
320 /// # use pix_engine::prelude::*;
321 /// # struct App;
322 /// # impl PixEngine for App {
323 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
324 /// if s.keys().contains(&Key::Space) && s.keys().contains(&Key::Up) {
325 /// s.background(Color::random());
326 /// }
327 /// Ok(())
328 /// }
329 /// # }
330 /// ```
331 #[inline]
332 #[must_use]
333 pub const fn keys(&self) -> &HashSet<Key> {
334 self.ui.keys()
335 }
336
337 /// Returns if a specific [`KeyMod`] was pressed this frame.
338 ///
339 /// # Example
340 ///
341 /// ```
342 /// # use pix_engine::prelude::*;
343 /// # struct App;
344 /// # impl PixEngine for App {
345 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
346 /// if s.keymod_down(KeyMod::CTRL) && s.key_down(Key::Space) {
347 /// s.background(Color::random());
348 /// }
349 /// Ok(())
350 /// }
351 /// # }
352 /// ```
353 #[inline]
354 #[must_use]
355 pub const fn keymod_down(&self, keymod: KeyMod) -> bool {
356 self.ui.keymod_down(keymod)
357 }
358
359 /// Returns a list of the current key modifiers pressed this frame.
360 ///
361 /// # Example
362 ///
363 /// ```
364 /// # use pix_engine::prelude::*;
365 /// # struct App;
366 /// # impl PixEngine for App {
367 /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
368 /// if s.keymod().intersects(KeyMod::SHIFT | KeyMod::CTRL)
369 /// && s.key_down(Key::Space)
370 /// {
371 /// s.background(Color::random());
372 /// }
373 /// Ok(())
374 /// }
375 /// # }
376 /// ```
377 #[inline]
378 pub const fn keymod(&self) -> &KeyMod {
379 self.ui.keymod()
380 }
381}
382
383impl PixState {
384 /// Constructs `PixState` with a given [Renderer].
385 #[inline]
386 pub(crate) fn new(settings: RendererSettings, theme: Theme) -> PixResult<Self> {
387 let show_frame_rate = settings.show_frame_rate;
388 let target_frame_rate = settings.target_frame_rate;
389 let renderer = Renderer::new(settings)?;
390 let mut state = Self {
391 renderer,
392 env: Environment::default(),
393 ui: UiState::default(),
394 settings: Settings::default(),
395 setting_stack: Vec::new(),
396 theme: theme.clone(),
397 };
398 state.background(theme.colors.background);
399 state.fill(theme.colors.on_background());
400 state.show_frame_rate(show_frame_rate);
401 state.frame_rate(target_frame_rate);
402 state.font_size(theme.font_size)?;
403 state.font_style(theme.styles.body);
404 state.font_family(theme.fonts.body)?;
405 Ok(state)
406 }
407
408 /// Handle state changes this frame prior to calling [`PixEngine::on_update`].
409 #[inline]
410 pub(crate) fn pre_update(&mut self) {
411 // Reset mouse cursor icon to the current setting
412 // Ignore any errors, as setting cursor in the first place should have succeeded.
413 let _ignore_result = self.renderer.cursor(self.settings.cursor.as_ref());
414 self.ui.pre_update(&self.theme);
415 }
416
417 /// Handle state updates for this frame.
418 #[inline]
419 pub(crate) fn on_update(&mut self) -> PixResult<()> {
420 for texture in self.ui.textures.iter_mut().filter(|t| t.visible) {
421 self.renderer
422 .texture(texture.id, texture.src, texture.dst, 0.0, None, None, None)?;
423 }
424 Ok(())
425 }
426
427 /// Handle state changes this frame after calling [`PixEngine::on_update`].
428 #[inline]
429 pub(crate) fn post_update(&mut self) {
430 self.ui.post_update();
431 }
432
433 /// Takes a [Rect] and returns a modified [Rect] based on the current [`RectMode`].
434 #[inline]
435 pub(crate) fn get_rect<R>(&self, rect: R) -> Rect<i32>
436 where
437 R: Into<Rect<i32>>,
438 {
439 let mut rect = rect.into();
440 if self.settings.rect_mode == RectMode::Center {
441 rect.center_on(rect.top_left());
442 }
443 rect
444 }
445
446 /// Takes an [Ellipse] and returns a modified [Ellipse] based on the current [`EllipseMode`].
447 #[inline]
448 pub(crate) fn get_ellipse<E>(&self, ellipse: E) -> Ellipse<i32>
449 where
450 E: Into<Ellipse<i32>>,
451 {
452 let mut ellipse = ellipse.into();
453 if self.settings.ellipse_mode == RectMode::Corner {
454 ellipse.center_on(ellipse.bottom_right());
455 }
456 ellipse
457 }
458
459 /// Updates the mouse position state this frame.
460 #[inline]
461 pub(crate) fn on_mouse_motion(&mut self, pos: Point<i32>) {
462 self.ui.pmouse.pos = self.ui.mouse.pos;
463 self.ui.mouse.pos = pos;
464 }
465
466 /// Updates the mouse click state this frame.
467 #[inline]
468 pub(crate) fn on_mouse_click(&mut self, btn: Mouse, time: Instant) {
469 self.ui.pmouse.clicked = mem::take(&mut self.ui.mouse.clicked);
470 self.ui.pmouse.last_clicked = mem::take(&mut self.ui.mouse.last_clicked);
471 self.ui.mouse.click(btn, time);
472 }
473
474 /// Updates the mouse double click state this frame.
475 #[inline]
476 pub(crate) fn on_mouse_dbl_click(&mut self, btn: Mouse, time: Instant) {
477 self.ui.pmouse.last_dbl_clicked = mem::take(&mut self.ui.mouse.last_dbl_clicked);
478 self.ui.mouse.dbl_click(btn, time);
479 }
480
481 /// Updates the mouse pressed state this frame.
482 #[inline]
483 pub(crate) fn on_mouse_pressed(&mut self, btn: Mouse) {
484 self.ui.pmouse.pressed = mem::take(&mut self.ui.mouse.pressed);
485 self.ui.mouse.press(btn);
486 }
487
488 /// Updates the mouse released state this frame.
489 #[inline]
490 pub(crate) fn on_mouse_released(&mut self, btn: Mouse) {
491 self.ui.pmouse.pressed = mem::take(&mut self.ui.mouse.pressed);
492 self.ui.mouse.release(btn);
493 }
494
495 /// Updates the mouse wheel state this frame.
496 #[inline]
497 pub(crate) fn on_mouse_wheel(&mut self, x: i32, y: i32) {
498 self.ui.pmouse.xrel = self.ui.mouse.xrel;
499 self.ui.pmouse.yrel = self.ui.mouse.yrel;
500 self.ui.mouse.wheel(x, y);
501 }
502
503 /// Polls for events from the underlying renderer.
504 #[inline]
505 pub fn poll_event(&mut self) -> Option<Event> {
506 self.renderer.poll_event()
507 }
508
509 /// Open a controller with a given ID to start handling events.
510 ///
511 /// # Errors
512 ///
513 /// If the `ControllerId` is invalid or the renderer fails to open the controller, then an
514 /// error is returned.
515 #[inline]
516 pub fn open_controller(&mut self, id: ControllerId) -> PixResult<()> {
517 self.renderer.open_controller(id)
518 }
519
520 /// Close a controller with a given ID to stop handling events.
521 #[inline]
522 pub fn close_controller(&mut self, id: ControllerId) {
523 self.renderer.close_controller(id);
524 }
525}