summaryrefslogtreecommitdiff
path: root/src/window_manager.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/window_manager.rs')
-rw-r--r--src/window_manager.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/window_manager.rs b/src/window_manager.rs
new file mode 100644
index 0000000..144f1c3
--- /dev/null
+++ b/src/window_manager.rs
@@ -0,0 +1,156 @@
+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()
+ }
+}