Support multiple windows, start on input UI
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
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<'_>>;
|
||||
}
|
||||
|
||||
impl UiExt for imgui::Ui {
|
||||
fn fullscreen_window(&self) -> Option<WindowToken<'_>> {
|
||||
self.window("fullscreen")
|
||||
.position([0.0, 0.0], imgui::Condition::Always)
|
||||
.size(self.window_size(), imgui::Condition::Always)
|
||||
.flags(imgui::WindowFlags::NO_DECORATION)
|
||||
.begin()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user