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, 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, } impl Window { pub fn new(event_loop: &EventLoopWindowTarget<()>, controller: Box) -> 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)); }