/// Common boilerplate for setting up a winit application.
use std::marker::PhantomData;
use std::sync::Arc as Rc;
use winit::application::ApplicationHandler;
use winit::event::{DeviceEvent, DeviceId, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::window::{Window, WindowAttributes, WindowId};
/// Run a Winit application.
#[allow(unused_mut)]
pub(crate) fn run_app(
event_loop: EventLoop,
mut app: impl ApplicationHandler + 'static,
) {
#[cfg(not(target_family = "wasm"))]
event_loop.run_app(app).unwrap();
#[cfg(target_family = "wasm")]
winit::platform::web::EventLoopExtWebSys::spawn_app(event_loop, app);
}
/// Create a window from a set of window attributes.
#[allow(dead_code)]
pub(crate) fn make_window(
elwt: &dyn ActiveEventLoop,
f: impl FnOnce(WindowAttributes) -> WindowAttributes,
) -> Rc<dyn Window> {
let attributes = f(WindowAttributes::default());
#[cfg(target_family = "wasm")]
let attributes =
winit::platform::web::WindowAttributesExtWebSys::with_append(
attributes, true,
);
let window = elwt.create_window(attributes);
window.unwrap().into()
}
/// Easily constructable winit application.
pub(crate) struct WinitApp<
T,
S,
Init,
InitSurface,
Handler,
DeviceEventHandler,
AboutToWaitHandler,
> {
/// Closure to initialize `state`.
init: Init,
/// Closure to initialize `surface_state`.
init_surface: InitSurface,
/// Closure to run on window events.
event: Handler,
/// Closure to run on device events.
device_event: DeviceEventHandler,
/// Closure to run on about_to_wait events.
about_to_wait: AboutToWaitHandler,
/// Contained state.
state: Option<T>,
/// Contained surface state.
surface_state: Option<S>,
}
/// Builder that makes it so we don't have to name `T`.
pub(crate) struct WinitAppBuilder<T, S, Init, InitSurface> {
/// Closure to initialize `state`.
init: Init,
/// Closure to initialize `surface_state`.
init_surface: InitSurface,
/// Eat the type parameter.
_marker: PhantomData<(Option<T>, Option<S>)>,
}
impl<T, S, Init, InitSurface> WinitAppBuilder<T, S, Init, InitSurface>
where
Init: FnMut(&dyn ActiveEventLoop) -> T,
InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S,
{
/// Create with an "init" closure.
pub(crate) fn with_init(
init: Init,
init_surface: InitSurface,
) -> Self {
Self { init, init_surface, _marker: PhantomData }
}
/// Build a new application.
#[allow(clippy::type_complexity)]
pub(crate) fn with_event_handler<F>(
self,
handler: F,
) -> WinitApp<
T,
S,
Init,
InitSurface,
F,
impl FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop),
impl FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop),
>
where
F: FnMut(
&mut T,
Option<&mut S>,
WindowId,
WindowEvent,
&dyn ActiveEventLoop,
),
{
WinitApp::new(
self.init,
self.init_surface,
handler,
|_, _, _, _| {},
|_, _, _| {},
)
}
}
impl<
T,
S,
Init,
InitSurface,
Handler,
DeviceEventHandler,
AboutToWaitHandler,
>
WinitApp<
T,
S,
Init,
InitSurface,
Handler,
DeviceEventHandler,
AboutToWaitHandler,
>
where
Init: FnMut(&dyn ActiveEventLoop) -> T,
InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S,
Handler: FnMut(
&mut T,
Option<&mut S>,
WindowId,
WindowEvent,
&dyn ActiveEventLoop,
),
DeviceEventHandler:
FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop),
AboutToWaitHandler:
FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop),
{
/// Create a new application.
pub(crate) fn new(
init: Init,
init_surface: InitSurface,
event: Handler,
device_event: DeviceEventHandler,
about_to_wait: AboutToWaitHandler,
) -> Self {
Self {
init,
init_surface,
event,
device_event,
about_to_wait,
state: None,
surface_state: None,
}
}
/// Build a new application.
#[allow(dead_code)]
pub(crate) fn with_about_to_wait_handler<F>(
self,
about_to_wait: F,
) -> WinitApp<T, S, Init, InitSurface, Handler, DeviceEventHandler, F>
where
F: FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop),
{
WinitApp::new(
self.init,
self.init_surface,
self.event,
self.device_event,
about_to_wait,
)
}
/// Build a new application.
#[allow(dead_code)]
pub(crate) fn with_device_event_handler<F>(
self,
device_event: F,
) -> WinitApp<T, S, Init, InitSurface, Handler, F, AboutToWaitHandler>
where
F: FnMut(
&mut T,
Option<&mut S>,
DeviceEvent,
&dyn ActiveEventLoop,
),
{
WinitApp::new(
self.init,
self.init_surface,
self.event,
device_event,
self.about_to_wait,
)
}
}
impl<
T,
S,
Init,
InitSurface,
Handler,
DeviceEventHandler,
AboutToWaitHandler,
> ApplicationHandler
for WinitApp<
T,
S,
Init,
InitSurface,
Handler,
DeviceEventHandler,
AboutToWaitHandler,
>
where
Init: FnMut(&dyn ActiveEventLoop) -> T,
InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S,
Handler: FnMut(
&mut T,
Option<&mut S>,
WindowId,
WindowEvent,
&dyn ActiveEventLoop,
),
DeviceEventHandler:
FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop),
AboutToWaitHandler:
FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop),
{
fn resumed(&mut self, el: &dyn ActiveEventLoop) {
debug_assert!(self.state.is_none());
let mut state = (self.init)(el);
self.surface_state = Some((self.init_surface)(el, &mut state));
self.state = Some(state);
}
fn suspended(&mut self, _event_loop: &dyn ActiveEventLoop) {
let surface_state = self.surface_state.take();
debug_assert!(surface_state.is_some());
drop(surface_state);
}
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let state = self.state.as_mut().unwrap();
let surface_state = self.surface_state.as_mut();
(self.event)(state, surface_state, window_id, event, event_loop);
}
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
if let Some(state) = self.state.as_mut() {
let surface_state = self.surface_state.as_mut();
(self.about_to_wait)(state, surface_state, event_loop);
}
}
fn device_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
_device_id: Option<DeviceId>,
event: DeviceEvent,
) {
let state = self.state.as_mut().unwrap();
let surface_state = self.surface_state.as_mut();
(self.device_event)(state, surface_state, event, event_loop);
}
fn can_create_surfaces(&mut self, el: &dyn ActiveEventLoop) {
let mut state = (self.init)(el);
self.surface_state = Some((self.init_surface)(el, &mut state));
self.state = Some(state);
}
}