thin_engine/
application.rs

1#![allow(clippy::type_complexity)]
2use winit::{
3    application::ApplicationHandler,
4    window::{Window, WindowId},
5    event_loop::{EventLoop, ActiveEventLoop},
6    event::*
7};
8use crate::{SimpleWindowBuilder, Display, Settings};
9use std::{hash::Hash, time::Instant};
10use winit_input_map::InputMap;
11/// holds all the data and runs the application.
12pub struct ThinEngine<'a, H, D, S, U, E>
13where H: Hash + PartialEq + Eq + Clone + Copy,
14S: FnMut(&Display, &mut Window, &ActiveEventLoop),
15U: FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window),
16D: FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window),
17E: FnMut(Event<()>, &ActiveEventLoop, Option<&(Window, Display)>)
18{
19    state: Option<(Window, Display)>,
20    window_settings: Option<SimpleWindowBuilder>,
21    update:        &'a mut U,
22    draw:          &'a mut D,
23    setup:         &'a mut S,
24    event_handler: &'a mut E,
25    input_map: InputMap<H>,
26    settings: Settings,
27    frame_start: Instant,
28}
29impl<H, D, S, U, E> ApplicationHandler for ThinEngine<'_, H, D, S, U, E>
30where H: Hash + PartialEq + Eq + Clone + Copy,
31S: FnMut(&Display, &mut Window, &ActiveEventLoop),
32U: FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window),
33D: FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window),
34E: FnMut(Event<()>, &ActiveEventLoop, Option<&(Window, Display)>)
35{
36   fn resumed(&mut self, event_loop: &ActiveEventLoop) {
37        if self.state.is_some() { return }
38        let (mut window, display) = self.window_settings
39            .take().expect("No window settings are available")
40            .build(event_loop);
41       (self.setup)(&display, &mut window, event_loop);
42        self.state = Some((window, display));
43    }
44    fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
45        match event {
46            WindowEvent::Resized(size) => self.state.as_mut().unwrap().1.resize(size.into()),
47            WindowEvent::CloseRequested => event_loop.exit(),
48            WindowEvent::RedrawRequested => {
49                let Some((ref mut window, ref display)) = self.state else { return };
50                let input = &mut self.input_map;
51                let settings = &mut self.settings;
52                (self.draw)(input, display, settings, event_loop, window)
53            },
54            _ => self.input_map.update_with_window_event(&event)
55        }
56        (self.event_handler)(Event::WindowEvent { window_id, event }, event_loop, self.state.as_ref());
57    }
58    fn device_event(&mut self, event_loop: &ActiveEventLoop, device_id: DeviceId, event: DeviceEvent) {
59        self.input_map.update_with_device_event(device_id, &event);
60        (self.event_handler)(Event::DeviceEvent { device_id, event }, event_loop, self.state.as_ref());
61    }
62    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
63        if let Some(ref mut gilrs) = self.settings.gamepads { self.input_map.update_with_gilrs(gilrs) }
64
65        let update = self.settings.min_frame_duration
66            .map(|i| i <= self.frame_start.elapsed())
67            .unwrap_or(true);
68        if update {
69            let (window, display) = self.state.as_mut().unwrap();
70            (self.update)(&mut self.input_map, display, &mut self.settings, event_loop, window);
71            self.frame_start = Instant::now();
72            self.input_map.init();
73        }
74        (self.event_handler)(Event::AboutToWait, event_loop, self.state.as_ref());
75    }
76    fn suspended(&mut self, event_loop: &ActiveEventLoop) {
77        let window = self.state.take().unwrap().0;
78
79        self.window_settings = Some(SimpleWindowBuilder::new()
80            .set_window_builder(Window::default_attributes()
81            .with_inner_size(window.inner_size())
82            // todo position
83            .with_resizable(window.is_resizable())
84            .with_enabled_buttons(window.enabled_buttons())
85            .with_title(window.title())
86            .with_fullscreen(window.fullscreen())
87            .with_maximized(window.is_maximized())
88            .with_visible(window.is_visible().unwrap_or(true))
89            // todo transparent
90            .with_decorations(window.is_decorated())
91            .with_theme(window.theme())
92            // todo resize increments
93            // todo parrent window
94        ));
95        (self.event_handler)(Event::Suspended, event_loop, self.state.as_ref());
96    }
97}
98/// holds data used to build and run the program
99pub struct ThinBuilder<'a, H: Hash + PartialEq + Eq + Clone + Copy> {
100    window_settings: SimpleWindowBuilder,
101    input_map: InputMap<H>,
102    settings: Settings,
103    update: Box<dyn FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window) + 'a>,
104    setup: Box<dyn FnMut(&Display, &mut Window, &ActiveEventLoop) + 'a>,
105    draw: Box<dyn FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window) + 'a>,
106    event_handler: Box<dyn FnMut(Event<()>, &ActiveEventLoop, Option<&(Window, Display)>) + 'a>
107}
108impl<'a, H: Hash + PartialEq + Eq + Clone + Copy> ThinBuilder<'a, H> {
109   pub fn new(input_map: InputMap<H>) -> ThinBuilder<'a, H> {
110        ThinBuilder {
111            input_map,
112            settings: Settings::default(),
113            update:        Box::new(|_, _, _, _, _| {}),
114            setup:         Box::new(|_, _, _|       {}),
115            draw:          Box::new(|_, _, _, _, _| {}),
116            event_handler: Box::new(|_, _, _|       {}),
117            window_settings: SimpleWindowBuilder::new()
118        }
119    }
120    /// builds and runs the program
121    pub fn build(mut self, ev: EventLoop<()>) -> Result<(), winit::error::EventLoopError> {
122        let mut engine = ThinEngine {
123            state: None,
124            window_settings: Some(self.window_settings),
125            update:        &mut self.update,
126            draw:          &mut self.draw,
127            setup:         &mut self.setup,
128            event_handler: &mut self.event_handler,
129            input_map: self.input_map,
130            settings:  self.settings,
131            frame_start: Instant::now(),
132        };
133        ev.run_app(&mut engine)
134    }
135    /// this is run whenever a draw request is scheduled
136    pub fn with_draw(
137        mut self,
138        draw: impl FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window) + 'a
139    ) -> Self {
140        self.draw = Box::new(draw);
141        self
142    }
143    /// this is run whenever the window and display are created
144    pub fn with_setup(mut self, setup: impl FnMut(&Display, &mut Window, &ActiveEventLoop) + 'a) -> Self {
145        self.setup = Box::new(setup);
146        self
147    }
148    /// this is run always. if min_duration in settings is set, then it is throttled to said value
149    pub fn with_update(
150        mut self,
151        update: impl FnMut(&mut InputMap<H>, &Display, &mut Settings, &ActiveEventLoop, &mut Window) + 'a
152    ) -> Self {
153        self.update = Box::new(update);
154        self
155    }
156    pub fn with_event_handler(
157        mut self,
158        event_handler: impl FnMut(Event<()>, &ActiveEventLoop, Option<&(Window, Display)>) + 'a
159    ) -> Self {
160        self.event_handler = Box::new(event_handler);
161        self
162    }
163    pub fn with_settings(mut self, settings: Settings) -> Self {
164        self.settings = settings;
165        self
166    }
167    pub fn with_window_settings(mut self, window_settings: SimpleWindowBuilder) -> Self {
168        self.window_settings = window_settings;
169        self
170    }
171}