258 lines
7.3 KiB
Rust
258 lines
7.3 KiB
Rust
use std::{
|
|
ops::{Deref, DerefMut},
|
|
sync::Arc,
|
|
time::Instant,
|
|
};
|
|
|
|
use imgui::{FontSource, MouseCursor, SuspendedContext, WindowToken};
|
|
use imgui_wgpu::{Renderer, RendererConfig};
|
|
use imgui_winit_support::WinitPlatform;
|
|
use pollster::block_on;
|
|
#[cfg(target_os = "windows")]
|
|
use winit::platform::windows::{CornerPreference, WindowAttributesExtWindows as _};
|
|
use winit::{
|
|
dpi::{LogicalSize, PhysicalSize, Size},
|
|
event_loop::ActiveEventLoop,
|
|
window::{Window, WindowAttributes},
|
|
};
|
|
|
|
pub struct WindowStateBuilder<'a> {
|
|
event_loop: &'a ActiveEventLoop,
|
|
attributes: WindowAttributes,
|
|
}
|
|
impl<'a> WindowStateBuilder<'a> {
|
|
pub fn new(event_loop: &'a ActiveEventLoop) -> Self {
|
|
let attributes = Window::default_attributes();
|
|
#[cfg(target_os = "windows")]
|
|
let attributes = attributes.with_corner_preference(CornerPreference::DoNotRound);
|
|
Self {
|
|
event_loop,
|
|
attributes,
|
|
}
|
|
}
|
|
|
|
pub fn with_title<T: Into<String>>(self, title: T) -> Self {
|
|
Self {
|
|
attributes: self.attributes.with_title(title),
|
|
..self
|
|
}
|
|
}
|
|
|
|
pub fn with_inner_size<S: Into<Size>>(self, size: S) -> Self {
|
|
Self {
|
|
attributes: self.attributes.with_inner_size(size),
|
|
..self
|
|
}
|
|
}
|
|
|
|
pub fn build(self) -> WindowState {
|
|
WindowState::new(self.event_loop, self.attributes)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct WindowState {
|
|
pub device: wgpu::Device,
|
|
pub queue: Arc<wgpu::Queue>,
|
|
pub window: Arc<Window>,
|
|
pub surface_desc: wgpu::SurfaceConfiguration,
|
|
pub surface: wgpu::Surface<'static>,
|
|
pub hidpi_factor: f64,
|
|
}
|
|
|
|
impl WindowState {
|
|
fn new(event_loop: &ActiveEventLoop, attributes: WindowAttributes) -> Self {
|
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
backends: wgpu::Backends::PRIMARY,
|
|
..Default::default()
|
|
});
|
|
|
|
let window = Arc::new(event_loop.create_window(attributes).unwrap());
|
|
|
|
let size = window.inner_size();
|
|
let hidpi_factor = window.scale_factor();
|
|
let surface = instance.create_surface(window.clone()).unwrap();
|
|
|
|
let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
|
compatible_surface: Some(&surface),
|
|
force_fallback_adapter: false,
|
|
}))
|
|
.unwrap();
|
|
|
|
let (device, queue) =
|
|
block_on(adapter.request_device(&wgpu::DeviceDescriptor::default(), None)).unwrap();
|
|
let queue = Arc::new(queue);
|
|
|
|
// Set up swap chain
|
|
let surface_desc = wgpu::SurfaceConfiguration {
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
|
width: size.width,
|
|
height: size.height,
|
|
present_mode: wgpu::PresentMode::Fifo,
|
|
desired_maximum_frame_latency: 2,
|
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
|
view_formats: vec![wgpu::TextureFormat::Bgra8Unorm],
|
|
};
|
|
|
|
surface.configure(&device, &surface_desc);
|
|
|
|
Self {
|
|
device,
|
|
queue,
|
|
window,
|
|
surface_desc,
|
|
surface,
|
|
hidpi_factor,
|
|
}
|
|
}
|
|
|
|
pub fn logical_size(&self) -> LogicalSize<u32> {
|
|
PhysicalSize::new(self.surface_desc.width, self.surface_desc.height)
|
|
.to_logical(self.hidpi_factor)
|
|
}
|
|
|
|
pub fn handle_resize(&mut self, size: &PhysicalSize<u32>) {
|
|
self.surface_desc.width = size.width;
|
|
self.surface_desc.height = size.height;
|
|
self.surface.configure(&self.device, &self.surface_desc);
|
|
}
|
|
}
|
|
|
|
pub struct ImguiState {
|
|
pub context: ContextGuard,
|
|
pub platform: WinitPlatform,
|
|
pub renderer: Renderer,
|
|
pub clear_color: wgpu::Color,
|
|
pub last_frame: Instant,
|
|
pub last_cursor: Option<MouseCursor>,
|
|
}
|
|
impl ImguiState {
|
|
pub fn new(window: &WindowState) -> Self {
|
|
let mut context_guard = ContextGuard::new();
|
|
let mut context = context_guard.lock().unwrap();
|
|
|
|
let mut platform = imgui_winit_support::WinitPlatform::new(&mut context);
|
|
platform.attach_window(
|
|
context.io_mut(),
|
|
&window.window,
|
|
imgui_winit_support::HiDpiMode::Default,
|
|
);
|
|
context.set_ini_filename(None);
|
|
|
|
let font_size = (16.0 * window.hidpi_factor) as f32;
|
|
context.io_mut().font_global_scale = (1.0 / window.hidpi_factor) as f32;
|
|
|
|
context.fonts().add_font(&[FontSource::TtfData {
|
|
data: include_bytes!("../../assets/selawk.ttf"),
|
|
size_pixels: font_size,
|
|
config: Some(imgui::FontConfig {
|
|
oversample_h: 1,
|
|
pixel_snap_h: true,
|
|
size_pixels: font_size,
|
|
..Default::default()
|
|
}),
|
|
}]);
|
|
|
|
let style = context.style_mut();
|
|
style.use_light_colors();
|
|
|
|
//
|
|
// Set up dear imgui wgpu renderer
|
|
//
|
|
let renderer_config = RendererConfig {
|
|
texture_format: window.surface_desc.format,
|
|
..Default::default()
|
|
};
|
|
|
|
let renderer = Renderer::new(&mut context, &window.device, &window.queue, renderer_config);
|
|
|
|
let last_frame = Instant::now();
|
|
let last_cursor = None;
|
|
|
|
drop(context);
|
|
Self {
|
|
context: context_guard,
|
|
platform,
|
|
renderer,
|
|
clear_color: wgpu::Color::BLACK,
|
|
last_frame,
|
|
last_cursor,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ContextGuard {
|
|
value: Option<SuspendedContext>,
|
|
}
|
|
|
|
impl ContextGuard {
|
|
fn new() -> Self {
|
|
Self {
|
|
value: Some(SuspendedContext::create()),
|
|
}
|
|
}
|
|
|
|
pub fn lock(&mut self) -> Option<ContextLock<'_>> {
|
|
let sus = self.value.take()?;
|
|
match sus.activate() {
|
|
Ok(ctx) => Some(ContextLock {
|
|
ctx: Some(ctx),
|
|
holder: self,
|
|
}),
|
|
Err(sus) => {
|
|
self.value = Some(sus);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ContextLock<'a> {
|
|
ctx: Option<imgui::Context>,
|
|
holder: &'a mut ContextGuard,
|
|
}
|
|
|
|
impl<'a> Deref for ContextLock<'a> {
|
|
type Target = imgui::Context;
|
|
fn deref(&self) -> &Self::Target {
|
|
self.ctx.as_ref().unwrap()
|
|
}
|
|
}
|
|
|
|
impl<'a> DerefMut for ContextLock<'a> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
self.ctx.as_mut().unwrap()
|
|
}
|
|
}
|
|
|
|
impl<'a> Drop for ContextLock<'a> {
|
|
fn drop(&mut self) {
|
|
self.holder.value = self.ctx.take().map(|c| c.suspend())
|
|
}
|
|
}
|
|
|
|
pub trait UiExt {
|
|
fn fullscreen_window(&self) -> Option<WindowToken<'_>>;
|
|
fn right_align_text<T: AsRef<str>>(&self, text: T, space: f32);
|
|
}
|
|
|
|
impl UiExt for imgui::Ui {
|
|
fn fullscreen_window(&self) -> Option<WindowToken<'_>> {
|
|
self.window("fullscreen")
|
|
.position([0.0, 0.0], imgui::Condition::Always)
|
|
.size(self.io().display_size, imgui::Condition::Always)
|
|
.flags(imgui::WindowFlags::NO_DECORATION)
|
|
.begin()
|
|
}
|
|
|
|
fn right_align_text<T: AsRef<str>>(&self, text: T, space: f32) {
|
|
let width = self.calc_text_size(text.as_ref())[0];
|
|
let [left, y] = self.cursor_pos();
|
|
let right = left + space;
|
|
self.set_cursor_pos([right - width, y]);
|
|
self.text(text);
|
|
}
|
|
}
|