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}