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