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}