pix_engine/state/
settings.rs

1//! Settings methods for the [`Engine`].
2//!
3//! Methods for reading and setting various engine configuration values.
4//!
5//! Provided types:
6//!
7//! - [`DrawMode`]: Determines how `(x, y)` coordinates are used for rendering.
8//! - [`RectMode`]: Alias for `DrawMode`.
9//! - [`EllipseMode`]: Alias for `DrawMode`.
10//! - [`ImageMode`]: Alias for `DrawMode`.
11//! - [`ArcMode`]: Determines how arcs are rendered.
12//! - [`BlendMode`]: Determines how images and textures are blended.
13//! - [`AngleMode`]: Determines how angles are interpreted.
14//! - [`FontStyle`]: Determines how text is rendered.
15//!
16//! Provided [`PixState`] methods:
17//!
18//! - [`PixState::background`]: Sets the [Color] used by [`PixState::clear`] to clear the canvas.
19//! - [`PixState::fill`]: Sets the [Color] used to fill shapes.
20//! - [`PixState::stroke`]: Sets the [Color] used to stroke shapes and text.
21//! - [`PixState::stroke_weight`]: Sets the stroke line thickness for lines and text.
22//! - [`PixState::text_shadow`]: Sets the shadow distance for drawing text.
23//! - [`PixState::smooth`]: Enables the anti-alias smoothing option for drawing shapes.
24//! - [`PixState::bezier_detail`]: Set the resolution at which Bezier curves are dispalyed.
25//! - [`PixState::wrap`]: Sets the wrap width for rendering text.
26//! - [`PixState::clip`]: Sets a clip rectangle for rendering.
27//! - [`PixState::fullscreen`]: Sets fullscreen mode to enabled or disabled.
28//! - [`PixState::toggle_fullscreen`]: Toggles fullscreen.
29//! - [`PixState::vsync`]: Sets vertical sync mode to enabled or disabled.
30//! - [`PixState::toggle_vsync`]: Toggles vertical sync.
31//! - [`PixState::cursor`]: Set a custom window cursor or hide the cursor.
32//! - [`PixState::disable`]: Disable UI elements from being interactive.
33//! - [`PixState::running`]: Whether the render loop is running (calling [`PixEngine::on_update`]).
34//! - [`PixState::run`]: Enable or disable the render loop.
35//! - [`PixState::show_frame_rate`]: Display the average frame rate in the title bar.
36//! - [`PixState::target_frame_rate`]: Return the current targeted frame rate.
37//! - [`PixState::frame_rate`]: Set or clear a targeted frame rate.
38//! - [`PixState::scale`]: Set the rendering scale of the current canvas.
39//! - [`PixState::rect_mode`]: Change the [`RectMode`] for rendering rectangles.
40//! - [`PixState::ellipse_mode`]: Change the [`EllipseMode`] for rendering ellipses.
41//! - [`PixState::image_mode`]: Change the [`ImageMode`] for rendering images.
42//! - [`PixState::image_tint`]: Set or clear a [Color] used to tint [Image]s.
43//! - [`PixState::arc_mode`]: Change the [`ArcMode`] for rendering arcs.
44//! - [`PixState::angle_mode`]: Change the [`AngleMode`] for angle interpretation.
45//! - [`PixState::blend_mode`]: Change the [`BlendMode`] for rendering images and textures.
46//! - [`PixState::push`]: Push a copy of all the current settings to a stack.
47//! - [`PixState::pop`]: Pop the previously pushed settings off the stack, restoring them.
48
49use crate::{
50    prelude::*,
51    renderer::{Rendering, WindowRenderer},
52};
53use bitflags::bitflags;
54#[cfg(feature = "serde")]
55use serde::{Deserialize, Serialize};
56use std::time::Duration;
57
58/// Drawing mode which changes how `(x, y)` coordinates are interpreted.
59#[non_exhaustive]
60#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
61#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
62pub enum DrawMode {
63    /// Use `(x, y)` as the top-left corner. Default.
64    Corner,
65    /// Use `(x, y)` as the center.
66    Center,
67}
68
69/// Drawing mode which determines how `(x, y)` coordinates are interpreted when drawing [Rect]s.
70pub type RectMode = DrawMode;
71
72/// Drawing mode which determines how `(x, y)` coordinates are interpreted when drawing [Ellipse]s.
73pub type EllipseMode = DrawMode;
74
75/// Drawing mode which determines how `(x, y)` coordinates are interpreted when drawing [Image]s.
76pub type ImageMode = DrawMode;
77
78/// Drawing mode which determines how arcs are drawn.
79#[non_exhaustive]
80#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
81#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
82pub enum ArcMode {
83    /// Draws arc as an open, unfilled pie segment using `stroke` color.
84    Default,
85    /// Draws arc as a closed pie segment using `stroke` and `fill` colors.
86    Pie,
87}
88
89/// Drawing mode which determines how textures are blended together.
90#[non_exhaustive]
91#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93pub enum BlendMode {
94    /// Disable blending.
95    None,
96    /// Alpha blending.
97    Blend,
98    /// Additive blending.
99    Add,
100    /// Color modulate.
101    Mod,
102}
103
104/// Determines how angles are interpreted.
105#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
106#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
107pub enum AngleMode {
108    /// Radians.
109    Radians,
110    /// Degrees.
111    Degrees,
112}
113
114bitflags! {
115    /// Font style for drawing text.
116    #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
118    #[cfg_attr(feature = "serde", serde(transparent))]
119    pub struct FontStyle: i32 {
120        /// Normal.
121        const NORMAL = 0x00;
122        /// Bold.
123        const BOLD = 0x01;
124        /// Italic.
125        const ITALIC = 0x02;
126        /// Underline
127        const UNDERLINE = 0x04;
128        /// Strike-through
129        const STRIKETHROUGH = 0x08;
130    }
131}
132
133/// Several settings used to change various functionality of the engine.
134#[derive(Debug, Clone)]
135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
136pub(crate) struct Settings {
137    pub(crate) background: Color,
138    pub(crate) fill: Option<Color>,
139    pub(crate) stroke: Option<Color>,
140    pub(crate) stroke_weight: u16,
141    pub(crate) font_size: u32,
142    pub(crate) font_style: FontStyle,
143    #[cfg_attr(feature = "serde", serde(skip))]
144    pub(crate) font_family: Font,
145    pub(crate) text_shadow: Option<u16>,
146    pub(crate) smooth: bool,
147    pub(crate) bezier_detail: i32,
148    pub(crate) wrap_width: Option<u32>,
149    pub(crate) clip: Option<Rect<i32>>,
150    pub(crate) running: bool,
151    pub(crate) show_frame_rate: bool,
152    pub(crate) target_frame_rate: Option<usize>,
153    pub(crate) target_delta_time: Option<Duration>,
154    pub(crate) scale_x: f32,
155    pub(crate) scale_y: f32,
156    pub(crate) rect_mode: RectMode,
157    pub(crate) ellipse_mode: EllipseMode,
158    pub(crate) image_mode: ImageMode,
159    pub(crate) image_tint: Option<Color>,
160    pub(crate) arc_mode: ArcMode,
161    pub(crate) angle_mode: AngleMode,
162    pub(crate) blend_mode: BlendMode,
163    pub(crate) cursor: Option<Cursor>,
164    pub(crate) disabled: bool,
165}
166
167impl Default for Settings {
168    fn default() -> Self {
169        Self {
170            background: Color::BLACK,
171            fill: Some(Color::WHITE),
172            stroke: None,
173            stroke_weight: 1,
174            font_size: 14,
175            font_style: FontStyle::NORMAL,
176            font_family: Font::default(),
177            text_shadow: None,
178            smooth: true,
179            bezier_detail: 20,
180            wrap_width: None,
181            clip: None,
182            running: true,
183            show_frame_rate: false,
184            target_frame_rate: None,
185            target_delta_time: None,
186            scale_x: 1.0,
187            scale_y: 1.0,
188            rect_mode: RectMode::Corner,
189            ellipse_mode: EllipseMode::Center,
190            image_mode: ImageMode::Corner,
191            image_tint: None,
192            arc_mode: ArcMode::Default,
193            angle_mode: AngleMode::Radians,
194            blend_mode: BlendMode::None,
195            cursor: Some(Cursor::default()),
196            disabled: false,
197        }
198    }
199}
200
201impl PixState {
202    /// Sets the [Color] value used to clear the canvas.
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    ///     s.background(Color::ALICE_BLUE);
212    ///     s.clear();
213    ///     Ok(())
214    /// }
215    /// # }
216    /// ```
217    #[inline]
218    pub fn background<C>(&mut self, color: C)
219    where
220        C: Into<Color>,
221    {
222        self.settings.background = color.into();
223        let _result = self.clear(); // If this errors, something is very wrong
224    }
225
226    /// Sets the [Color] value used to fill shapes drawn on the canvas. `None` disables fill
227    /// entirely.
228    ///
229    /// # Example
230    ///
231    /// ```
232    /// # use pix_engine::prelude::*;
233    /// # struct App;
234    /// # impl PixEngine for App {
235    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
236    ///     s.fill(Color::ALICE_BLUE);
237    ///     s.rect([0, 0, 100, 100])?;
238    ///     s.fill((None));
239    ///     s.rect([25, 25, 75, 75])?;
240    ///     Ok(())
241    /// }
242    /// # }
243    /// ```
244    #[inline]
245    pub fn fill<C>(&mut self, color: C)
246    where
247        C: Into<Option<Color>>,
248    {
249        self.settings.fill = color.into();
250    }
251
252    /// Sets the [Color] value used to outline shapes drawn on the canvas. `None` disables stroke
253    /// entirely.
254    ///
255    /// # Example
256    ///
257    /// ```
258    /// # use pix_engine::prelude::*;
259    /// # struct App;
260    /// # impl PixEngine for App {
261    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
262    ///     s.stroke(Color::BLACK);
263    ///     s.rect([0, 0, 100, 100])?;
264    ///     s.stroke((None));
265    ///     s.rect([25, 25, 75, 75])?;
266    ///     Ok(())
267    /// }
268    /// # }
269    /// ```
270    #[inline]
271    pub fn stroke<C>(&mut self, color: C)
272    where
273        C: Into<Option<Color>>,
274    {
275        self.settings.stroke = color.into();
276    }
277
278    /// Sets the width used to draw lines on the canvas.
279    ///
280    /// # Example
281    ///
282    /// ```
283    /// # use pix_engine::prelude::*;
284    /// # struct App;
285    /// # impl PixEngine for App {
286    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
287    ///     s.stroke(Color::BLUE);
288    ///     s.stroke_weight(2);
289    ///     // Draws a 2-pixel wide diagonal line
290    ///     s.line(line_![0, 0, 100, 100])?;
291    ///     Ok(())
292    /// }
293    /// # }
294    /// ```
295    #[inline]
296    pub fn stroke_weight(&mut self, weight: u16) {
297        self.settings.stroke_weight = weight;
298    }
299
300    /// Set the font size for drawing to the current canvas.
301    ///
302    /// # Errors
303    ///
304    /// If the renderer fails to load the given font size from the currently loaded font data, then
305    /// an error is returned.
306    ///
307    /// # Example
308    ///
309    /// ```
310    /// # use pix_engine::prelude::*;
311    /// # struct App;
312    /// # impl PixEngine for App {
313    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
314    ///     s.font_size(22);
315    ///     s.text("Some big text")?;
316    ///     Ok(())
317    /// }
318    /// # }
319    /// ```
320    #[inline]
321    pub fn font_size(&mut self, size: u32) -> PixResult<()> {
322        self.settings.font_size = size;
323        self.theme.font_size = size;
324        self.renderer.font_size(size)
325    }
326
327    /// Set the font style for drawing to the current canvas.
328    ///
329    /// # Errors
330    ///
331    /// If the renderer fails to load the current font, then an error is returned.
332    ///
333    /// # Example
334    ///
335    /// ```
336    /// # use pix_engine::prelude::*;
337    /// # struct App;
338    /// # impl PixEngine for App {
339    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
340    ///     s.font_style(FontStyle::BOLD);
341    ///     s.text("Some bold text")?;
342    ///     Ok(())
343    /// }
344    /// # }
345    /// ```
346    #[inline]
347    pub fn font_style(&mut self, style: FontStyle) {
348        self.settings.font_style = style;
349        self.renderer.font_style(style);
350    }
351
352    /// Set the font family for drawing to the current canvas.
353    ///
354    /// # Errors
355    ///
356    /// If the renderer fails to load the given font size from the currently loaded font data, then
357    /// an error is returned.
358    ///
359    /// # Example
360    ///
361    /// ```
362    /// # use pix_engine::prelude::*;
363    /// # struct App;
364    /// # impl PixEngine for App {
365    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
366    ///     s.font_family(Font::NOTO)?;
367    ///     s.text("Some NOTO family text")?;
368    ///     s.font_family(Font::from_file("Custom font", "./custom_font.ttf"))?;
369    ///     s.text("Some custom family text")?;
370    ///     Ok(())
371    /// }
372    /// # }
373    /// ```
374    #[inline]
375    pub fn font_family(&mut self, font: Font) -> PixResult<()> {
376        self.settings.font_family = font;
377        self.renderer.font_family(&self.settings.font_family)
378    }
379
380    /// Sets the text shadow distance used to draw text on the canvas.
381    ///
382    /// # Example
383    ///
384    /// ```
385    /// # use pix_engine::prelude::*;
386    /// # struct App;
387    /// # impl PixEngine for App {
388    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
389    ///     s.text_shadow(2);
390    ///     // Draws a 2-pixel offset shhadow
391    ///     s.text("Shadowed")?;
392    ///     Ok(())
393    /// }
394    /// # }
395    /// ```
396    #[inline]
397    pub fn text_shadow<D>(&mut self, distance: D)
398    where
399        D: Into<Option<u16>>,
400    {
401        self.settings.text_shadow = distance.into();
402    }
403
404    /// Enable or disable the anti-alias option used for drawing shapes on the canvas. `smooth` is
405    /// enabled by default.
406    ///
407    /// # Example
408    ///
409    /// ```
410    /// # use pix_engine::prelude::*;
411    /// # struct App;
412    /// # impl PixEngine for App {
413    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
414    ///     // Draws a anti-aliased diagonal line
415    ///     s.smooth(true);
416    ///     s.line(line_![0, 0, 100, 100])?;
417    ///     // Disables anti-aliasing
418    ///     s.smooth(false);
419    ///     s.line(line_![0, 0, 100, 100])?;
420    ///     Ok(())
421    /// }
422    /// # }
423    /// ```
424    #[inline]
425    pub fn smooth(&mut self, val: bool) {
426        self.settings.smooth = val;
427    }
428
429    /// Set the resolution at which [`PixState::bezier`] curves are displayed. The default is `20`.
430    ///
431    /// # Example
432    ///
433    /// ```
434    /// # use pix_engine::prelude::*;
435    /// # struct App;
436    /// # impl PixEngine for App {
437    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
438    ///     s.bezier_detail(5);
439    ///     s.stroke(Color::RED);
440    ///     s.bezier([[85, 20], [10, 10], [90, 90], [15, 80]])?;
441    ///     Ok(())
442    /// }
443    /// # }
444    /// ```
445    #[inline]
446    pub fn bezier_detail(&mut self, detail: i32) {
447        self.settings.bezier_detail = detail;
448    }
449
450    /// Sets the wrap width used to draw text on the canvas. `None` disables text wrap.
451    ///
452    /// # Example
453    ///
454    /// ```
455    /// # use pix_engine::prelude::*;
456    /// # struct App;
457    /// # impl PixEngine for App {
458    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
459    ///     // Renders as (depending on font width):
460    ///     //
461    ///     // Lorem ipsum
462    ///     // dollor sit amet,
463    ///     // consetetur
464    ///     // sadipscing
465    ///     // elitr, sed diam
466    ///     s.wrap(100);
467    ///     s.text("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam")?;
468    ///
469    ///     // Disable wrapping
470    ///     s.wrap((None));
471    ///     s.text("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam")?;
472    ///     Ok(())
473    /// }
474    /// # }
475    /// ```
476    #[inline]
477    pub fn wrap<W>(&mut self, width: W)
478    where
479        W: Into<Option<u32>>,
480    {
481        self.settings.wrap_width = width.into();
482    }
483
484    /// Sets the clip [Rect] used by the renderer to draw to the current canvas. `None` disables
485    /// clipping.
486    ///
487    /// # Errors
488    ///
489    /// If the current render target is closed or dropped, then an error is returned.
490    ///
491    /// # Example
492    ///
493    /// ```
494    /// # use pix_engine::prelude::*;
495    /// # struct App;
496    /// # impl PixEngine for App {
497    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
498    ///     s.clip(rect![0, 0, 100, 100])?;
499    ///     // Renders a quarter pie-slice with radius 100
500    ///     s.circle([100, 100, 200, 200])?;
501    ///     Ok(())
502    /// }
503    /// # }
504    /// ```
505    #[inline]
506    pub fn clip<R>(&mut self, rect: R) -> PixResult<()>
507    where
508        R: Into<Option<Rect<i32>>>,
509    {
510        self.settings.clip = rect.into();
511        self.renderer.clip(self.settings.clip)
512    }
513
514    /// Set the application to fullscreen or not.
515    ///
516    /// # Errors
517    ///
518    /// If the current render target is closed or dropped or the renderer fails to set
519    /// fullscreen, then an error is returned.
520    ///
521    /// # Example
522    ///
523    /// ```
524    /// # use pix_engine::prelude::*;
525    /// # struct App;
526    /// # impl PixEngine for App {
527    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
528    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
529    ///     if let Key::Return = event.key {
530    ///         s.fullscreen(true)?;
531    ///         return Ok(true);
532    ///     }
533    ///     Ok(false)
534    /// }
535    /// # }
536    /// ```
537    #[inline]
538    pub fn fullscreen(&mut self, val: bool) -> PixResult<()> {
539        self.renderer.set_fullscreen(val)
540    }
541
542    /// Toggle fullscreen.
543    ///
544    /// # Errors
545    ///
546    /// If the current render target is closed or dropped or the renderer fails to toggle
547    /// fullscreen, then an error is returned.
548    ///
549    /// # Example
550    ///
551    /// ```
552    /// # use pix_engine::prelude::*;
553    /// # struct App;
554    /// # impl PixEngine for App {
555    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
556    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
557    ///     if let Key::Return = event.key {
558    ///         s.toggle_fullscreen()?;
559    ///         return Ok(true);
560    ///     }
561    ///     Ok(false)
562    /// }
563    /// # }
564    /// ```
565    #[inline]
566    pub fn toggle_fullscreen(&mut self) -> PixResult<()> {
567        let is_fullscreen = self.renderer.fullscreen()?;
568        self.renderer.set_fullscreen(!is_fullscreen)
569    }
570
571    /// Set the window to synchronize frame rate to the screens refresh rate ([`VSync`]).
572    ///
573    /// # Note
574    ///
575    /// Due to the current limitation with changing `VSync` at runtime, this method creates a new
576    /// window using the properties of the current window and returns the new `WindowId`.
577    ///
578    /// If you are storing and interacting with this window using the `WindowId`, make sure to
579    /// use the newly returned `WindowId`.
580    ///
581    /// [`VSync`]: https://en.wikipedia.org/wiki/Screen_tearing#Vertical_synchronization
582    ///
583    /// # Errors
584    ///
585    /// If the current render target is closed or dropped or the renderer fails to set
586    /// vsync, then an error is returned.
587    ///
588    /// # Example
589    ///
590    /// ```
591    /// # use pix_engine::prelude::*;
592    /// # struct App;
593    /// # impl PixEngine for App {
594    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
595    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
596    ///     if let Key::Return = event.key {
597    ///         s.vsync(true)?;
598    ///         return Ok(true);
599    ///     }
600    ///     Ok(false)
601    /// }
602    /// # }
603    /// ```
604    #[inline]
605    pub fn vsync(&mut self, val: bool) -> PixResult<WindowId> {
606        self.renderer.set_vsync(val)
607    }
608
609    /// Toggle synchronizing frame rate to the screens refresh rate ([`VSync`]).
610    ///
611    /// # Note
612    ///
613    /// Due to the current limitation with changing `VSync` at runtime, this method creates a new
614    /// window using the properties of the current window and returns the new `WindowId`.
615    ///
616    /// If you are storing and interacting with this window using the `WindowId`, make sure to
617    /// use the newly returned `WindowId`.
618    ///
619    /// [`VSync`]: https://en.wikipedia.org/wiki/Screen_tearing#Vertical_synchronization
620    ///
621    /// # Errors
622    ///
623    /// If the current render target is closed or dropped or the renderer fails to toggle
624    /// vsync, then an error is returned.
625    ///
626    /// # Example
627    ///
628    /// ```
629    /// # use pix_engine::prelude::*;
630    /// # struct App;
631    /// # impl PixEngine for App {
632    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
633    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
634    ///     if let Key::Return = event.key {
635    ///         s.toggle_vsync()?;
636    ///         return Ok(true);
637    ///     }
638    ///     Ok(false)
639    /// }
640    /// # }
641    /// ```
642    #[inline]
643    pub fn toggle_vsync(&mut self) -> PixResult<WindowId> {
644        let vsync_enabled = self.renderer.vsync();
645        self.renderer.set_vsync(vsync_enabled)
646    }
647
648    /// Set the mouse cursor to a predefined symbol or image. `None` hides the cursor.
649    ///
650    /// # Errors
651    ///
652    /// If the renderer fails to set the cursor or load it from an image file, then an error is
653    /// returned.
654    ///
655    /// # Example
656    ///
657    /// ```
658    /// # use pix_engine::prelude::*;
659    /// # struct App;
660    /// # impl PixEngine for App {
661    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
662    ///     s.text("Hover me")?;
663    ///     if s.hovered() {
664    ///         s.cursor(Cursor::hand())?;
665    ///     } else {
666    ///         s.cursor(Cursor::arrow())?;
667    ///     }
668    ///     Ok(())
669    /// }
670    /// # }
671    /// ```
672    #[inline]
673    pub fn cursor<C>(&mut self, cursor: C) -> PixResult<()>
674    where
675        C: Into<Option<Cursor>>,
676    {
677        self.settings.cursor = cursor.into();
678        self.renderer.cursor(self.settings.cursor.as_ref())
679    }
680
681    /// Disables any UI elements drawn after this is called, preventing them from being interacted
682    /// with.
683    ///
684    /// # Example
685    ///
686    /// ```
687    /// # use pix_engine::prelude::*;
688    /// # struct App { checkbox: bool };
689    /// # impl PixEngine for App {
690    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
691    ///     if s.button("Disable UI")? {
692    ///         s.disable(true);
693    ///     }
694    ///     s.checkbox("Disabled checkbox", &mut self.checkbox)?;
695    ///     Ok(())
696    /// }
697    /// # }
698    /// ```
699    pub fn disable(&mut self, disabled: bool) {
700        self.settings.disabled = disabled;
701        self.ui.disabled = disabled;
702    }
703
704    /// Whether the render loop is running or not.
705    ///
706    /// # Example
707    ///
708    /// ```
709    /// # use pix_engine::prelude::*;
710    /// # struct App;
711    /// # impl PixEngine for App {
712    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
713    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
714    ///     if let Key::Return = event.key {
715    ///         // Toggle pausing rendering
716    ///         let running = s.running();
717    ///         s.run(!running);
718    ///         return Ok(true);
719    ///     }
720    ///     Ok(false)
721    /// }
722    /// # }
723    /// ```
724    #[inline]
725    #[must_use]
726    pub fn running(&mut self) -> bool {
727        self.settings.running
728    }
729
730    /// Pause or resume the render loop called by [`PixEngine::on_update`].
731    ///
732    /// # Example
733    ///
734    /// ```
735    /// # use pix_engine::prelude::*;
736    /// # struct App;
737    /// # impl PixEngine for App {
738    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
739    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
740    ///     if let Key::Return = event.key {
741    ///         // Toggle rendering
742    ///         let running = s.running();
743    ///         s.run(running);
744    ///         return Ok(true);
745    ///     }
746    ///     Ok(false)
747    /// }
748    /// # }
749    /// ```
750    #[inline]
751    pub fn run(&mut self, val: bool) {
752        self.settings.running = val;
753    }
754
755    /// Set whether to show the current frame rate per second in the title or not.
756    ///
757    /// # Example
758    ///
759    /// ```
760    /// # use pix_engine::prelude::*;
761    /// # struct App;
762    /// # impl PixEngine for App {
763    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
764    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
765    ///     if let Key::Return = event.key {
766    ///         s.show_frame_rate(true);
767    ///         return Ok(true);
768    ///     }
769    ///     Ok(false)
770    /// }
771    /// # }
772    /// ```
773    #[inline]
774    pub fn show_frame_rate(&mut self, show: bool) {
775        self.settings.show_frame_rate = show;
776    }
777
778    /// Get the target frame rate to render at.
779    ///
780    /// # Example
781    ///
782    /// ```
783    /// # use pix_engine::prelude::*;
784    /// # struct App;
785    /// # impl PixEngine for App {
786    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
787    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
788    ///     if let Key::Down = event.key {
789    ///         let target = s.target_frame_rate().unwrap_or(60);
790    ///         s.frame_rate(target - 10);
791    ///         return Ok(true);
792    ///     }
793    ///     Ok(false)
794    /// }
795    /// # }
796    /// ```
797    #[inline]
798    #[must_use]
799    pub fn target_frame_rate(&mut self) -> Option<usize> {
800        self.settings.target_frame_rate
801    }
802
803    /// Set a target frame rate to render at, controls how often [`PixEngine::on_update`] is
804    /// called. `None` clears the target frame rate.
805    ///
806    /// # Example
807    ///
808    /// ```
809    /// # use pix_engine::prelude::*;
810    /// # struct App;
811    /// # impl PixEngine for App {
812    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
813    /// fn on_start(&mut self, s: &mut PixState) -> PixResult<()> {
814    ///     // Target a lower FPS than natively possible
815    ///     s.frame_rate(30);
816    ///     Ok(())
817    /// }
818    /// # }
819    /// ```
820    #[inline]
821    pub fn frame_rate<R>(&mut self, frame_rate: R)
822    where
823        R: Into<Option<usize>>,
824    {
825        let frame_rate = frame_rate.into();
826        self.settings.target_frame_rate = frame_rate;
827        self.settings.target_delta_time =
828            frame_rate.map(|frame_rate| Duration::from_secs(1) / frame_rate as u32);
829    }
830
831    /// Set the rendering scale of the current canvas. Drawing coordinates are scaled by x/y
832    /// factors before being drawn to the canvas.
833    ///
834    /// # Errors
835    ///
836    /// If the current render target is closed or dropped, or `(x, y`) contain invalid values,
837    /// then an error is returned.
838    ///
839    /// # Example
840    ///
841    /// ```
842    /// # use pix_engine::prelude::*;
843    /// # struct App;
844    /// # impl PixEngine for App {
845    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
846    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
847    ///     if let Key::Plus = event.key {
848    ///         s.scale(2.0, 2.0)?;
849    ///         return Ok(true);
850    ///     }
851    ///     Ok(false)
852    /// }
853    /// # }
854    /// ```
855    #[inline]
856    pub fn scale(&mut self, x: f32, y: f32) -> PixResult<()> {
857        let s = &mut self.settings;
858        s.scale_x = x;
859        s.scale_y = y;
860        self.renderer.scale(s.scale_x, s.scale_y)
861    }
862
863    /// Change the way parameters are interpreted for drawing [Square](Rect)s and
864    /// [Rectangle](Rect)s.
865    ///
866    /// # Example
867    ///
868    /// ```
869    /// # use pix_engine::prelude::*;
870    /// # struct App;
871    /// # impl PixEngine for App {
872    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
873    ///     s.rect_mode(RectMode::Center);
874    ///     // Draw rect with center at `(100, 100)`
875    ///     s.rect([100, 100, 50, 50])?;
876    ///     Ok(())
877    /// }
878    /// # }
879    /// ```
880    #[inline]
881    pub fn rect_mode(&mut self, mode: RectMode) {
882        self.settings.rect_mode = mode;
883    }
884
885    /// Change the way parameters are interpreted for drawing [Ellipse]s.
886    ///
887    /// # Example
888    ///
889    /// ```
890    /// # use pix_engine::prelude::*;
891    /// # struct App;
892    /// # impl PixEngine for App {
893    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
894    ///     s.ellipse_mode(EllipseMode::Center);
895    ///     // Draw ellipse with center at `(100, 100)`
896    ///     s.ellipse([100, 100, 50, 50])?;
897    ///     Ok(())
898    /// }
899    /// # }
900    /// ```
901    #[inline]
902    pub fn ellipse_mode(&mut self, mode: EllipseMode) {
903        self.settings.ellipse_mode = mode;
904    }
905
906    /// Change the way parameters are interpreted for drawing [Image]s.
907    ///
908    /// # Example
909    ///
910    /// ```
911    /// # use pix_engine::prelude::*;
912    /// # struct App;
913    /// # impl PixEngine for App {
914    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
915    ///     s.image_mode(ImageMode::Center);
916    ///     // Draw image with center at `(100, 100)`
917    ///     s.image(&Image::from_file("./some_image.png")?, [100, 100])?;
918    ///     Ok(())
919    /// }
920    /// # }
921    /// ```
922    #[inline]
923    pub fn image_mode(&mut self, mode: ImageMode) {
924        self.settings.image_mode = mode;
925    }
926
927    /// Add a color tint to [Image]s when drawing.
928    ///
929    /// # Example
930    ///
931    /// ```
932    /// # use pix_engine::prelude::*;
933    /// # struct App;
934    /// # impl PixEngine for App {
935    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
936    ///     s.image_tint(Color::RED);
937    ///     // Draw image tinted red
938    ///     s.image(&Image::from_file("./some_image.png")?, [0, 0])?;
939    ///     Ok(())
940    /// }
941    /// # }
942    /// ```
943    #[inline]
944    pub fn image_tint<C>(&mut self, tint: C)
945    where
946        C: Into<Option<Color>>,
947    {
948        self.settings.image_tint = tint.into();
949    }
950
951    /// Change the way arcs are drawn.
952    ///
953    /// # Example
954    ///
955    /// ```
956    /// # use pix_engine::prelude::*;
957    /// # struct App;
958    /// # impl PixEngine for App {
959    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
960    ///     // Draw arc as a open, unfilled pie segment using only the `stroke` (The default)
961    ///     s.arc_mode(ArcMode::Default);
962    ///     s.arc([100, 100], 20, 0, 180)?;
963    ///     s.arc_mode(ArcMode::Pie);
964    ///     // Draw arc as a closed pie segment using both `fill` and `stroke`
965    ///     s.arc([200, 200], 20, 0, 180)?;
966    ///     Ok(())
967    /// }
968    /// # }
969    /// ```
970    #[inline]
971    pub fn arc_mode(&mut self, mode: ArcMode) {
972        self.settings.arc_mode = mode;
973    }
974
975    /// Change the way angles are interpreted for rotation and matrix transformations.
976    ///
977    /// # Example
978    ///
979    /// ```
980    /// # use pix_engine::prelude::*;
981    /// # struct App;
982    /// # impl PixEngine for App {
983    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
984    ///     s.angle_mode(AngleMode::Degrees);
985    ///     let angle = 10.0;
986    ///     let center = point!(10, 10);
987    ///     s.text_transformed("Rotated text", angle, center, None)?;
988    ///     Ok(())
989    /// }
990    /// # }
991    /// ```
992    #[inline]
993    pub fn angle_mode(&mut self, mode: AngleMode) {
994        self.settings.angle_mode = mode;
995    }
996
997    /// Change the way textures are blended together.
998    ///
999    /// # Example
1000    ///
1001    /// ```
1002    /// # use pix_engine::prelude::*;
1003    /// # struct App;
1004    /// # impl PixEngine for App {
1005    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
1006    ///     s.blend_mode(BlendMode::Blend);
1007    ///     // Draw image with alpha blended with background
1008    ///     s.image(&Image::from_file("./some_image.png")?, [0, 0])?;
1009    ///     Ok(())
1010    /// }
1011    /// # }
1012    /// ```
1013    #[inline]
1014    pub fn blend_mode(&mut self, mode: BlendMode) {
1015        self.settings.blend_mode = mode;
1016        self.renderer.blend_mode(mode);
1017    }
1018
1019    /// Saves the current draw settings and transforms.
1020    ///
1021    /// # Example
1022    ///
1023    /// ```
1024    /// # use pix_engine::prelude::*;
1025    /// # struct App;
1026    /// # impl PixEngine for App {
1027    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
1028    ///     s.fill(Color::BLUE);
1029    ///     s.stroke(Color::WHITE);
1030    ///
1031    ///     s.push(); // Save settings
1032    ///
1033    ///     s.fill(Color::RED);
1034    ///     s.stroke(Color::BLACK);
1035    ///     s.rect([0, 0, 100, 100])?;
1036    ///
1037    ///     s.pop(); // Restore settings
1038    ///
1039    ///     // Rectangle is blue with a white outline
1040    ///     s.rect([0, 0, 100, 100])?;
1041    ///     Ok(())
1042    /// }
1043    /// # }
1044    #[inline]
1045    pub fn push(&mut self) {
1046        self.setting_stack.push(self.settings.clone());
1047    }
1048
1049    /// Restores the previous draw settings and transforms, if present. If the settings stack is
1050    /// empty, the settings will remain unchanged.
1051    ///
1052    /// # Example
1053    ///
1054    /// ```
1055    /// # use pix_engine::prelude::*;
1056    /// # struct App;
1057    /// # impl PixEngine for App {
1058    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
1059    ///     s.fill(Color::BLUE);
1060    ///     s.stroke(Color::WHITE);
1061    ///
1062    ///     s.push(); // Save settings
1063    ///
1064    ///     s.fill(Color::RED);
1065    ///     s.stroke(Color::BLACK);
1066    ///     s.rect([0, 0, 100, 100])?;
1067    ///
1068    ///     s.pop(); // Restore settings
1069    ///
1070    ///     // Rectangle is blue with a white outline
1071    ///     s.rect([0, 0, 100, 100])?;
1072    ///     Ok(())
1073    /// }
1074    /// # }
1075    #[inline]
1076    pub fn pop(&mut self) {
1077        if let Some(settings) = self.setting_stack.pop() {
1078            self.settings = settings;
1079            self.ui.disabled = self.settings.disabled;
1080        }
1081        let s = &self.settings;
1082        // All of these settings should be valid since they were set prior to `pop()` being
1083        // called.
1084        let _ = self.renderer.clip(s.clip);
1085        // Excluding restoring cursor - as it's used for mouse hover.
1086        let _ = self.renderer.font_size(s.font_size);
1087        self.renderer.font_style(s.font_style);
1088        let _ = self.renderer.font_family(&s.font_family);
1089        self.renderer.blend_mode(s.blend_mode);
1090    }
1091}
1092
1093impl PixState {
1094    /// Set the mouse cursor to a predefined symbol or image for a single frame.
1095    ///
1096    /// Cursor will get reset to the current setting next frame.
1097    #[inline]
1098    pub(crate) fn frame_cursor(&mut self, cursor: &Cursor) -> PixResult<()> {
1099        self.renderer.cursor(Some(cursor))
1100    }
1101
1102    /// Get the target delta time between frames.
1103    #[inline]
1104    pub(crate) fn target_delta_time(&self) -> Option<Duration> {
1105        self.settings.target_delta_time
1106    }
1107
1108    /// Get whether `VSync` is enabled.
1109    #[inline]
1110    pub(crate) fn vsync_enabled(&self) -> bool {
1111        self.renderer.vsync()
1112    }
1113}