use crate::Color;
use crate::ContextHandle;
use crate::ImageInfo;
use crate::ImageView;
use crate::WindowId;
use crate::WindowProxy;
use crate::backend::Context;
use crate::backend::util::GpuImage;
use crate::backend::util::UniformsBuffer;
use crate::error;
use crate::event::EventHandlerControlFlow;
use crate::event::WindowEvent;
use glam::Vec3;
use glam::{Affine2, Vec2};
use indexmap::IndexMap;
type DynWindowEventHandler = dyn FnMut(WindowHandle, &mut WindowEvent, &mut EventHandlerControlFlow);
pub(crate) struct Window {
pub window: winit::window::Window,
pub preserve_aspect_ratio: bool,
pub background_color: Color,
pub surface: wgpu::Surface,
pub uniforms: UniformsBuffer<WindowUniforms>,
pub image: Option<GpuImage>,
pub overlays: IndexMap<String, Overlay>,
pub user_transform: Affine2,
pub event_handlers: Vec<Box<DynWindowEventHandler>>,
}
pub(crate) struct Overlay {
pub image: GpuImage,
pub visible: bool,
}
pub struct WindowHandle<'a> {
context_handle: ContextHandle<'a>,
index: usize,
destroy_flag: Option<&'a mut bool>,
}
impl<'a> WindowHandle<'a> {
pub fn new(context_handle: ContextHandle<'a>, index: usize, destroy_flag: Option<&'a mut bool>) -> Self {
Self { context_handle, index, destroy_flag }
}
fn context(&self) -> &Context {
self.context_handle().context
}
unsafe fn context_mut(&mut self) -> &mut Context {
self.context_handle.context
}
fn window(&self) -> &Window {
&self.context().windows[self.index]
}
fn window_mut(&mut self) -> &mut Window {
let index = self.index;
unsafe { &mut self.context_mut().windows[index] }
}
pub fn id(&self) -> WindowId {
self.window().id()
}
pub fn proxy(&self) -> WindowProxy {
WindowProxy::new(self.id(), self.context_handle.proxy())
}
pub fn release(self) -> ContextHandle<'a> {
self.context_handle
}
pub fn context_handle(&self) -> &ContextHandle<'a> {
&self.context_handle
}
pub fn destroy(self) -> ContextHandle<'a> {
let WindowHandle { context_handle, index, destroy_flag } = self;
context_handle.context.windows.remove(index);
if let Some(destroy_flag) = destroy_flag {
*destroy_flag = true;
}
context_handle
}
pub fn set_title(&self, title: impl AsRef<str>) {
self.window().window.set_title(title.as_ref());
}
pub fn image_info(&self) -> Option<&ImageInfo> {
Some(self.window().image.as_ref()?.info())
}
pub fn preserve_aspect_ratio(&self) -> bool {
self.window().preserve_aspect_ratio
}
pub fn set_preserve_aspect_ratio(&mut self, preserve_aspect_ratio: bool) {
self.window_mut().preserve_aspect_ratio = preserve_aspect_ratio;
self.window().window.request_redraw();
}
pub fn background_color(&self) -> Color {
self.window().background_color
}
pub fn set_background_color(&mut self, background_color: Color) {
self.window_mut().background_color = background_color;
self.window().window.request_redraw();
}
pub fn set_visible(&mut self, visible: bool) {
self.window_mut().set_visible(visible);
self.window().window.request_redraw();
}
pub fn set_outer_position(&self, position: impl Into<glam::IVec2>) {
let position = position.into();
self.window().window.set_outer_position(winit::dpi::PhysicalPosition::new(position.x, position.y));
}
pub fn inner_size(&self) -> glam::UVec2 {
let size = self.window().window.inner_size();
glam::UVec2::new(size.width, size.height)
}
pub fn outer_size(&self) -> glam::UVec2 {
let size = self.window().window.outer_size();
glam::UVec2::new(size.width, size.height)
}
pub fn set_inner_size(&mut self, size: impl Into<glam::UVec2>) {
let size = size.into();
self.window_mut().window.set_inner_size(winit::dpi::PhysicalSize::new(size.x, size.y));
self.window().window.request_redraw();
}
pub fn set_resizable(&mut self, resizable: bool) {
self.window().window.set_resizable(resizable);
}
pub fn set_borderless(&mut self, borderless: bool) {
self.window().window.set_decorations(!borderless);
}
pub fn set_fullscreen(&mut self, fullscreen: bool) {
let opt = if fullscreen {
Some(winit::window::Fullscreen::Borderless(None))
} else {
None
};
self.window().window.set_fullscreen(opt);
}
pub fn is_fullscreen(&self) -> bool {
self.window().window.fullscreen().is_some()
}
pub fn set_image(&mut self, name: impl Into<String>, image: &ImageView) {
let image = self.context().make_gpu_image(name, image);
self.window_mut().image = Some(image);
self.window_mut().uniforms.mark_dirty(true);
self.window_mut().window.request_redraw();
}
pub fn set_overlay(&mut self, name: impl Into<String>, image: &ImageView, initially_visible: bool) {
use indexmap::map::Entry;
let name = name.into();
let image = self.context().make_gpu_image(name.clone(), image);
match self.window_mut().overlays.entry(name) {
Entry::Occupied(mut entry) => {
entry.get_mut().image = image;
},
Entry::Vacant(entry) => {
entry.insert(Overlay {
image,
visible: initially_visible,
});
},
};
self.window().window.request_redraw()
}
pub fn remove_overlay(&mut self, name: &impl AsRef<str>) -> bool {
let removed = self.window_mut().overlays.shift_remove(name.as_ref()).is_some();
self.window().window.request_redraw();
removed
}
pub fn clear_overlays(&mut self) {
self.window_mut().overlays.clear();
self.window().window.request_redraw()
}
pub fn is_overlay_visible(&mut self, name: impl AsRef<str>) -> Result<bool, error::UnknownOverlay> {
Ok(self.window().get_overlay(name)?.visible)
}
pub fn set_overlay_visible(&mut self, name: impl AsRef<str>, visible: bool) -> Result<(), error::UnknownOverlay> {
self.window_mut().get_overlay_mut(name)?.visible = visible;
self.window().window.request_redraw();
Ok(())
}
pub fn toggle_overlay_visible(&mut self, name: impl AsRef<str>) -> Result<(), error::UnknownOverlay> {
let overlay = self.window_mut().get_overlay_mut(name)?;
overlay.visible = !overlay.visible;
self.window().window.request_redraw();
Ok(())
}
pub fn set_all_overlays_visible(&mut self, visible: bool) {
for (_name, overlay) in &mut self.window_mut().overlays {
overlay.visible = visible;
}
self.window().window.request_redraw()
}
pub fn add_event_handler<F>(&mut self, handler: F)
where
F: 'static + FnMut(WindowHandle, &mut WindowEvent, &mut EventHandlerControlFlow),
{
self.window_mut().event_handlers.push(Box::new(handler))
}
pub fn transform(&self) -> Affine2 {
self.window().user_transform
}
pub fn effective_transform(&self) -> Affine2 {
self.window().calculate_uniforms().transform
}
pub fn set_transform(&mut self, transform: Affine2) {
self.window_mut().user_transform = transform;
self.window_mut().uniforms.mark_dirty(true);
self.window().window.request_redraw();
}
pub fn pre_apply_transform(&mut self, transform: Affine2) {
self.set_transform(transform * self.transform());
}
pub fn post_apply_transform(&mut self, transform: Affine2) {
self.set_transform(self.transform() * transform)
}
}
#[derive(Debug, Clone)]
pub struct WindowOptions {
pub preserve_aspect_ratio: bool,
pub background_color: Color,
pub start_hidden: bool,
pub size: Option<[u32; 2]>,
pub resizable: bool,
pub borderless: bool,
pub fullscreen: bool,
pub overlays_visible: bool,
pub default_controls: bool,
}
impl Default for WindowOptions {
fn default() -> Self {
Self::new()
}
}
impl WindowOptions {
pub fn new() -> Self {
Self {
preserve_aspect_ratio: true,
background_color: Color::black(),
start_hidden: false,
size: None,
resizable: true,
borderless: false,
fullscreen: false,
overlays_visible: true,
default_controls: true,
}
}
pub fn set_preserve_aspect_ratio(mut self, preserve_aspect_ratio: bool) -> Self {
self.preserve_aspect_ratio = preserve_aspect_ratio;
self
}
pub fn set_background_color(mut self, background_color: Color) -> Self {
self.background_color = background_color;
self
}
pub fn set_start_hidden(mut self, start_hidden: bool) -> Self {
self.start_hidden = start_hidden;
self
}
pub fn set_size(mut self, size: impl Into<Option<[u32; 2]>>) -> Self {
self.size = size.into();
self
}
pub fn set_resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
pub fn set_borderless(mut self, borderless: bool) -> Self {
self.borderless = borderless;
self
}
pub fn set_fullscreen(mut self, fullscreen: bool) -> Self {
self.fullscreen = fullscreen;
self
}
pub fn set_show_overlays(mut self, overlays_visible: bool) -> Self {
self.overlays_visible = overlays_visible;
self
}
pub fn set_default_controls(mut self, default_controls: bool) -> Self {
self.default_controls = default_controls;
self
}
}
impl Window {
pub fn id(&self) -> WindowId {
self.window.id()
}
pub fn set_visible(&mut self, visible: bool) {
self.window.set_visible(visible);
}
pub fn calculate_uniforms(&self) -> WindowUniforms {
if let Some(image) = &self.image {
let image_size = image.info().size.as_vec2();
if !self.preserve_aspect_ratio {
WindowUniforms::stretch(image_size)
.pre_apply_transform(self.user_transform)
} else {
let window_size = glam::UVec2::new(self.window.inner_size().width, self.window.inner_size().height).as_vec2();
WindowUniforms::fit(window_size, image_size)
.pre_apply_transform(self.user_transform)
}
} else {
WindowUniforms {
transform: self.user_transform,
image_size: Vec2::new(0.0, 0.0),
}
}
}
fn get_overlay(&self, name: impl AsRef<str>) -> Result<&Overlay, error::UnknownOverlay> {
let name = name.as_ref();
self.overlays.get(name)
.ok_or_else(|| error::UnknownOverlay { name: name.into() })
}
fn get_overlay_mut(&mut self, name: impl AsRef<str>) -> Result<&mut Overlay, error::UnknownOverlay> {
let name = name.as_ref();
self.overlays.get_mut(name)
.ok_or_else(|| error::UnknownOverlay { name: name.into() })
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct WindowUniforms {
pub transform: Affine2,
pub image_size: Vec2,
}
impl WindowUniforms {
pub fn no_image() -> Self {
Self::stretch(Vec2::new(0.0, 0.0))
}
pub fn stretch(image_size: Vec2) -> Self {
Self {
transform: Affine2::IDENTITY,
image_size,
}
}
pub fn fit(window_size: Vec2, image_size: Vec2) -> Self {
let ratios = image_size / window_size;
let w;
let h;
if ratios.x >= ratios.y {
w = 1.0;
h = ratios.y / ratios.x;
} else {
w = ratios.x / ratios.y;
h = 1.0;
}
let transform = Affine2::from_scale_angle_translation(Vec2::new(w, h), 0.0, 0.5 * Vec2::new(1.0 - w, 1.0 - h));
Self {
transform,
image_size,
}
}
pub fn pre_apply_transform(mut self, transform: Affine2) -> Self {
self.transform = transform * self.transform;
self
}
}
#[repr(C, align(8))]
#[derive(Debug, Copy, Clone)]
struct Vec2A8 {
pub x: f32,
pub y: f32,
}
#[repr(C, align(16))]
#[derive(Debug, Copy, Clone)]
struct Vec3A16 {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Mat3x3 {
pub cols: [Vec3A16; 3]
}
impl Vec2A8 {
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl Vec3A16 {
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
}
impl Mat3x3 {
pub const fn new(col0: Vec3A16, col1: Vec3A16, col2: Vec3A16) -> Self {
Self {
cols: [col0, col1, col2],
}
}
}
impl From<Vec2> for Vec2A8 {
fn from(other: Vec2) -> Self {
Self::new(other.x, other.y)
}
}
impl From<Vec3> for Vec3A16 {
fn from(other: Vec3) -> Self {
Self::new(other.x, other.y, other.z)
}
}
impl From<Affine2> for Mat3x3 {
fn from(other: Affine2) -> Self {
let x_axis = other.matrix2.x_axis;
let y_axis = other.matrix2.y_axis;
let z_axis = other.translation;
Self::new(
Vec3A16::new(x_axis.x, x_axis.y, 0.0),
Vec3A16::new(y_axis.x, y_axis.y, 0.0),
Vec3A16::new(z_axis.x, z_axis.y, 1.0),
)
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct WindowUniformsStd140 {
image_size: Vec2A8,
transform: Mat3x3,
}
unsafe impl crate::backend::util::ToStd140 for WindowUniforms {
type Output = WindowUniformsStd140;
fn to_std140(&self) -> Self::Output {
Self::Output {
image_size: self.image_size.into(),
transform: self.transform.into(),
}
}
}
pub(super) fn default_controls_handler(mut window: WindowHandle, event: &mut crate::event::WindowEvent, _control_flow: &mut crate::event::EventHandlerControlFlow) {
match event {
WindowEvent::MouseWheel(event) => {
let delta = match event.delta {
winit::event::MouseScrollDelta::LineDelta(_x, y) => y,
winit::event::MouseScrollDelta::PixelDelta(delta) => delta.y as f32 / 20.0,
};
let scale = 1.1f32.powf(delta);
let origin = event.position
.map(|pos| pos / window.inner_size().as_vec2())
.unwrap_or_else(|| glam::Vec2::new(0.5, 0.5));
let transform = glam::Affine2::from_scale_angle_translation(glam::Vec2::splat(scale), 0.0, origin - scale * origin);
window.pre_apply_transform(transform);
},
WindowEvent::MouseMove(event) => {
if event.buttons.is_pressed(crate::event::MouseButton::Left) {
let translation = (event.position - event.prev_position) / window.inner_size().as_vec2();
window.pre_apply_transform(Affine2::from_translation(translation));
}
},
_ => (),
}
}