use crate::EngineResult;
use winit::{
application::ApplicationHandler,
event::{DeviceEvent, DeviceId, StartCause, WindowEvent},
event_loop::{ActiveEventLoop, EventLoop},
window::{WindowId, WindowAttributes, Window as WinitWindow},
};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct WindowConfig {
pub title: String,
pub width: u32,
pub height: u32,
pub resizable: bool,
}
impl Default for WindowConfig {
fn default() -> Self {
Self {
title: "ShadowEngine2D".to_string(),
width: 800,
height: 600,
resizable: true,
}
}
}
pub struct Window {
pub window: Arc<WinitWindow>,
pub config: WindowConfig,
}
impl Window {
pub fn new(window: WinitWindow, config: WindowConfig) -> Self {
Self {
window: Arc::new(window),
config,
}
}
pub fn inner_size(&self) -> (u32, u32) {
let size = self.window.inner_size();
(size.width, size.height)
}
pub fn set_title(&self, title: &str) {
self.window.set_title(title);
}
}
pub trait AppHandler {
fn on_init(&mut self, window: &Window) -> EngineResult<()> {
Ok(())
}
fn on_update(&mut self, window: &Window, delta_time: f32) -> EngineResult<()>;
fn on_render(&mut self, window: &Window) -> EngineResult<()>;
fn on_window_event(&mut self, window: &Window, event: &WindowEvent) -> EngineResult<()> {
Ok(())
}
fn on_exit(&mut self) {}
}
pub struct EventLoopHandler {
window: Option<Window>,
config: WindowConfig,
app_handler: Box<dyn AppHandler>,
last_frame: std::time::Instant,
}
impl EventLoopHandler {
pub fn new(config: WindowConfig, app_handler: Box<dyn AppHandler>) -> Self {
Self {
window: None,
config,
app_handler,
last_frame: std::time::Instant::now(),
}
}
}
impl ApplicationHandler for EventLoopHandler {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
let window_attrs = WindowAttributes::default()
.with_title(&self.config.title)
.with_inner_size(winit::dpi::LogicalSize::new(
self.config.width,
self.config.height
))
.with_resizable(self.config.resizable);
match event_loop.create_window(window_attrs) {
Ok(winit_window) => {
let window = Window::new(winit_window, self.config.clone());
if let Err(e) = self.app_handler.on_init(&window) {
eprintln!("Initialization error: {}", e);
event_loop.exit();
return;
}
self.window = Some(window);
},
Err(e) => {
eprintln!("Failed to create window: {}", e);
event_loop.exit();
}
}
}
}
fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: StartCause) {
if let Some(window) = &self.window {
let now = std::time::Instant::now();
let delta_time = now.duration_since(self.last_frame).as_secs_f32();
self.last_frame = now;
if let Err(e) = self.app_handler.on_update(window, delta_time) {
eprintln!("Update error: {}", e);
}
if let Err(e) = self.app_handler.on_render(window) {
eprintln!("Render error: {}", e);
}
window.window.request_redraw();
}
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
if let Some(window) = &self.window {
match &event {
WindowEvent::CloseRequested => {
self.app_handler.on_exit();
event_loop.exit();
},
_ => {
if let Err(e) = self.app_handler.on_window_event(window, &event) {
eprintln!("Window event error: {}", e);
}
}
}
}
}
fn device_event(
&mut self,
_event_loop: &ActiveEventLoop,
_device_id: DeviceId,
_event: DeviceEvent,
) {
}
}
pub fn run_window(config: WindowConfig, app_handler: Box<dyn AppHandler>) -> EngineResult<()> {
let event_loop = EventLoop::new()?;
event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
let mut handler = EventLoopHandler::new(config, app_handler);
event_loop.run_app(&mut handler)?;
Ok(())
}