use crate::*;
use std::collections::HashMap;
use std::time::Duration;
use winit::{event::*, event_loop::{EventLoop, ControlFlow}, window::WindowId};
pub struct WindowManager {
event_loop: EventLoop<()>,
windows: HashMap<WindowId, Window>,
min_frame_duration: Duration,
}
impl WindowManager {
pub fn new() -> Self {
Self {
event_loop: EventLoop::new(),
windows: HashMap::new(),
min_frame_duration: Duration::ZERO,
}
}
pub fn with_frame_limit(mut self, limit: u64) -> Self {
self.min_frame_duration = match limit {
0 => Duration::ZERO,
_ => Duration::from_nanos(1_000_000_000 / limit),
};
return self;
}
/// Add a window to the window manager before the event loop begins.
pub fn add_window(&mut self, controller: Box<dyn WindowController>) {
let window = Window::new(&self.event_loop, controller);
self.windows.insert(window.id(), window);
}
/// Start the event loop, passing program control to the window manager.
pub fn run(mut self) -> ! {
self.event_loop.run(move |event, _window_target, control_flow| {
match event {
// Called when the event loop is first initialized.
Event::NewEvents(StartCause::Init) => (),
Event::NewEvents(..) => {
control_flow.set_wait_timeout(self.min_frame_duration);
}
// Called when an application suspends on a mobile platform.
Event::Suspended => (),
// Called when an application resumes, or after initialization on non-mobile platforms.
Event::Resumed => (),
Event::WindowEvent { window_id, event } => {
if let Some(window) = self.windows.get_mut(&window_id) {
use WindowEvent::*;
match event {
Resized(dim) => window.resize_buffer_and_surface(Dimensions::new(dim.width, dim.height)),
Moved(position) => window.controller.on_move(Point::new(position.x, position.y)),
Focused(is_focused) => window.controller.on_focus_change(is_focused),
CursorEntered { .. } => window.controller.on_cursor_enter(),
CursorLeft { .. } => window.controller.on_cursor_exit(),
CursorMoved { position: p, .. } => { window.move_cursor(Point::new(p.x as i32, p.y as i32)) }
MouseWheel {delta, ..} => match delta {
MouseScrollDelta::LineDelta(x, y) => {
let (x, y) = (x as f64, y as f64);
if x != 0.0 {window.controller.on_line_scroll_horizontal(x)}
if y != 0.0 {window.controller.on_line_scroll_vertical(y)}
}
MouseScrollDelta::PixelDelta(point) => {
let (x, y) = (point.x, point.y);
if x != 0.0 {window.controller.on_pixel_scroll_horizontal(x)}
if y != 0.0 {window.controller.on_pixel_scroll_vertical(y)}
}
}
MouseInput { state, button, .. } => match button {
MouseButton::Left => window.controller.on_left_mouse_button(state.into()),
MouseButton::Middle => window.controller.on_middle_mouse_button(state.into()),
MouseButton::Right => window.controller.on_right_mouse_button(state.into()),
MouseButton::Other(_) => (),
}
KeyboardInput { input, .. } => if let Ok(input) = input.try_into() { window.controller.on_keyboard_input(input)},
ModifiersChanged(state) => window.controller.on_keyboard_modifier_change(state),
ReceivedCharacter(character) => window.controller.on_character_input(character),
HoveredFile(path) => window.controller.on_file_hover(&path),
HoveredFileCancelled => window.controller.on_file_hover_cancel(),
DroppedFile(path) => window.controller.on_file_drop(&path),
CloseRequested => {
window.controller.on_close_request();
*control_flow = ControlFlow::Exit;
},
Destroyed => window.controller.on_close(),
Ime(_) => (),
AxisMotion { .. } => (),
Touch(_) => (),
TouchpadRotate { .. } => (),
TouchpadPressure { .. } => (),
TouchpadMagnify { .. } => (),
SmartMagnify { .. } => (),
ScaleFactorChanged { .. } => (),
ThemeChanged(_) => (),
Occluded(_) => (),
}
}
}
// Called before any render events are called.
Event::MainEventsCleared => {
for window in self.windows.values_mut() {
window.controller.on_process();
window.update_title();
window.update_window_size();
window.update_cursor_icon();
window.update_cursor_visible();
window.handle_render_request();
}
}
// Called if a window has requested to be rendered.
Event::RedrawRequested(window_id) => {
if let Some(window) = self.windows.get_mut(&window_id) {
window.render();
}
}
// Called after all rendering has completed, or if there were no render requests.
Event::RedrawEventsCleared => (),
// Called before the program closes.
Event::LoopDestroyed => (),
_ => (),
}
})
}
}