pix_engine/
window.rs

1//! `Window` methods.
2//!
3//! Provides window creation and manipulation methods on [`PixState`].
4//!
5//! Provided methods:
6//!
7//! - [`PixState::window_id`]: Get current window target ID.
8//! - [`PixState::window`]: Create a [`WindowBuilder`] to open a new window.
9//! - [`PixState::close_window`]: Close a window by ID.
10//! - [`PixState::dimensions`]: Get the current render target (window or texture) dimensions as
11//!   `(width, height)`.
12//! - [`PixState::window_dimensions`]: Get the current window target dimensions as `(width, height)`.
13//! - [`PixState::set_window_dimensions`]: Set the current window target dimensions.
14//! - [`PixState::viewport`]: Get the current render target (window or texture) viewport.
15//! - [`PixState::set_viewport`]: Set the current render target (window or texture) viewport.
16//! - [`PixState::clear_viewport`]: Clear the current render target (window or texture) viewport
17//!   back to the entire render size.
18//! - [`PixState::width`]: Get the current render target (window or texture) width.
19//! - [`PixState::window_width`]: Get the current window target width.
20//! - [`PixState::set_window_width`]: Set the current window target width.
21//! - [`PixState::height`]: Get the current render target (window or texture) height.
22//! - [`PixState::window_height`]: Get the current window target height.
23//! - [`PixState::set_window_height`]: Set the current window target height.
24//! - [`PixState::center`]: Get the current render target (window or texture) center.
25//! - [`PixState::window_center`]: Get the current window target center.
26//! - [`PixState::display_dimensions`]: Get the primary display dimensions as `(width, height)`.
27//! - [`PixState::display_width`]: Get the primary display width.
28//! - [`PixState::display_height`]: Get the primary display height.
29//! - [`PixState::show_window`]: Show the current window target if it is hidden.
30//! - [`PixState::hide_window`]: Hide the current window target if it is shown.
31//! - [`PixState::set_window_target`]: Set a window as the primary target for drawing operations.
32//! - [`PixState::reset_window_target`]: Reset window target back to the primary window for drawing
33//!   operations.
34
35use crate::{
36    image::Icon,
37    ops::clamp_dimensions,
38    prelude::*,
39    renderer::{Renderer, RendererSettings},
40};
41#[cfg(feature = "serde")]
42use serde::{Deserialize, Serialize};
43#[cfg(not(target_arch = "wasm32"))]
44use std::path::PathBuf;
45use std::{
46    fmt,
47    ops::{Deref, DerefMut},
48};
49
50/// Represents a possible screen position.
51#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53pub enum Position {
54    /// A positioned `(x, y)` coordinate.
55    Positioned(i32),
56    /// A coordinate placed in the center of the display.
57    Centered,
58}
59
60impl Default for Position {
61    fn default() -> Self {
62        Self::Centered
63    }
64}
65
66/// Window Identifier.
67#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
68pub struct WindowId(pub(crate) u32);
69
70impl fmt::Display for WindowId {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        write!(f, "{}", self.0)
73    }
74}
75
76impl Deref for WindowId {
77    type Target = u32;
78    fn deref(&self) -> &Self::Target {
79        &self.0
80    }
81}
82
83impl DerefMut for WindowId {
84    fn deref_mut(&mut self) -> &mut Self::Target {
85        &mut self.0
86    }
87}
88
89/// A window cursor indicating the position of the mouse.
90#[non_exhaustive]
91#[derive(Debug, Clone, Eq, PartialEq, Hash)]
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93#[cfg_attr(target_arch = "wasm32", derive(Copy))]
94#[allow(variant_size_differences)]
95pub enum Cursor {
96    /// A system supported cursor. e.g. Arrow, Hand, etc.
97    System(SystemCursor),
98    #[cfg(not(target_arch = "wasm32"))]
99    /// A custom cursor from a image path starting at `(x, y)`.
100    Image(PathBuf, (i32, i32)),
101}
102
103impl Default for Cursor {
104    fn default() -> Self {
105        Self::System(SystemCursor::Arrow)
106    }
107}
108
109impl Cursor {
110    /// Constructs a `Cursor` from a file path.
111    #[inline]
112    #[cfg(not(target_arch = "wasm32"))]
113    pub fn new<P: Into<PathBuf>>(path: P, x: i32, y: i32) -> Self {
114        Self::Image(path.into(), (x, y))
115    }
116
117    /// Constructs a `Cursor` with `SystemCursor::Arrow`.
118    #[inline]
119    #[must_use]
120    pub const fn arrow() -> Self {
121        Self::System(SystemCursor::Arrow)
122    }
123
124    /// Constructs a `Cursor` with `SystemCursor::IBeam`.
125    #[inline]
126    #[must_use]
127    pub const fn ibeam() -> Self {
128        Self::System(SystemCursor::IBeam)
129    }
130
131    /// Constructs a `Cursor` with `SystemCursor::No`.
132    #[inline]
133    #[must_use]
134    pub const fn no() -> Self {
135        Self::System(SystemCursor::No)
136    }
137
138    /// Constructs a `Cursor` with `SystemCursor::Hand`.
139    #[inline]
140    #[must_use]
141    pub const fn hand() -> Self {
142        Self::System(SystemCursor::Hand)
143    }
144}
145
146/// System Cursor Icon.
147#[non_exhaustive]
148#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
150pub enum SystemCursor {
151    /// Default arrow cursor.
152    Arrow,
153    /// Vertical I-Beam icon, typically used for text input position.
154    IBeam,
155    /// Wait hour-glass icon, typically used as a loading indicator.
156    Wait,
157    /// Cross-hair icon.
158    Crosshair,
159    /// Wait hour-glass + Arrow combined.
160    WaitArrow,
161    /// Resize icon with arrows oriented North-West to South-East.
162    SizeNWSE,
163    /// Resize icon with arrows oriented North-East to South-West.
164    SizeNESW,
165    /// Resize icon with arrows oriented West to East.
166    SizeWE,
167    /// Resize icon with arrows oriented North to South.
168    SizeNS,
169    /// Resize icon with arrows in all cardinal directions.
170    SizeAll,
171    /// Circle with a line through it.
172    No,
173    /// Hand icon, typically used as a clickable indicator.
174    Hand,
175}
176
177/// Trait representing window operations.
178pub(crate) trait WindowRenderer {
179    /// Get the count of open windows.
180    fn window_count(&self) -> usize;
181
182    /// Get the primary window ID.
183    fn primary_window_id(&self) -> WindowId;
184
185    /// Get the current window target ID.
186    fn window_id(&self) -> WindowId;
187
188    /// Create a new window.
189    fn create_window(&mut self, s: &mut RendererSettings) -> PixResult<WindowId>;
190
191    /// Close a window.
192    fn close_window(&mut self, id: WindowId) -> PixResult<()>;
193
194    /// Set the mouse cursor to a predefined symbol or image, or hides cursor if `None`.
195    fn cursor(&mut self, cursor: Option<&Cursor>) -> PixResult<()>;
196
197    /// Returns a single event or None if the event pump is empty.
198    fn poll_event(&mut self) -> Option<Event>;
199
200    /// Get the current window title.
201    fn title(&self) -> &str;
202
203    /// Set the current window title.
204    fn set_title(&mut self, title: &str) -> PixResult<()>;
205
206    /// Set the average frames-per-second rendered.
207    fn set_fps(&mut self, fps: f32) -> PixResult<()>;
208
209    /// Dimensions of the current render target as `(width, height)`.
210    fn dimensions(&self) -> PixResult<(u32, u32)>;
211
212    /// Dimensions of the current window target as `(width, height)`.
213    fn window_dimensions(&self) -> PixResult<(u32, u32)>;
214
215    /// Position of the current window target as `(x, y)`.
216    fn window_position(&self) -> PixResult<(i32, i32)>;
217
218    /// Set dimensions of the current window target as `(width, height)`.
219    fn set_window_dimensions(&mut self, dimensions: (u32, u32)) -> PixResult<()>;
220
221    /// Returns the rendering viewport of the current render target.
222    fn viewport(&self) -> PixResult<Rect<i32>>;
223
224    /// Set the rendering viewport of the current render target.
225    fn set_viewport(&mut self, rect: Option<Rect<i32>>) -> PixResult<()>;
226
227    /// Dimensions of the primary display as `(width, height)`.
228    fn display_dimensions(&self) -> PixResult<(u32, u32)>;
229
230    /// Returns whether the application is fullscreen or not.
231    fn fullscreen(&self) -> PixResult<bool>;
232
233    /// Set the application to fullscreen or not.
234    fn set_fullscreen(&mut self, val: bool) -> PixResult<()>;
235
236    /// Returns whether the window synchronizes frame rate to the screens refresh rate.
237    fn vsync(&self) -> bool;
238
239    /// Set the window to synchronize frame rate to the screens refresh rate.
240    ///
241    /// # Note
242    ///
243    /// Due to the current limitation with changing `VSync` at runtime, this method creates a new
244    /// window using the properties of the current window and returns the new `WindowId`.
245    ///
246    /// If you are storing and interacting with this window using the `WindowId`, make sure to
247    /// use the newly returned `WindowId`.
248    fn set_vsync(&mut self, val: bool) -> PixResult<WindowId>;
249
250    /// Set window as the target for drawing operations.
251    fn set_window_target(&mut self, id: WindowId) -> PixResult<()>;
252
253    /// Reset main window as the target for drawing operations.
254    fn reset_window_target(&mut self);
255
256    /// Show the current window target.
257    fn show(&mut self) -> PixResult<()>;
258
259    /// Hide the current window target.
260    fn hide(&mut self) -> PixResult<()>;
261}
262
263/// Opens a new window by providing several window configuration functions.
264///
265/// In addition to the primary window created for you when calling [`Engine::run`], you can open
266/// additional windows with various configurations and render to them using the
267/// [`PixState::set_window_target`] method.
268///
269/// # Example
270///
271/// ```
272/// # use pix_engine::prelude::*;
273/// # struct App { windows: Vec<WindowId> };
274/// # impl PixEngine for App {
275/// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
276/// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
277///     if let Key::O = event.key {
278///         let window_id = s.window()
279///             .title("New Window")
280///             .dimensions(800, 600)
281///             .position(10, 10)
282///             .resizable()
283///             .borderless()
284///             .build()?;
285///         self.windows.push(window_id);
286///         return Ok(true);
287///     }
288///     Ok(false)
289/// }
290/// # }
291/// ```
292#[must_use]
293#[derive(Debug)]
294pub struct WindowBuilder<'a> {
295    renderer: &'a mut Renderer,
296    settings: RendererSettings,
297}
298
299impl<'a> WindowBuilder<'a> {
300    /// Creates a new `WindowBuilder` instance.
301    #[inline]
302    pub(crate) fn new(renderer: &'a mut Renderer) -> Self {
303        let vsync = renderer.vsync();
304        Self {
305            renderer,
306            settings: RendererSettings {
307                vsync,
308                ..RendererSettings::default()
309            },
310        }
311    }
312
313    /// Set window dimensions.
314    #[inline]
315    pub fn dimensions(&mut self, width: u32, height: u32) -> &mut Self {
316        self.settings.width = width;
317        self.settings.height = height;
318        self
319    }
320
321    /// Set a window title.
322    #[inline]
323    pub fn title<S: Into<String>>(&mut self, title: S) -> &mut Self {
324        self.settings.title = title.into();
325        self
326    }
327
328    /// Position the window at the given `(x, y)` coordinates of the display.
329    #[inline]
330    pub fn position(&mut self, x: i32, y: i32) -> &mut Self {
331        self.settings.x = Position::Positioned(x);
332        self.settings.y = Position::Positioned(y);
333        self
334    }
335
336    /// Position the window in the center of the display.
337    #[inline]
338    pub fn position_centered(&mut self) -> &mut Self {
339        self.settings.x = Position::Centered;
340        self.settings.y = Position::Centered;
341        self
342    }
343
344    /// Start window in fullscreen mode.
345    #[inline]
346    pub fn fullscreen(&mut self) -> &mut Self {
347        self.settings.fullscreen = true;
348        self
349    }
350
351    /// Allow window resizing.
352    #[inline]
353    pub fn resizable(&mut self) -> &mut Self {
354        self.settings.resizable = true;
355        self
356    }
357
358    #[inline]
359    /// Removes the window decoration.
360    pub fn borderless(&mut self) -> &mut Self {
361        self.settings.borderless = true;
362        self
363    }
364
365    /// Set a window icon.
366    #[inline]
367    pub fn icon<I>(&mut self, icon: I) -> &mut Self
368    where
369        I: Into<Icon>,
370    {
371        self.settings.icon = Some(icon.into());
372        self
373    }
374
375    /// Create a new window from the `WindowBuilder` and return its id.
376    ///
377    /// # Errors
378    ///
379    /// If the renderer fails to create a new window, then an error is returned.
380    ///
381    /// Possible errors include the title containing a `nul` character, the position or dimensions
382    /// being invalid values or overlowing and an internal renderer error such as running out of
383    /// memory or a software driver issue.
384    pub fn build(&mut self) -> PixResult<WindowId> {
385        self.renderer.create_window(&mut self.settings)
386    }
387}
388
389impl PixState {
390    /// Get the current window target ID.
391    #[inline]
392    #[must_use]
393    pub fn window_id(&self) -> WindowId {
394        self.renderer.window_id()
395    }
396
397    /// Create a new [`WindowBuilder`].
398    #[inline]
399    pub fn window(&mut self) -> WindowBuilder<'_> {
400        WindowBuilder::new(&mut self.renderer)
401    }
402
403    /// Close a window.
404    ///
405    /// # Errors
406    ///
407    /// If the window has already been closed or is invalid, then an error is returned.
408    #[inline]
409    pub fn close_window(&mut self, id: WindowId) -> PixResult<()> {
410        if id == self.renderer.primary_window_id() || self.renderer.window_count() == 1 {
411            self.quit();
412            return Ok(());
413        }
414        self.renderer.close_window(id)
415    }
416
417    /// The dimensions of the current render target (window or texture) as `(width, height)`.
418    ///
419    /// # Errors
420    ///
421    /// If the window has been closed or is invalid, then an error is returned.
422    #[inline]
423    pub fn dimensions(&self) -> PixResult<(u32, u32)> {
424        self.renderer.dimensions()
425    }
426
427    /// The dimensions of the current window target as `(width, height)`.
428    ///
429    /// # Errors
430    ///
431    /// If the window has been closed or is invalid, then an error is returned.
432    #[inline]
433    pub fn window_dimensions(&self) -> PixResult<(u32, u32)> {
434        self.renderer.window_dimensions()
435    }
436
437    /// Set the dimensions of the current window target from `(width, height)`.
438    ///
439    /// # Errors
440    ///
441    /// If the window has been closed or is invalid, then an error is returned.
442    #[inline]
443    pub fn set_window_dimensions(&mut self, dimensions: (u32, u32)) -> PixResult<()> {
444        self.renderer.set_window_dimensions(dimensions)
445    }
446
447    /// The position of the current window target as `(x, y)`.
448    ///
449    /// # Errors
450    ///
451    /// If the window has been closed or is invalid, then an error is returned.
452    #[inline]
453    pub fn window_position(&self) -> PixResult<(i32, i32)> {
454        self.renderer.window_position()
455    }
456
457    /// Returns the rendering viewport of the current render target.
458    ///
459    /// # Errors
460    ///
461    /// If the window has been closed or is invalid, then an error is returned.
462    #[inline]
463    pub fn viewport(&mut self) -> PixResult<Rect<i32>> {
464        self.renderer.viewport()
465    }
466
467    /// Set the rendering viewport of the current render target.
468    ///
469    /// # Errors
470    ///
471    /// If the window has been closed or is invalid, then an error is returned.
472    #[inline]
473    pub fn set_viewport<R>(&mut self, rect: R) -> PixResult<()>
474    where
475        R: Into<Rect<i32>>,
476    {
477        self.renderer.set_viewport(Some(rect.into()))
478    }
479
480    /// Clears the rendering viewport of the current render target back to the entire target.
481    ///
482    /// # Errors
483    ///
484    /// If the window has been closed or is invalid, then an error is returned.
485    #[inline]
486    pub fn clear_viewport(&mut self) -> PixResult<()> {
487        self.renderer.set_viewport(None)
488    }
489
490    /// The width of the current render target.
491    ///
492    /// # Errors
493    ///
494    /// If the window has been closed or is invalid, then an error is returned.
495    #[inline]
496    pub fn width(&self) -> PixResult<u32> {
497        let (width, _) = self.dimensions()?;
498        Ok(width)
499    }
500
501    /// The width of the current window.
502    ///
503    /// # Errors
504    ///
505    /// If the window has been closed or is invalid, then an error is returned.
506    #[inline]
507    pub fn window_width(&self) -> PixResult<u32> {
508        let (width, _) = self.window_dimensions()?;
509        Ok(width)
510    }
511
512    /// Set the width of the current window.
513    ///
514    /// # Errors
515    ///
516    /// If the window has been closed or is invalid, then an error is returned.
517    #[inline]
518    pub fn set_window_width(&mut self, width: u32) -> PixResult<()> {
519        let (_, height) = self.window_dimensions()?;
520        self.renderer.set_window_dimensions((width, height))
521    }
522
523    /// The height of the current render target.
524    ///
525    /// # Errors
526    ///
527    /// If the window has been closed or is invalid, then an error is returned.
528    #[inline]
529    pub fn height(&self) -> PixResult<u32> {
530        let (_, height) = self.dimensions()?;
531        Ok(height)
532    }
533
534    /// The height of the current window.
535    ///
536    /// # Errors
537    ///
538    /// If the window has been closed or is invalid, then an error is returned.
539    #[inline]
540    pub fn window_height(&self) -> PixResult<u32> {
541        let (_, height) = self.window_dimensions()?;
542        Ok(height)
543    }
544
545    /// Set the height of the current window.
546    ///
547    /// # Errors
548    ///
549    /// If the window has been closed or is invalid, then an error is returned.
550    #[inline]
551    pub fn set_window_height(&mut self, height: u32) -> PixResult<()> {
552        let (width, _) = self.window_dimensions()?;
553        self.renderer.set_window_dimensions((width, height))
554    }
555
556    /// The x of the current window.
557    ///
558    /// # Errors
559    ///
560    /// If the window has been closed or is invalid, then an error is returned.
561    #[inline]
562    pub fn window_x(&self) -> PixResult<i32> {
563        let (x, _) = self.window_position()?;
564        Ok(x)
565    }
566
567    /// The y of the current window.
568    ///
569    /// # Errors
570    ///
571    /// If the window has been closed or is invalid, then an error is returned.
572    #[inline]
573    pub fn window_y(&self) -> PixResult<i32> {
574        let (_, y) = self.window_position()?;
575        Ok(y)
576    }
577
578    /// The center [Point] of the current render target.
579    ///
580    /// # Errors
581    ///
582    /// If the window has been closed or is invalid, then an error is returned.
583    #[inline]
584    pub fn center(&self) -> PixResult<Point<i32>> {
585        let (width, height) = self.dimensions()?;
586        let (width, height) = clamp_dimensions(width, height);
587        Ok(point![width / 2, height / 2])
588    }
589
590    /// The center [Point] of the current window.
591    ///
592    /// # Errors
593    ///
594    /// If the window has been closed or is invalid, then an error is returned.
595    #[inline]
596    pub fn window_center(&self) -> PixResult<Point<i32>> {
597        let (width, height) = self.window_dimensions()?;
598        let (width, height) = clamp_dimensions(width, height);
599        Ok(point![width / 2, height / 2])
600    }
601
602    /// The dimensions of the primary display as `(width, height)`.
603    ///
604    /// # Errors
605    ///
606    /// If the window has been closed or is invalid, then an error is returned.
607    #[inline]
608    pub fn display_dimensions(&self) -> PixResult<(u32, u32)> {
609        self.renderer.display_dimensions()
610    }
611
612    /// The width of the primary display.
613    ///
614    /// # Errors
615    ///
616    /// If the window has been closed or is invalid, then an error is returned.
617    #[inline]
618    pub fn display_width(&self) -> PixResult<u32> {
619        let (width, _) = self.display_dimensions()?;
620        Ok(width)
621    }
622
623    /// The height of the primary display.
624    ///
625    /// # Errors
626    ///
627    /// If the window has been closed or is invalid, then an error is returned.
628    #[inline]
629    pub fn display_height(&self) -> PixResult<u32> {
630        let (_, height) = self.display_dimensions()?;
631        Ok(height)
632    }
633
634    /// Show the current window target if it is hidden.
635    ///
636    /// # Errors
637    ///
638    /// If the window has been closed or is invalid, then an error is returned.
639    #[inline]
640    pub fn show_window(&mut self) -> PixResult<()> {
641        self.renderer.show()
642    }
643
644    /// Hide the current window target if it is shown.
645    ///
646    /// # Errors
647    ///
648    /// If the window has been closed or is invalid, then an error is returned.
649    #[inline]
650    pub fn hide_window(&mut self) -> PixResult<()> {
651        self.renderer.hide()
652    }
653
654    /// Set a `Window` as the primary target for drawing operations. Pushes current settings and UI
655    /// cursor to the stack, so any changes made while a window target is set will be in effect
656    /// until [`PixState::reset_window_target`] is called.
657    ///
658    /// # Errors
659    ///
660    /// If the window has been closed or is invalid, then an error is returned.
661    pub fn set_window_target(&mut self, id: WindowId) -> PixResult<()> {
662        if id != self.renderer.primary_window_id() {
663            self.push();
664            self.ui.push_cursor();
665            self.set_cursor_pos(self.theme.spacing.frame_pad);
666            self.renderer.set_window_target(id)
667        } else {
668            Ok(())
669        }
670    }
671
672    /// Reset `Window` target back to the primary window for drawing operations. Pops previous
673    /// settings and UI cursor off the stack, so that changes made while window target was set are
674    /// reverted.
675    pub fn reset_window_target(&mut self) {
676        if self.window_id() != self.renderer.primary_window_id() {
677            self.renderer.reset_window_target();
678            self.ui.pop_cursor();
679            self.pop();
680        }
681    }
682}