swamp_window/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
/*
* Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/swamp-window
* Licensed under the MIT License. See LICENSE in the project root for license information.
*/
use crate::dpi::LogicalSize;
use crate::dpi::PhysicalSize;
use log::info;
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::dpi;
use winit::error::EventLoopError;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowAttributes, WindowId};
/// A trait for handling application-specific window creation and management.
///
/// The `AppHandler` trait defines the behavior required for handling
/// the creation of application windows. Implementing this trait allows
/// for customized window management tailored to the needs of your application.
pub trait AppHandler {
fn create_window(&mut self, window: Arc<Window>);
fn resized(&mut self, size: dpi::PhysicalSize<u32>);
fn min_size(&self) -> (u16, u16);
fn redraw(&mut self);
}
pub struct App<'a> {
window: Option<Arc<Window>>,
handler: &'a mut (dyn AppHandler),
window_attributes: WindowAttributes,
next_resize: Option<PhysicalSize<u32>>,
}
impl<'a> App<'a> {
pub fn new(
handler: &'a mut dyn AppHandler,
title: &str,
min_width: u16,
min_height: u16,
) -> Self {
let min_size = LogicalSize::new(min_width as f64, min_height as f64);
let window_attributes = WindowAttributes::default()
.with_title(title)
.with_resizable(true)
.with_min_inner_size(min_size);
Self {
handler,
window: None,
window_attributes,
next_resize: None,
}
}
}
impl ApplicationHandler for App<'_> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
info!("creating new window");
let window = Arc::new(
event_loop
.create_window(self.window_attributes.clone())
.unwrap(),
);
self.window = Some(window.clone());
self.handler.create_window(window);
info!("created the window");
}
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
if id != self.window.as_ref().unwrap().id() {
return;
}
match event {
WindowEvent::CloseRequested => {
event_loop.exit();
}
WindowEvent::Resized(physical_size) => {
self.next_resize = Some(physical_size);
// This tells winit that we want another frame after this one
self.window.as_ref().unwrap().request_redraw();
}
WindowEvent::RedrawRequested => {
if let Some(next_resize) = self.next_resize {
self.handler.resized(next_resize);
self.next_resize = None;
}
// This tells winit that we want another frame after this one
self.window.as_ref().unwrap().request_redraw();
if self.window.is_some() {
self.handler.redraw();
}
}
_ => {}
}
}
fn suspended(&mut self, _: &ActiveEventLoop) {}
fn exiting(&mut self, _: &ActiveEventLoop) {}
}
/// A struct responsible for managing the application window lifecycle.
///
/// The `WindowRunner` struct provides functionality to run an application
/// that utilizes an event loop for window management. It abstracts the details
/// of creating and running the event loop, making it easier to integrate window
/// handling into your game application.
pub struct WindowRunner;
impl WindowRunner {
/// Runs the application with the provided handler.
///
/// This method initializes an event loop and starts the application by
/// executing the provided `AppHandler`. The event loop runs in a polling
/// mode, allowing for responsive event handling. It is not guaranteed to ever return.
///
/// # Parameters
///
/// - `handler`: A mutable reference to an object implementing the `AppHandler`
/// trait, which defines the behavior of the application in response to events.
///
/// # Returns
///
/// This method returns a `Result<(), EventLoopError>`.
/// If an error occurs during event loop creation, it returns an `EventLoopError`.
///
/// # Note
///
/// It is not guaranteed to ever return, as the event loop will run indefinitely
/// until the application is terminated.
///
/// # Example
///
/// ```rust
/// use swamp_window::WindowRunner;
/// use async_trait::async_trait;
/// use swamp_window::AppHandler;
/// use std::sync::Arc;
/// use winit::window::Window;
/// use winit::dpi;
///
/// struct MyApp;
///
/// #[async_trait]
/// impl AppHandler for MyApp {
/// async fn create_window(&mut self, window: Arc<Window>) {
/// // Custom window initialization code here
/// }
///
/// fn redraw(&mut self) { todo!() }
/// fn resized(&mut self, size: dpi::PhysicalSize<u32>) { todo!() }
/// }
///
/// let mut my_app = MyApp;
///
/// #[cfg(test)]
/// fn test() {
/// if let Err(e) = WindowRunner::run_app(&mut my_app) {
/// eprintln!("Error running the application: {:?}", e);
/// }
/// }
/// ```
pub fn run_app(handler: &mut dyn AppHandler, title: &str) -> Result<(), EventLoopError> {
let event_loop = EventLoop::new()?;
event_loop.set_control_flow(ControlFlow::Poll);
let (min_width, min_height) = handler.min_size();
let mut app = App::new(handler, title, min_width, min_height);
let _ = event_loop.run_app(&mut app);
Ok(())
}
}