use crate::{
image::Icon,
ops::clamp_dimensions,
prelude::*,
renderer::{Renderer, RendererSettings},
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
use std::{
fmt,
ops::{Deref, DerefMut},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Position {
Positioned(i32),
Centered,
}
impl Default for Position {
fn default() -> Self {
Self::Centered
}
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct WindowId(pub(crate) u32);
impl fmt::Display for WindowId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for WindowId {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for WindowId {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(target_arch = "wasm32", derive(Copy))]
#[allow(variant_size_differences)]
pub enum Cursor {
System(SystemCursor),
#[cfg(not(target_arch = "wasm32"))]
Image(PathBuf, (i32, i32)),
}
impl Default for Cursor {
fn default() -> Self {
Self::System(SystemCursor::Arrow)
}
}
impl Cursor {
#[inline]
#[cfg(not(target_arch = "wasm32"))]
pub fn new<P: Into<PathBuf>>(path: P, x: i32, y: i32) -> Self {
Self::Image(path.into(), (x, y))
}
#[inline]
#[must_use]
pub const fn arrow() -> Self {
Self::System(SystemCursor::Arrow)
}
#[inline]
#[must_use]
pub const fn ibeam() -> Self {
Self::System(SystemCursor::IBeam)
}
#[inline]
#[must_use]
pub const fn no() -> Self {
Self::System(SystemCursor::No)
}
#[inline]
#[must_use]
pub const fn hand() -> Self {
Self::System(SystemCursor::Hand)
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SystemCursor {
Arrow,
IBeam,
Wait,
Crosshair,
WaitArrow,
SizeNWSE,
SizeNESW,
SizeWE,
SizeNS,
SizeAll,
No,
Hand,
}
pub(crate) trait WindowRenderer {
fn window_count(&self) -> usize;
fn primary_window_id(&self) -> WindowId;
fn window_id(&self) -> WindowId;
fn create_window(&mut self, s: &mut RendererSettings) -> PixResult<WindowId>;
fn close_window(&mut self, id: WindowId) -> PixResult<()>;
fn cursor(&mut self, cursor: Option<&Cursor>) -> PixResult<()>;
fn poll_event(&mut self) -> Option<Event>;
fn title(&self) -> &str;
fn set_title(&mut self, title: &str) -> PixResult<()>;
fn set_fps(&mut self, fps: f32) -> PixResult<()>;
fn dimensions(&self) -> PixResult<(u32, u32)>;
fn window_dimensions(&self) -> PixResult<(u32, u32)>;
fn window_position(&self) -> PixResult<(i32, i32)>;
fn set_window_dimensions(&mut self, dimensions: (u32, u32)) -> PixResult<()>;
fn viewport(&self) -> PixResult<Rect<i32>>;
fn set_viewport(&mut self, rect: Option<Rect<i32>>) -> PixResult<()>;
fn display_dimensions(&self) -> PixResult<(u32, u32)>;
fn fullscreen(&self) -> PixResult<bool>;
fn set_fullscreen(&mut self, val: bool) -> PixResult<()>;
fn vsync(&self) -> bool;
fn set_vsync(&mut self, val: bool) -> PixResult<WindowId>;
fn set_window_target(&mut self, id: WindowId) -> PixResult<()>;
fn reset_window_target(&mut self);
fn show(&mut self) -> PixResult<()>;
fn hide(&mut self) -> PixResult<()>;
}
#[must_use]
#[derive(Debug)]
pub struct WindowBuilder<'a> {
renderer: &'a mut Renderer,
settings: RendererSettings,
}
impl<'a> WindowBuilder<'a> {
#[inline]
pub(crate) fn new(renderer: &'a mut Renderer) -> Self {
let vsync = renderer.vsync();
Self {
renderer,
settings: RendererSettings {
vsync,
..RendererSettings::default()
},
}
}
#[inline]
pub fn dimensions(&mut self, width: u32, height: u32) -> &mut Self {
self.settings.width = width;
self.settings.height = height;
self
}
#[inline]
pub fn title<S: Into<String>>(&mut self, title: S) -> &mut Self {
self.settings.title = title.into();
self
}
#[inline]
pub fn position(&mut self, x: i32, y: i32) -> &mut Self {
self.settings.x = Position::Positioned(x);
self.settings.y = Position::Positioned(y);
self
}
#[inline]
pub fn position_centered(&mut self) -> &mut Self {
self.settings.x = Position::Centered;
self.settings.y = Position::Centered;
self
}
#[inline]
pub fn fullscreen(&mut self) -> &mut Self {
self.settings.fullscreen = true;
self
}
#[inline]
pub fn resizable(&mut self) -> &mut Self {
self.settings.resizable = true;
self
}
#[inline]
pub fn borderless(&mut self) -> &mut Self {
self.settings.borderless = true;
self
}
#[inline]
pub fn icon<I>(&mut self, icon: I) -> &mut Self
where
I: Into<Icon>,
{
self.settings.icon = Some(icon.into());
self
}
pub fn build(&mut self) -> PixResult<WindowId> {
self.renderer.create_window(&mut self.settings)
}
}
impl PixState {
#[inline]
#[must_use]
pub fn window_id(&self) -> WindowId {
self.renderer.window_id()
}
#[inline]
pub fn window(&mut self) -> WindowBuilder<'_> {
WindowBuilder::new(&mut self.renderer)
}
#[inline]
pub fn close_window(&mut self, id: WindowId) -> PixResult<()> {
if id == self.renderer.primary_window_id() || self.renderer.window_count() == 1 {
self.quit();
return Ok(());
}
self.renderer.close_window(id)
}
#[inline]
pub fn dimensions(&self) -> PixResult<(u32, u32)> {
self.renderer.dimensions()
}
#[inline]
pub fn window_dimensions(&self) -> PixResult<(u32, u32)> {
self.renderer.window_dimensions()
}
#[inline]
pub fn set_window_dimensions(&mut self, dimensions: (u32, u32)) -> PixResult<()> {
self.renderer.set_window_dimensions(dimensions)
}
#[inline]
pub fn window_position(&self) -> PixResult<(i32, i32)> {
self.renderer.window_position()
}
#[inline]
pub fn viewport(&mut self) -> PixResult<Rect<i32>> {
self.renderer.viewport()
}
#[inline]
pub fn set_viewport<R>(&mut self, rect: R) -> PixResult<()>
where
R: Into<Rect<i32>>,
{
self.renderer.set_viewport(Some(rect.into()))
}
#[inline]
pub fn clear_viewport(&mut self) -> PixResult<()> {
self.renderer.set_viewport(None)
}
#[inline]
pub fn width(&self) -> PixResult<u32> {
let (width, _) = self.dimensions()?;
Ok(width)
}
#[inline]
pub fn window_width(&self) -> PixResult<u32> {
let (width, _) = self.window_dimensions()?;
Ok(width)
}
#[inline]
pub fn set_window_width(&mut self, width: u32) -> PixResult<()> {
let (_, height) = self.window_dimensions()?;
self.renderer.set_window_dimensions((width, height))
}
#[inline]
pub fn height(&self) -> PixResult<u32> {
let (_, height) = self.dimensions()?;
Ok(height)
}
#[inline]
pub fn window_height(&self) -> PixResult<u32> {
let (_, height) = self.window_dimensions()?;
Ok(height)
}
#[inline]
pub fn set_window_height(&mut self, height: u32) -> PixResult<()> {
let (width, _) = self.window_dimensions()?;
self.renderer.set_window_dimensions((width, height))
}
#[inline]
pub fn window_x(&self) -> PixResult<i32> {
let (x, _) = self.window_position()?;
Ok(x)
}
#[inline]
pub fn window_y(&self) -> PixResult<i32> {
let (_, y) = self.window_position()?;
Ok(y)
}
#[inline]
pub fn center(&self) -> PixResult<Point<i32>> {
let (width, height) = self.dimensions()?;
let (width, height) = clamp_dimensions(width, height);
Ok(point![width / 2, height / 2])
}
#[inline]
pub fn window_center(&self) -> PixResult<Point<i32>> {
let (width, height) = self.window_dimensions()?;
let (width, height) = clamp_dimensions(width, height);
Ok(point![width / 2, height / 2])
}
#[inline]
pub fn display_dimensions(&self) -> PixResult<(u32, u32)> {
self.renderer.display_dimensions()
}
#[inline]
pub fn display_width(&self) -> PixResult<u32> {
let (width, _) = self.display_dimensions()?;
Ok(width)
}
#[inline]
pub fn display_height(&self) -> PixResult<u32> {
let (_, height) = self.display_dimensions()?;
Ok(height)
}
#[inline]
pub fn show_window(&mut self) -> PixResult<()> {
self.renderer.show()
}
#[inline]
pub fn hide_window(&mut self) -> PixResult<()> {
self.renderer.hide()
}
pub fn set_window_target(&mut self, id: WindowId) -> PixResult<()> {
if id != self.renderer.primary_window_id() {
self.push();
self.ui.push_cursor();
self.set_cursor_pos(self.theme.spacing.frame_pad);
self.renderer.set_window_target(id)
} else {
Ok(())
}
}
pub fn reset_window_target(&mut self) {
if self.window_id() != self.renderer.primary_window_id() {
self.renderer.reset_window_target();
self.ui.pop_cursor();
self.pop();
}
}
}