use std::sync::Arc;
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use winit::dpi::{LogicalSize, PhysicalSize};
use winit::window::WindowAttributes;
use crate::Result;
#[derive(Debug, Clone)]
pub struct WindowConfig {
pub title: String,
pub width: u32,
pub height: u32,
pub resizable: bool,
pub vsync: bool,
pub fullscreen: bool,
pub min_size: Option<(u32, u32)>,
pub max_size: Option<(u32, u32)>,
}
impl Default for WindowConfig {
fn default() -> Self {
Self {
title: "Sable Engine".to_string(),
width: 1280,
height: 720,
resizable: true,
vsync: true,
fullscreen: false,
min_size: Some((320, 240)),
max_size: None,
}
}
}
impl WindowConfig {
#[must_use]
pub fn new(title: impl Into<String>) -> Self {
Self {
title: title.into(),
..Default::default()
}
}
#[must_use]
pub fn with_size(mut self, width: u32, height: u32) -> Self {
self.width = width;
self.height = height;
self
}
#[must_use]
pub fn with_resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
#[must_use]
pub fn with_vsync(mut self, vsync: bool) -> Self {
self.vsync = vsync;
self
}
#[must_use]
pub fn with_fullscreen(mut self, fullscreen: bool) -> Self {
self.fullscreen = fullscreen;
self
}
#[must_use]
pub fn with_min_size(mut self, width: u32, height: u32) -> Self {
self.min_size = Some((width, height));
self
}
#[must_use]
pub fn with_max_size(mut self, width: u32, height: u32) -> Self {
self.max_size = Some((width, height));
self
}
}
#[derive(Debug, Clone)]
pub struct Window {
inner: Arc<winit::window::Window>,
vsync: bool,
}
impl Window {
pub fn new(event_loop: &winit::event_loop::ActiveEventLoop, config: &WindowConfig) -> Result<Self> {
let mut attributes = WindowAttributes::default()
.with_title(&config.title)
.with_inner_size(LogicalSize::new(config.width, config.height))
.with_resizable(config.resizable);
if let Some((min_w, min_h)) = config.min_size {
attributes = attributes.with_min_inner_size(LogicalSize::new(min_w, min_h));
}
if let Some((max_w, max_h)) = config.max_size {
attributes = attributes.with_max_inner_size(LogicalSize::new(max_w, max_h));
}
if config.fullscreen {
attributes =
attributes.with_fullscreen(Some(winit::window::Fullscreen::Borderless(None)));
}
let window = event_loop.create_window(attributes)?;
Ok(Self {
inner: Arc::new(window),
vsync: config.vsync,
})
}
#[must_use]
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.inner.inner_size()
}
#[must_use]
pub fn scale_factor(&self) -> f64 {
self.inner.scale_factor()
}
#[must_use]
pub fn vsync(&self) -> bool {
self.vsync
}
pub fn set_title(&self, title: &str) {
self.inner.set_title(title);
}
pub fn request_redraw(&self) {
self.inner.request_redraw();
}
pub fn window_handle(
&self,
) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError>
{
self.inner.window_handle()
}
pub fn display_handle(
&self,
) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError>
{
self.inner.display_handle()
}
#[must_use]
pub fn winit_window(&self) -> &winit::window::Window {
&self.inner
}
#[must_use]
pub fn id(&self) -> winit::window::WindowId {
self.inner.id()
}
}
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_window_config_default() {
let config = WindowConfig::default();
assert_eq!(config.title, "Sable Engine");
assert_eq!(config.width, 1280);
assert_eq!(config.height, 720);
assert!(config.resizable);
assert!(config.vsync);
assert!(!config.fullscreen);
assert_eq!(config.min_size, Some((320, 240)));
assert_eq!(config.max_size, None);
}
#[test]
fn test_window_config_builder() {
let config = WindowConfig::new("Test Window")
.with_size(800, 600)
.with_resizable(false)
.with_vsync(false)
.with_fullscreen(true)
.with_min_size(400, 300)
.with_max_size(1920, 1080);
assert_eq!(config.title, "Test Window");
assert_eq!(config.width, 800);
assert_eq!(config.height, 600);
assert!(!config.resizable);
assert!(!config.vsync);
assert!(config.fullscreen);
assert_eq!(config.min_size, Some((400, 300)));
assert_eq!(config.max_size, Some((1920, 1080)));
}
#[test]
fn test_window_config_into_string() {
let config = WindowConfig::new(String::from("Dynamic Title"));
assert_eq!(config.title, "Dynamic Title");
}
}