summaryrefslogblamecommitdiff
path: root/src/window.rs
blob: ffe35e83417957f32b059483a75e0f920c5c6097 (plain) (tree)
1
2
3
4
5
6
7
8
9
             
                         
                                             
                            

                                              
                                  
                           


                                                     

             








                                                                                                       
 
              
                       
                   
                                                  


                                                    


                                  
                        
     
                                                        
     


                                                                                
     


                                                                          
     

                                                                          
     
                                                                            
     






                                                                            

         
















                                                                               

         
                                             
                                                                               
                                             


                              










                                                                                   
     
 
 
                                                                                      
 


                                                                                    
 
use crate::*;

use std::num::NonZeroU32;
use winit::dpi::{Size, PhysicalSize};
use winit::event_loop::EventLoopWindowTarget;
use winit::window::WindowId;

pub struct Window {
    pub controller: Box<dyn WindowController>,
    window: winit::window::Window,
    buffer: Buffer,
    dimensions: Dimensions,
    #[allow(dead_code)] context: softbuffer::Context,
    surface: softbuffer::Surface,
    current_render_hint: RenderHint,
    previous_cursor_position: Option<Point>,
}

impl Window {
    pub fn new(event_loop: &EventLoopWindowTarget<()>, controller: Box<dyn WindowController>) -> Self {
        let window = winit::window::WindowBuilder::new()
            .with_title(controller.title())
            .with_resizable(controller.is_resizable())
            .with_inner_size(dim_to_size(controller.initial_size()))
            .build(event_loop)
            .unwrap();

        let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
        let surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();

        Self {
            controller,
            window,
            buffer: Buffer::new(Dimensions::ZERO),
            dimensions: Dimensions::ZERO,
            context,
            surface,
            previous_cursor_position: None,
            current_render_hint: RenderHint::Redraw,
        }
    }

    pub fn id(&self) -> WindowId {
        self.window.id()
    }

    pub fn update_title(&mut self) {
        self.window.set_title(&self.controller.title());
    }

    pub fn update_cursor_icon(&mut self) {
        let icon = self.controller.cursor_icon().unwrap_or(CursorIcon::Default);
        self.window.set_cursor_icon(icon);
    }

    pub fn update_minimum_size(&mut self) {
        let size = self.controller.minimum_size().map(|d| dim_to_size(d));
        self.window.set_min_inner_size(size);
    }

    pub fn update_maximum_size(&mut self) {
        let size = self.controller.maximum_size().map(|d| dim_to_size(d));
        self.window.set_max_inner_size(size);
    }

    pub fn update_cursor_visible(&mut self) {
        self.window.set_cursor_visible(self.controller.is_cursor_visible());
    }

    pub fn update_resizable(&mut self) {
        let is_resizable = self.controller.is_resizable();
        self.window.set_resizable(is_resizable);
        // Hack to force window to be impossible to resize on DWM, where the
        // 'is_resizable' window attribute isn't respected.
        if !is_resizable {
            self.window.set_min_inner_size(Some(self.window.inner_size()));
            self.window.set_max_inner_size(Some(self.window.inner_size()));
        }
    }

    /// Resize the frame buffer to be the new size of the window.
    pub fn resize(&mut self, dimensions: Dimensions) {
        if self.dimensions == dimensions { return }
        self.dimensions = dimensions;
        self.buffer.resize(dimensions);
        if let Some((width, height)) = dim_to_nonzero_size(dimensions) {
            self.surface.resize(width, height).unwrap();
        };
        self.controller.on_resize(dimensions);
        self.current_render_hint = RenderHint::Redraw;
    }

    pub fn move_cursor(&mut self, position: Point) {
        // The cursor position is rounded to i32 from f64, so we need to ignore
        // duplicate consecutive cursor positions.
        if self.previous_cursor_position != Some(position) {
            self.previous_cursor_position = Some(position);
            self.controller.on_cursor_move(position);
        }
    }

    pub fn handle_render_request(&mut self) {
        if let RenderRequest::Render(hint) = self.controller.render_request() {
            self.current_render_hint &= hint;
            self.window.request_redraw();
        }
    }

    pub fn render(&mut self) {
        let size = self.window.inner_size();
        let dim = Dimensions::new(size.width, size.height);
        self.resize(dim);
        self.controller.on_render(&mut self.buffer, self.current_render_hint);
        let buffer = self.buffer.as_u32_slice();
        let mut surface = self.surface.buffer_mut().unwrap();
        let buffer_len = buffer.len();
        let surface_len = surface.len();
        surface[..buffer_len].copy_from_slice(&buffer[..surface_len]);
        surface.present().unwrap();
        // Reset current_render_hint back to the lowest variant for the next frame.
        self.current_render_hint = RenderHint::Update;
    }
}

fn dim_to_size(dimensions: Dimensions) -> Size {
    Size::Physical( PhysicalSize { width:dimensions.width, height:dimensions.height })
}

fn dim_to_nonzero_size(dimensions: Dimensions) -> Option<(NonZeroU32, NonZeroU32)> {
    let width = NonZeroU32::new(dimensions.width)?;
    let height = NonZeroU32::new(dimensions.height)?;
    return Some((width, height));
}