use crate::prelude::Color;
use crossbeam::atomic::AtomicCell;
use glam::{vec2, Vec2};
use std::sync::Arc;
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, UserAttentionType, WindowLevel};
use winit::{
dpi::*,
error::ExternalError,
window::{Fullscreen, WindowButtons},
};
#[derive(Clone)]
pub struct Window {
window: Arc<winit::window::Window>,
clear_color: Arc<AtomicCell<Color>>,
}
impl Window {
#[inline]
pub fn request_redraw(&self) {
self.window.request_redraw();
}
#[inline]
pub fn inner_size(&self) -> Vec2 {
Size(self.window.inner_size().into()).into()
}
#[inline]
pub fn set_inner_size(&self, size: Vec2) {
self.window.set_inner_size(Size::from_vec2(size));
}
#[inline]
pub fn outer_size(&self) -> Vec2 {
Size(self.window.outer_size().into()).into()
}
#[inline]
pub fn set_min_inner_size(&self, size: Option<Vec2>) {
self.window.set_min_inner_size(size.map(Size::from_vec2));
}
#[inline]
pub fn set_max_inner_size(&self, size: Option<Vec2>) {
self.window.set_max_inner_size(size.map(Size::from_vec2));
}
#[inline]
pub fn resize_increments(&self) -> Option<Vec2> {
self.window
.resize_increments()
.map(|x| Size(x.into()).into())
}
#[inline]
pub fn set_resize_increments(&self, increments: Option<Vec2>) {
self.window
.set_resize_increments(increments.map(Size::from_vec2));
}
#[inline]
pub fn title(&self) -> String {
self.window.title()
}
#[inline]
pub fn set_title(&self, title: &str) {
self.window.set_title(title)
}
#[inline]
pub fn set_visible(&self, visible: bool) {
self.window.set_visible(visible)
}
#[inline]
pub fn visible(&self) -> Option<bool> {
self.window.is_visible()
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
self.window.set_resizable(resizable)
}
#[inline]
pub fn resizable(&self) -> bool {
self.window.is_resizable()
}
#[inline]
pub fn set_enabled_buttons(&self, close: bool, minimize: bool, maximize: bool) {
let mut buttons = WindowButtons::empty();
if close {
buttons.toggle(WindowButtons::CLOSE);
};
if minimize {
buttons.toggle(WindowButtons::MINIMIZE);
};
if maximize {
buttons.toggle(WindowButtons::MAXIMIZE);
};
self.window.set_enabled_buttons(buttons)
}
#[inline]
pub fn enabled_buttons(&self) -> (bool, bool, bool) {
let mut result = (false, false, false);
let enabled = self.window.enabled_buttons();
if enabled.contains(WindowButtons::CLOSE) {
result.0 = true;
};
if enabled.contains(WindowButtons::MINIMIZE) {
result.1 = true;
};
if enabled.contains(WindowButtons::MAXIMIZE) {
result.2 = true;
};
result
}
#[inline]
pub fn set_minimized(&self, minimized: bool) {
self.window.set_minimized(minimized)
}
#[inline]
pub fn minimized(&self) -> Option<bool> {
self.window.is_minimized()
}
#[inline]
pub fn set_maximized(&self, maximized: bool) {
self.window.set_maximized(maximized)
}
#[inline]
pub fn maximized(&self) -> bool {
self.window.is_maximized()
}
#[inline]
pub fn set_fullscreen(&self, fullscreen: bool) {
let fullscreen = if fullscreen {
Some(Fullscreen::Borderless(None))
} else {
None
};
self.window.set_fullscreen(fullscreen)
}
#[inline]
pub fn fullscreen(&self) -> bool {
self.window.fullscreen().is_some()
}
#[inline]
pub fn set_decorations(&self, decorations: bool) {
self.window.set_decorations(decorations)
}
#[inline]
pub fn decorated(&self) -> bool {
self.window.is_decorated()
}
#[inline]
pub fn set_window_level(&self, level: WindowLevel) {
self.window.set_window_level(level)
}
#[inline]
pub fn set_window_icon(&self, icon: Option<Icon>) {
self.window.set_window_icon(icon);
}
#[inline]
pub fn focus(&self) {
self.window.focus_window();
}
#[inline]
pub fn has_focus(&self) -> bool {
self.window.has_focus()
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
self.window.request_user_attention(request_type);
}
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.window.set_cursor_icon(cursor);
}
#[inline]
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
self.window.set_cursor_grab(mode)
}
#[inline]
pub fn set_cursor_visible(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
self.window.set_cursor_grab(mode)
}
#[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> {
self.window.drag_window()
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
self.window.set_cursor_hittest(hittest)
}
pub fn set_clear_color(&self, color: impl Into<Color>) {
let color: Color = color.into();
self.window.set_transparent(color.alpha() < 1.0);
self.clear_color.store(color);
}
pub fn clear_color(&self) -> Color {
self.clear_color.load()
}
}
#[derive(Clone)]
#[must_use]
pub struct WindowBuilder {
attributes: winit::window::WindowBuilder,
pub(crate) clear_color: Color,
}
impl WindowBuilder {
pub fn new() -> Self {
let attributes = winit::window::WindowBuilder::new().with_title("Game");
Self {
attributes,
clear_color: Color::BLACK,
}
}
#[inline]
pub fn from_winit_builder(builder: winit::window::WindowBuilder) -> Self {
Self {
attributes: builder,
clear_color: Color::BLACK,
}
}
#[inline]
pub fn inner_size(mut self, size: Vec2) -> Self {
self.attributes = self.attributes.with_inner_size(Size::from(size));
self
}
#[inline]
pub fn max_inner_size(mut self, size: Vec2) -> Self {
self.attributes = self.attributes.with_max_inner_size(Size::from(size));
self
}
#[inline]
pub fn min_inner_size(mut self, size: Vec2) -> Self {
self.attributes = self.attributes.with_min_inner_size(Size::from(size));
self
}
#[inline]
pub fn position(mut self, position: Vec2) -> Self {
let position = Position::Physical(PhysicalPosition {
x: position.x as i32,
y: position.y as i32,
});
self.attributes = self.attributes.with_position(position);
self
}
#[inline]
pub fn resizable(mut self, resizable: bool) -> Self {
self.attributes = self.attributes.with_resizable(resizable);
self
}
#[inline]
pub fn enabled_buttons(mut self, close: bool, minimize: bool, maximize: bool) -> Self {
let mut buttons = WindowButtons::empty();
if close {
buttons.toggle(WindowButtons::CLOSE);
};
if minimize {
buttons.toggle(WindowButtons::MINIMIZE);
};
if maximize {
buttons.toggle(WindowButtons::MAXIMIZE);
};
self.attributes = self.attributes.with_enabled_buttons(buttons);
self
}
#[inline]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.attributes = self.attributes.with_title(title);
self
}
#[inline]
pub fn fullscreen(mut self, fullscreen: bool) -> Self {
let fullscreen = if fullscreen {
Some(Fullscreen::Borderless(None))
} else {
None
};
self.attributes = self.attributes.with_fullscreen(fullscreen);
self
}
#[inline]
pub fn maximized(mut self, maximized: bool) -> Self {
self.attributes = self.attributes.with_maximized(maximized);
self
}
#[inline]
pub fn visible(mut self, visible: bool) -> Self {
self.attributes = self.attributes.with_visible(visible);
self
}
pub fn clear_color(mut self, color: impl Into<Color>) -> Self {
let color: Color = color.into();
self.attributes = self.attributes.with_transparent(color.alpha() < 1.0);
self.clear_color = color;
self
}
#[inline]
pub fn decorations(mut self, decorations: bool) -> Self {
self.attributes = self.attributes.with_decorations(decorations);
self
}
#[inline]
pub fn window_level(mut self, level: WindowLevel) -> Self {
self.attributes = self.attributes.with_window_level(level);
self
}
#[inline]
pub fn icon(mut self, icon: Option<Icon>) -> Self {
self.attributes = self.attributes.with_window_icon(icon);
self
}
#[inline]
pub fn resize_increments(mut self, increments: Vec2) -> Self {
self.attributes = self
.attributes
.with_resize_increments(Size::from(increments));
self
}
#[inline]
pub fn active(mut self, active: bool) -> Self {
self.attributes = self.attributes.with_active(active);
self
}
}
impl From<WindowBuilder> for winit::window::WindowBuilder {
fn from(val: WindowBuilder) -> Self {
val.attributes
}
}
impl From<Arc<winit::window::Window>> for Window {
fn from(value: Arc<winit::window::Window>) -> Self {
Self {
window: value,
clear_color: Arc::new(AtomicCell::new(Color::BLACK)),
}
}
}
impl Default for WindowBuilder {
fn default() -> Self {
Self::new()
}
}
struct Size(winit::dpi::Size);
impl Size {
fn from_vec2(vec2: Vec2) -> Self {
vec2.into()
}
}
impl From<Vec2> for Size {
fn from(value: Vec2) -> Self {
Size(winit::dpi::Size::Physical(PhysicalSize {
width: value.x as u32,
height: value.y as u32,
}))
}
}
impl From<Size> for Vec2 {
fn from(value: Size) -> Self {
vec2(
value.0.to_physical(1.0).width,
value.0.to_physical(1.0).height,
)
}
}
impl From<Size> for winit::dpi::Size {
fn from(value: Size) -> winit::dpi::Size {
value.0
}
}