abletk/src/application.rs
2022-04-16 20:25:56 +03:00

137 lines
4.3 KiB
Rust
Executable file

/*
* Copyright (C) 2022 Umut İnan Erdoğan <umutinanerdogan@pm.me>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use winit::event::{Event, WindowEvent as WinitWindowEvent};
use winit::event_loop::EventLoop;
use winit::window::WindowId;
use crate::context::Context;
use crate::event::{
application::Event as ApplicationEvent,
window::Event as WindowEvent,
};
use crate::plugin::Plugin;
use crate::window::{Window, WindowBuilder};
macro_rules! emit_app_event {
($app:expr, $event:expr) => {
if let Some(handlers) = $app.events.get_mut(&$event) {
handlers.iter_mut().for_each(|handler| handler(&mut $app.ctx.borrow_mut()));
}
};
}
pub struct Application {
winit_event_loop: EventLoop<()>,
windows: HashMap<WindowId, Window>,
events: HashMap<ApplicationEvent, Vec<Box<dyn FnMut(&mut Context)>>>,
ctx: Rc<RefCell<Context>>,
}
impl Application {
pub fn new() -> Self {
Default::default()
}
pub fn apply_plugin<P>(self, plugin: P) -> Self
where
P: Plugin
{
plugin.apply(self)
}
pub fn add_window(mut self, builder: WindowBuilder) -> Self {
let window = builder
.build(self.ctx.clone(), &self.winit_event_loop)
.unwrap();
self.windows.insert(window.id(), window);
self
}
pub fn add_windows<const N: usize>(mut self,
builders: [WindowBuilder; N]
) -> Self {
self.windows.extend(builders.map(|builder| {
let window = builder
.build(self.ctx.clone(), &self.winit_event_loop)
.unwrap();
(window.id(), window)
}));
self
}
pub fn on_event(mut self,
event: ApplicationEvent,
handler: fn(&mut Context),
) -> Self {
if let Some(handlers) = self.events.get_mut(&event) {
handlers.push(Box::new(handler));
} else {
self.events.insert(event, vec![Box::new(handler)]);
}
self
}
/// This method enters the event loop. You probably don't want to call this
/// directly, the `launch` macro will call this in the generated main
/// function.
pub fn launch(mut self, rt: tokio::runtime::Runtime) {
self.ctx.borrow_mut().set_rt(rt);
self.winit_event_loop.run(move |event, target, control_flow| {
*control_flow = self.ctx.borrow().control_flow();
while let Some(builder) = self.ctx.borrow_mut().pop_window_builder() {
let window = builder.build(self.ctx.clone(), target).unwrap();
self.windows.insert(window.id(), window);
}
match event {
Event::WindowEvent {
window_id,
event: WinitWindowEvent::CloseRequested,
} => {
self.windows.get_mut(&window_id).unwrap()
.emit_event(WindowEvent::Closed);
self.windows.remove(&window_id);
if self.windows.is_empty() {
emit_app_event!(self, ApplicationEvent::AllWindowsClosed);
}
}
Event::WindowEvent {
window_id,
event: WinitWindowEvent::Resized(size)
} => self.windows.get_mut(&window_id).unwrap().resized(size),
Event::MainEventsCleared => {
// determine if state changed and request redraw if needed
// rinse and repeat for every window
}
Event::RedrawRequested(window_id) =>
self.windows.get_mut(&window_id).unwrap().render(),
_ => ()
}
})
}
}
impl Default for Application {
fn default() -> Self {
Self {
winit_event_loop: EventLoop::new(),
windows: Default::default(),
events: Default::default(),
ctx: Rc::new(RefCell::new(Context::new())),
}
}
}