summaryrefslogblamecommitdiff
path: root/src/window_manager.rs
blob: 144f1c3311c5f9a048efcb64698a6504c5a41cdc (plain) (tree)


























































































































































                                                                                                                  
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()
    }
}