use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use winit::window::{Cursor, Fullscreen, Window as WinitWindow};
use crate::math::{Vec2I, Vec2U};
use super::{CursorIcon, DisplayMode, Monitor, VideoMode};
#[derive(Clone)]
pub struct Window(pub(crate) Arc<WinitWindow>);
impl Debug for Window {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Window").finish_non_exhaustive()
}
}
impl Window {
#[inline]
pub fn title(&self) -> String {
self.0.title()
}
#[inline]
pub fn set_title(&self, title: &str) {
self.0.set_title(title);
}
#[inline]
pub fn scale_factor(&self) -> f32 {
self.0.scale_factor() as f32
}
#[inline]
pub fn inv_scale_factor(&self) -> f32 {
self.scale_factor().recip()
}
#[inline]
pub fn monitor(&self) -> Option<Monitor> {
self.0.current_monitor().map(Monitor)
}
#[inline]
pub fn primary_monitor(&self) -> Option<Monitor> {
self.0.primary_monitor().map(Monitor)
}
#[inline]
pub fn monitors(&self) -> impl Iterator<Item = Monitor> {
self.0.available_monitors().map(Monitor)
}
#[inline]
pub fn center_on(&self, monitor: &Monitor) {
let mon_pos = monitor.0.position();
let mon_size = monitor.0.size();
let win_size = self.0.outer_size();
self.0.set_outer_position(PhysicalPosition::new(
mon_pos.x + ((mon_size.width - win_size.width) / 2) as i32,
mon_pos.y + ((mon_size.height - win_size.height) / 2) as i32,
));
}
#[inline]
pub fn display_mode(&self) -> DisplayMode {
match self.0.fullscreen() {
Some(Fullscreen::Exclusive(mode)) => DisplayMode::FullscreenExclusive(VideoMode(mode)),
Some(Fullscreen::Borderless(monitor)) => {
DisplayMode::FullscreenBorderless(monitor.map(Monitor))
}
None => DisplayMode::Windowed(self.monitor()),
}
}
#[inline]
pub fn set_display_mode(&self, display_mode: DisplayMode) {
match display_mode {
DisplayMode::FullscreenExclusive(mode) => {
self.0.set_fullscreen(Some(Fullscreen::Exclusive(mode.0)));
}
DisplayMode::FullscreenBorderless(monitor) => {
self.0
.set_fullscreen(Some(Fullscreen::Borderless(monitor.map(|m| m.0))));
}
DisplayMode::Windowed(monitor) => {
self.0.set_fullscreen(None);
if let Some(monitor) = monitor {
self.center_on(&monitor);
}
}
}
}
#[inline]
pub fn is_fullscreen(&self) -> bool {
self.0.fullscreen().is_some()
}
#[inline]
pub fn set_fullscreen_borderless(&self, monitor: Option<Monitor>) {
self.set_display_mode(DisplayMode::FullscreenBorderless(monitor));
}
#[inline]
pub fn set_fullscreen_exclusive(&self, video_mode: VideoMode) {
self.set_display_mode(DisplayMode::FullscreenExclusive(video_mode));
}
#[inline]
pub fn set_windowed(&self, monitor: Option<Monitor>) {
self.set_display_mode(DisplayMode::Windowed(monitor));
}
#[inline]
pub fn has_focus(&self) -> bool {
self.0.has_focus()
}
#[inline]
pub fn pixel_pos(&self) -> Option<Vec2I> {
self.0.inner_position().ok().map(Vec2I::from)
}
#[inline]
pub fn pos(&self) -> Option<Vec2I> {
Some(
self.0
.inner_position()
.ok()?
.to_logical(self.0.scale_factor())
.into(),
)
}
#[inline]
pub fn outer_pixel_pos(&self) -> Option<Vec2I> {
self.0.outer_position().ok().map(Vec2I::from)
}
#[inline]
pub fn outer_pos(&self) -> Option<Vec2I> {
Some(
self.0
.outer_position()
.ok()?
.to_logical(self.0.scale_factor())
.into(),
)
}
#[inline]
pub fn set_outer_pos(&self, pos: impl Into<Vec2I>) {
let pos = pos.into();
self.0
.set_outer_position(LogicalPosition::new(pos.x, pos.y));
}
#[inline]
pub fn set_outer_pixel_pos(&self, pos: impl Into<Vec2I>) {
let pos = pos.into();
self.0
.set_outer_position(PhysicalPosition::new(pos.x, pos.y));
}
#[inline]
pub fn pixel_size(&self) -> Vec2U {
self.0.inner_size().into()
}
#[inline]
pub fn size(&self) -> Vec2U {
self.0.inner_size().to_logical(self.0.scale_factor()).into()
}
#[inline]
pub fn request_size(&self, size: impl Into<Vec2U>) -> bool {
let size = size.into();
self.0
.request_inner_size(LogicalSize::new(size.x, size.y))
.is_none()
}
#[inline]
pub fn request_pixel_size(&self, size: impl Into<Vec2U>) -> bool {
let size = size.into();
self.0
.request_inner_size(PhysicalSize::new(size.x, size.y))
.is_none()
}
#[inline]
pub fn outer_pixel_size(&self) -> Vec2U {
self.0.outer_size().into()
}
#[inline]
pub fn outer_size(&self) -> Vec2U {
self.0.outer_size().to_logical(self.0.scale_factor()).into()
}
#[inline]
pub fn center(&self) -> Vec2U {
self.size() / 2
}
#[inline]
pub fn pixel_center(&self) -> Vec2U {
self.pixel_size() / 2
}
#[inline]
pub fn resizable(&self) -> bool {
self.0.is_resizable()
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
self.0.set_resizable(resizable);
}
#[inline]
pub fn maximized(&self) -> bool {
self.0.is_maximized()
}
#[inline]
pub fn set_maximized(&self, maximized: bool) {
self.0.set_maximized(maximized);
}
#[inline]
pub fn minimized(&self) -> Option<bool> {
self.0.is_minimized()
}
#[inline]
pub fn set_minimized(&self, minimized: bool) {
self.0.set_minimized(minimized);
}
#[inline]
pub fn set_min_inner_size(&self, size: impl Into<Option<Vec2U>>) {
self.0
.set_min_inner_size(size.into().map(|s| LogicalSize::new(s.x, s.y)));
}
#[inline]
pub fn set_min_inner_pixel_size(&self, size: impl Into<Option<Vec2U>>) {
self.0
.set_min_inner_size(size.into().map(|s| LogicalSize::new(s.x, s.y)));
}
#[inline]
pub fn set_max_inner_size(&self, size: impl Into<Option<Vec2U>>) {
self.0
.set_max_inner_size(size.into().map(|s| LogicalSize::new(s.x, s.y)));
}
#[inline]
pub fn set_max_inner_pixel_size(&self, size: impl Into<Option<Vec2U>>) {
self.0
.set_max_inner_size(size.into().map(|s| LogicalSize::new(s.x, s.y)));
}
#[inline]
pub fn set_cursor(&self, icon: CursorIcon) {
self.0.set_cursor(Cursor::Icon(icon.into()));
}
}