From daf039c098333e6501a850031af8e6bac2864ebc Mon Sep 17 00:00:00 2001 From: Robin Quint Date: Tue, 1 Mar 2022 13:38:20 +0100 Subject: [PATCH] WIP --- imgui-examples/Cargo.toml | 2 +- imgui-examples/examples/hello_world.rs | 3 +- imgui-examples/examples/support/mod.rs | 7 +- imgui-winit-support/Cargo.toml | 4 +- imgui-winit-support/src/lib.rs | 239 ++++++++++++++++++++++++- imgui/src/context.rs | 24 ++- imgui/src/lib.rs | 2 +- imgui/src/platform_io.rs | 2 +- 8 files changed, 269 insertions(+), 14 deletions(-) diff --git a/imgui-examples/Cargo.toml b/imgui-examples/Cargo.toml index 639b0ba..148d06c 100644 --- a/imgui-examples/Cargo.toml +++ b/imgui-examples/Cargo.toml @@ -15,4 +15,4 @@ glium = { version = "0.31", default-features = true } image = "0.23" imgui = { path = "../imgui", features = ["tables-api", "docking"] } imgui-glium-renderer = { path = "../imgui-glium-renderer" } -imgui-winit-support = { path = "../imgui-winit-support" } +imgui-winit-support = { path = "../imgui-winit-support", features=["viewports"] } diff --git a/imgui-examples/examples/hello_world.rs b/imgui-examples/examples/hello_world.rs index 8728558..0b21816 100644 --- a/imgui-examples/examples/hello_world.rs +++ b/imgui-examples/examples/hello_world.rs @@ -3,7 +3,8 @@ use imgui::*; mod support; fn main() { - let system = support::init(file!()); + let mut system = support::init(file!()); + system.imgui.io_mut().config_flags.insert(ConfigFlags::VIEWPORTS_ENABLE); let mut value = 0; let choices = ["test test this is 1", "test test this is 2"]; diff --git a/imgui-examples/examples/support/mod.rs b/imgui-examples/examples/support/mod.rs index 44e8825..0924593 100644 --- a/imgui-examples/examples/support/mod.rs +++ b/imgui-examples/examples/support/mod.rs @@ -43,6 +43,7 @@ pub fn init(title: &str) -> System { } let mut platform = WinitPlatform::init(&mut imgui); + WinitPlatform::init_viewports(&mut imgui, &event_loop); { let gl_window = display.gl_window(); let window = gl_window.window(); @@ -123,7 +124,7 @@ impl System { } = self; let mut last_frame = Instant::now(); - event_loop.run(move |event, _, control_flow| match event { + event_loop.run(move |event, window_target, control_flow| match event { Event::NewEvents(_) => { let now = Instant::now(); imgui.io_mut().update_delta_time(now - last_frame); @@ -154,6 +155,9 @@ impl System { .render(&mut target, draw_data) .expect("Rendering failed"); target.finish().expect("Failed to swap buffers"); + + imgui.update_platform_windows(); + platform.update_viewports(&mut imgui, window_target); } Event::WindowEvent { event: WindowEvent::CloseRequested, @@ -162,6 +166,7 @@ impl System { event => { let gl_window = display.gl_window(); platform.handle_event(imgui.io_mut(), gl_window.window(), &event); + platform.handle_viewport_event(&mut imgui, gl_window.window(), &event); } }) } diff --git a/imgui-winit-support/Cargo.toml b/imgui-winit-support/Cargo.toml index e105b0e..f9ca765 100644 --- a/imgui-winit-support/Cargo.toml +++ b/imgui-winit-support/Cargo.toml @@ -20,9 +20,11 @@ winit-25 = { version = "0.25", package = "winit", default-features = false, opti winit-26 = { version = "0.26", package = "winit", default-features = false, optional = true } [features] -default = ["winit-26/default"] +default = ["winit-26/default", "viewports"] test = ["winit-23/default", "winit-24/default", "winit-25/default", "winit-26/default"] +viewports = [ "imgui/docking" ] + # This is phrased as a negative (unlike most features) so that it needs to be # explicitly disabled (and `default-features = false` won't do it). To avoid # problems from this we don't expose this in the public API in any way, keeping diff --git a/imgui-winit-support/src/lib.rs b/imgui-winit-support/src/lib.rs index 3c2c65d..7f311ca 100644 --- a/imgui-winit-support/src/lib.rs +++ b/imgui-winit-support/src/lib.rs @@ -189,7 +189,7 @@ use winit_20 as winit; use winit_19 as winit; use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui}; -use std::cell::Cell; +use std::{cell::Cell, collections::HashMap}; use std::cmp::Ordering; use winit::dpi::{LogicalPosition, LogicalSize}; @@ -342,6 +342,9 @@ pub struct WinitPlatform { hidpi_factor: f64, cursor_cache: Option, mouse_buttons: [Button; 5], + + #[cfg(feature = "viewports")] + windows: HashMap, } #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -444,6 +447,107 @@ impl HiDpiMode { } } +#[cfg(feature = "viewports")] +struct ViewportBackend {} + +#[cfg(feature = "viewports")] +impl imgui::PlatformViewportBackend for ViewportBackend { + fn create_window(&mut self, viewport: &mut imgui::Viewport) { + viewport.platform_user_data = Box::into_raw(Box::new(ViewportState { + create: true, + create_flags: viewport.flags, + set_show: false, + set_pos: None, + set_size: None, + set_focus: false, + set_title: None, + pos: [0.0, 0.0], + size: [0.0, 0.0], + focus: false, + minimized: false, + })) as *mut _; + } + + fn destroy_window(&mut self, viewport: &mut imgui::Viewport) { + unsafe { + Box::from_raw(viewport.platform_user_data as *mut ViewportState); + } + viewport.platform_user_data = std::ptr::null_mut(); + } + + fn show_window(&mut self, viewport: &mut imgui::Viewport) { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.set_show = true; + } + + fn set_window_pos(&mut self, viewport: &mut imgui::Viewport, pos: [f32; 2]) { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.set_pos = Some(pos); + } + + fn get_window_pos(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.pos + } + + fn set_window_size(&mut self, viewport: &mut imgui::Viewport, size: [f32; 2]) { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.set_size = Some(size); + } + + fn get_window_size(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.size + } + + fn set_window_focus(&mut self, viewport: &mut imgui::Viewport) { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.set_focus = true; + } + + fn get_window_focus(&mut self, viewport: &mut imgui::Viewport) -> bool { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.focus + } + + fn get_window_minimized(&mut self, viewport: &mut imgui::Viewport) -> bool { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.focus + } + + fn set_window_title(&mut self, viewport: &mut imgui::Viewport, title: &str) { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + state.set_title = Some(title.to_string()); + } + + fn set_window_alpha(&mut self, _viewport: &mut imgui::Viewport, _alpha: f32) {} + + fn update_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn render_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn swap_buffers(&mut self, _viewport: &mut imgui::Viewport) {} + + fn create_vk_surface(&mut self, _viewport: &mut imgui::Viewport, _instance: u64, _out_surface: &mut u64) -> i32 { 0 } +} + +#[cfg(feature = "viewports")] +struct ViewportState { + create: bool, + create_flags: imgui::ViewportFlags, + + set_show: bool, + set_pos: Option<[f32; 2]>, + set_size: Option<[f32; 2]>, + set_focus: bool, + set_title: Option, + + pos: [f32; 2], + size: [f32; 2], + focus: bool, + minimized: bool, +} + impl WinitPlatform { /// Initializes a winit platform instance and configures imgui. /// @@ -489,8 +593,102 @@ impl WinitPlatform { hidpi_factor: 1.0, cursor_cache: None, mouse_buttons: [Button::INIT; 5], + + #[cfg(feature = "viewports")] + windows: HashMap::new(), } } + + #[cfg(feature = "viewports")] + pub fn init_viewports(imgui: &mut Context, event_loop: &winit::event_loop::EventLoop) { + let io = imgui.io_mut(); + + io.backend_flags.insert(BackendFlags::PLATFORM_HAS_VIEWPORTS); + + imgui.set_platform_backend(ViewportBackend {}); + + let mut monitors = Vec::new(); + for monitor in event_loop.available_monitors() { + monitors.push(imgui::PlatformMonitor { + main_pos: [monitor.position().x as f32, monitor.position().y as f32], + main_size: [monitor.size().width as f32, monitor.size().height as f32], + work_pos: [monitor.position().x as f32, monitor.position().y as f32], + work_size: [monitor.size().width as f32, monitor.size().height as f32], + dpi_scale: 1.0, + }); + } + imgui.platform_io_mut().monitors.replace_from_slice(&monitors); + + let main_viewport = imgui.main_viewport_mut(); + main_viewport.platform_user_data = Box::into_raw(Box::new(ViewportState { + create: false, + create_flags: imgui::ViewportFlags::empty(), + set_show: false, + set_pos: None, + set_size: None, + set_focus: false, + set_title: None, + pos: [0.0, 0.0], + size: [0.0, 0.0], + focus: true, + minimized: false, + })) as *mut _; + } + + #[cfg(feature = "viewports")] + pub fn update_viewports(&mut self, imgui: &mut Context, window_target: &winit::event_loop::EventLoopWindowTarget) { + // remove destroyed windows + self.windows.retain(|id, _| { + imgui.viewport_by_id(*id).is_some() + }); + + // handle new viewports + for viewport in imgui.viewports_mut() { + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + + if state.create { + let window = winit::window::WindowBuilder::new() + .with_always_on_top(state.create_flags.contains(imgui::ViewportFlags::TOP_MOST)) + // .with_decorations(!state.create_flags.contains(imgui::ViewportFlags::NO_DECORATION)) + .with_resizable(true) + .with_visible(false) + .build(window_target) + .unwrap(); + + self.windows.insert(viewport.id, window); + + state.create = false; + } + } + + // handle other viewport events + for (id, wnd) in &self.windows { + let viewport = imgui.viewport_by_id_mut(*id).unwrap(); + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + + if state.set_show { + wnd.set_visible(true); + state.set_show = false; + } + if let Some(pos) = &state.set_pos { + wnd.set_outer_position(winit::dpi::LogicalPosition::new(pos[0], pos[1])); + state.set_pos = None; + } + if let Some(size) = &state.set_size { + wnd.set_inner_size(winit::dpi::LogicalSize::new(size[0], size[1])); + state.set_size = None; + } + if state.set_focus { + wnd.focus_window(); + state.set_focus = false; + } + if let Some(title) = &state.set_title { + wnd.set_title(title); + state.set_title = None; + } + } + } + /// Attaches the platform instance to a winit window. /// /// This function configures imgui-rs in the following ways: @@ -847,6 +1045,45 @@ impl WinitPlatform { _ => (), } } + #[cfg(feature = "viewports")] + pub fn handle_viewport_event(&mut self, imgui: &mut imgui::Context, main_window: &Window, event: &Event) { + match *event { + Event::WindowEvent { window_id, ref event } => { + let viewport = { + if window_id == main_window.id() { + imgui.main_viewport_mut() + } else { + let imgui_id = self.windows.iter().find(|(id, wnd)| wnd.id() == window_id).map(|(id, wnd)| *id).unwrap(); + imgui.viewport_by_id_mut(imgui_id).unwrap() + } + }; + let state = unsafe{&mut *(viewport.platform_user_data as *mut ViewportState)}; + + match *event { + WindowEvent::Resized(new_size) => { + state.size = [new_size.width as f32, new_size.height as f32]; + }, + WindowEvent::Moved(new_pos) => { + state.pos = [new_pos.x as f32, new_pos.y as f32]; + }, + WindowEvent::CloseRequested => { + viewport.platform_request_close = true; + }, + WindowEvent::Focused(focus) => { + state.focus = focus; + }, + WindowEvent::CursorMoved { position, .. } => { + let mut pos = state.pos; + pos[0] += position.x as f32; + pos[1] += position.y as f32; + imgui.io_mut().mouse_pos = pos; + }, + _ => {}, + } + }, + _ => {}, + } + } #[cfg(all( not(any( feature = "winit-26", diff --git a/imgui/src/context.rs b/imgui/src/context.rs index e822b83..00d780a 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -9,7 +9,7 @@ use crate::clipboard::{ClipboardBackend, ClipboardContext}; use crate::fonts::atlas::{FontAtlas, FontId, SharedFontAtlas}; use crate::io::Io; use crate::style::Style; -use crate::{sys, DrawData, PlatformIo, PlatformViewportContext, RendererViewportContext, PlatformViewportBackend, RendererViewportBackend, Viewport}; +use crate::{sys, DrawData, PlatformIo, PlatformViewportContext, RendererViewportContext, PlatformViewportBackend, RendererViewportBackend, Viewport, Id}; use crate::{MouseCursor, Ui}; /// An imgui-rs context. @@ -569,16 +569,26 @@ impl Context { &mut *(sys::igGetMainViewport() as *mut Viewport) } } - pub fn viewport_by_id(&self, id: *mut c_void) -> Option<&Viewport> { + pub fn viewport_by_id(&self, id: Id) -> Option<&Viewport> { unsafe { - let ptr = sys::igFindViewportByPlatformHandle(id) as *const Viewport; - ptr.as_ref() + (sys::igFindViewportByID(id.0) as *const Viewport).as_ref() } } - pub fn viewport_by_id_mut(&mut self, id: *mut c_void) -> Option<&mut Viewport> { + pub fn viewport_by_id_mut(&mut self, id: Id) -> Option<&mut Viewport> { unsafe { - let ptr = sys::igFindViewportByPlatformHandle(id) as *mut Viewport; - ptr.as_mut() + (sys::igFindViewportByID(id.0) as *mut Viewport).as_mut() + } + } + pub fn viewports(&self) -> impl Iterator { + let slice = self.platform_io().viewports.as_slice(); + unsafe { + slice.iter().map(|ptr| &**ptr) + } + } + pub fn viewports_mut(&mut self) -> impl Iterator { + let slice = self.platform_io_mut().viewports.as_slice(); + unsafe { + slice.iter().map(|ptr| &mut **ptr) } } /// Returns an immutable reference to the user interface style diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index e3d0a31..6958686 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -284,7 +284,7 @@ impl Ui { /// Now, however, it is made from the `Ui` object directly, with a few /// deprecated helper methods here. #[repr(transparent)] -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash)] pub struct Id(pub(crate) u32); impl Id { diff --git a/imgui/src/platform_io.rs b/imgui/src/platform_io.rs index 69aab2b..9e0c696 100644 --- a/imgui/src/platform_io.rs +++ b/imgui/src/platform_io.rs @@ -334,7 +334,7 @@ impl RendererViewportContext { #[cfg(feature = "docking")] #[repr(C)] pub struct Viewport { - pub(crate) id: crate::Id, + pub id: crate::Id, pub flags: ViewportFlags, pub(crate) pos: [f32; 2], pub(crate) size: [f32; 2],