use crate::*;
use nosferatu::*;
use std::collections::HashMap;
use winit::event::{Event, MouseButton, StartCause, WindowEvent, MouseScrollDelta};
use winit::event_loop::EventLoop;
use winit::window::WindowId;
/// Controls the entire program.
pub struct WindowManager<P: ProgramController> {
limiter: Option<FrameRateLimiter>,
frame_timer: FrameTimer,
event_loop: EventLoop<()>,
windows: HashMap<WindowId, Window>,
program: P,
}
impl WindowManager<DefaultProgramController> {
pub fn without_program() -> Self {
Self::with_program(DefaultProgramController {})
}
}
impl<P: 'static + ProgramController> WindowManager<P> {
pub fn with_program(program: P) -> Self {
Self {
limiter: None,
frame_timer: FrameTimer::one_second(),
event_loop: EventLoop::new(),
windows: HashMap::new(),
program,
}
}
pub fn with_frame_limit(mut self, limit: usize) -> Self {
self.limiter = Some(FrameRateLimiter::from_frame_rate(limit)); self
}
/// Used to create one or more windows before the event loop starts.
pub fn create_window(&mut self, controller: Box<dyn WindowController>) {
let window = unsafe { Window::new(&self.event_loop, controller) };
self.windows.insert(window.id(), window);
}
/// Starts the event loop, causing program control to be passed permanently to the window manager.
pub fn run(mut self) -> ! {
self.event_loop.run(move |event, window_target, control_flow| {
control_flow.set_poll();
match event {
// Event loop has just initialised (is only ever emitted once)
Event::NewEvents(StartCause::Init) => self.program.initialise(),
Event::WindowEvent { window_id, event } => {
if let Some(window) = self.windows.get_mut(&window_id) {
use WindowEvent::*;
match event {
Resized(dim) => window.resize_buffer(Dimensions::new(dim.width, dim.height)),
Moved(p) => window.controller.on_move(Point::new(p.x, p.y)),
Focused(state) => window.controller.on_focus_change(state),
CursorEntered { .. } => window.controller.on_mouse_enter(),
CursorLeft { .. } => window.controller.on_mouse_exit(),
CursorMoved { position, .. } => {
let point = Point::new(position.x as i32, position.y as i32);
window.move_mouse(point);
}
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()),
_ => (),
}
window.bump_mouse();
}
KeyboardInput { input, .. } => window.controller.on_keyboard_input(input.into()),
ModifiersChanged(state) => window.controller.on_keyboard_modifier_change(state),
ReceivedCharacter(c) => window.controller.on_character_received(c),
HoveredFile(path) => window.controller.on_file_hovered(path),
DroppedFile(path) => window.controller.on_file_dropped(path),
// Tell the window, and let it raise its own event to close itself
// When all windows are closed: control_flow.set_exit_with_code(0);
CloseRequested => todo!("window.controller.on_close_requested()"),
_ => (),
}
}
}
// Called each loop before any rendering would begin.
Event::MainEventsCleared => {
self.frame_timer.tick();
// println!("{}", self.frame_timer.frame_rate());
self.program.on_process(&mut |controller: Box<dyn WindowController>| {
let window = unsafe { Window::new(window_target, controller) };
self.windows.insert(window.id(), window);
});
for window in self.windows.values_mut() {
window.controller.on_process();
let cursor_icon = window.controller.cursor_icon();
window.set_cursor_icon(cursor_icon);
// let resizable = window.controller.resizable();
// window.set_resizable(resizable);
// let cursor_visible = window.controller.cursor_visible();
// window.set_cursor_visible(cursor_visible);
window.check_render_request();
let title = window.controller.title();
window.set_title(&title);
let minimum_dimensions = window.controller.minimum_dimensions();
window.set_minimum_dimensions(minimum_dimensions);
let maximum_dimensions = window.controller.maximum_dimensions();
window.set_maximum_dimensions(maximum_dimensions);
}
}
// Called if a render was requested for a window
Event::RedrawRequested(window_id) => {
if let Some(window) = self.windows.get_mut(&window_id) {
window.render();
}
}
// Called after rendering has completed
Event::RedrawEventsCleared => {
if let Some(limiter) = &mut self.limiter {
limiter.tick();
}
}
_other => {} // todo!("{:?}", other),
}
})
}
}
impl Default for WindowManager<DefaultProgramController> {
fn default() -> Self {
Self::without_program()
}
}