diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 75d018d..5cb7abb 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -60,6 +60,8 @@ pub struct Context { // imgui a mutable pointer to it. clipboard_ctx: Box>, + // we need to store an owning reference to our PlatformViewportBackend and PlatformRendererBackend, + // so that it is ensured that PlatformIo::backend_platform_user_data and PlatformIo::backend_renderer_user_data remain valid #[cfg(feature = "docking")] platform_viewport_ctx: Box>, #[cfg(feature = "docking")] @@ -592,39 +594,77 @@ impl Context { #[cfg(feature = "docking")] impl Context { + /// Returns an immutable reference to the Context's [`PlatformIo`](crate::PlatformIo) object. pub fn platform_io(&self) -> &crate::PlatformIo { unsafe { // safe because PlatformIo is a transparent wrapper around sys::ImGuiPlatformIO + // and &self ensures we have shared ownership of PlatformIo. &*(sys::igGetPlatformIO() as *const crate::PlatformIo) } } + /// Returns a mutable reference to the Context's [`PlatformIo`](crate::PlatformIo) object. pub fn platform_io_mut(&mut self) -> &mut crate::PlatformIo { unsafe { // safe because PlatformIo is a transparent wrapper around sys::ImGuiPlatformIO + // and &mut self ensures exclusive ownership of PlatformIo. &mut *(sys::igGetPlatformIO() as *mut crate::PlatformIo) } } + /// Returns an immutable reference to the main [`Viewport`](crate::Viewport) pub fn main_viewport(&self) -> &crate::Viewport { - unsafe { &*(sys::igGetMainViewport() as *mut crate::Viewport) } + unsafe { + // safe because Viewport is a transparent wrapper around sys::ImGuiViewport + // and &self ensures we have shared ownership. + &*(sys::igGetMainViewport() as *mut crate::Viewport) + } } + /// Returns a mutable reference to the main [`Viewport`](crate::Viewport) pub fn main_viewport_mut(&mut self) -> &mut crate::Viewport { - unsafe { &mut *(sys::igGetMainViewport() as *mut crate::Viewport) } + unsafe { + // safe because Viewport is a transparent wrapper around sys::ImGuiViewport + // and &mut self ensures we have exclusive ownership. + &mut *(sys::igGetMainViewport() as *mut crate::Viewport) + } } + /// Tries to find and return a Viewport identified by `id`. + /// + /// # Returns + /// A [`Viewport`](crate::Viewport) with the given `id` + /// or `None` if no [`Viewport`](crate::Viewport) exists. pub fn viewport_by_id(&self, id: crate::Id) -> Option<&crate::Viewport> { - unsafe { (sys::igFindViewportByID(id.0) as *const crate::Viewport).as_ref() } + unsafe { + // safe because Viewport is a transparent wrapper around sys::ImGuiViewport + // and &self ensures shared ownership. + (sys::igFindViewportByID(id.0) as *const crate::Viewport).as_ref() + } } + /// Tries to find and return a Viewport identified by `id`. + /// + /// # Returns + /// A [`Viewport`](crate::Viewport) with the given `id` + /// or `None` if no [`Viewport`](crate::Viewport) exists. pub fn viewport_by_id_mut(&mut self, id: crate::Id) -> Option<&mut crate::Viewport> { - unsafe { (sys::igFindViewportByID(id.0) as *mut crate::Viewport).as_mut() } + unsafe { + // safe because Viewport is a transparent wrapper around sys::ImGuiViewport + // and &mut self ensures exclusive ownership. + (sys::igFindViewportByID(id.0) as *mut crate::Viewport).as_mut() + } } + /// Returns an iterator containing every [`Viewport`](crate::Viewport) that currently exists. pub fn viewports(&self) -> impl Iterator { let slice = self.platform_io().viewports.as_slice(); + // safe because &self ensures shared ownership unsafe { slice.iter().map(|ptr| &**ptr) } } + /// Returns an iterator containing every [`Viewport`](crate::Viewport) that currently exists. pub fn viewports_mut(&mut self) -> impl Iterator { let slice = self.platform_io_mut().viewports.as_slice(); + // safe because &self ensures exclusive ownership unsafe { slice.iter().map(|ptr| &mut **ptr) } } + /// Installs a [`PlatformViewportBackend`](crate::PlatformViewportBackend) that is used to + /// create platform windows on demand if a window is dragged outside of the main viewport. pub fn set_platform_backend(&mut self, backend: T) { let ctx = Box::new(UnsafeCell::new(crate::PlatformViewportContext { backend: Box::new(backend), @@ -638,7 +678,7 @@ impl Context { pio.platform_destroy_window = Some(crate::platform_io::platform_destroy_window); pio.platform_show_window = Some(crate::platform_io::platform_show_window); pio.platform_set_window_pos = Some(crate::platform_io::platform_set_window_pos); - // pio.platform_get_window_pos = Some(crate::platform_io::platform_get_window_pos); + // since pio.platform_get_window_pos is not a C compatible function, cimgui provides an extra function to set it. unsafe { crate::platform_io::ImGuiPlatformIO_Set_Platform_GetWindowPos( pio, @@ -646,7 +686,7 @@ impl Context { ); } pio.platform_set_window_size = Some(crate::platform_io::platform_set_window_size); - // pio.platform_get_window_size = Some(crate::platform_io::platform_get_window_size); + // since pio.platform_get_window_size is not a C compatible function, cimgui provides an extra function to set it. unsafe { crate::platform_io::ImGuiPlatformIO_Set_Platform_GetWindowSize( pio, @@ -665,6 +705,8 @@ impl Context { self.platform_viewport_ctx = ctx; } + /// Installs a [`RendererViewportBackend`](crate::RendererViewportBackend) that is used to + /// render extra viewports created by ImGui. pub fn set_renderer_backend(&mut self, backend: T) { let ctx = Box::new(UnsafeCell::new(crate::RendererViewportContext { backend: Box::new(backend), @@ -682,11 +724,15 @@ impl Context { self.renderer_viewport_ctx = ctx; } + /// Updates the extra Viewports created by ImGui. + /// Has to be called every frame if Viewports are enabled. pub fn update_platform_windows(&mut self) { unsafe { sys::igUpdatePlatformWindows(); } } + /// Basically just calls the [`PlatformViewportBackend`](crate::PlatformViewportBackend) and [`RendererViewportBackend`](crate::RendererViewportBackend) + /// functions. If you render your extra viewports manually this function is not needed at all. pub fn render_platform_windows_default(&mut self) { unsafe { sys::igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut()); diff --git a/imgui/src/io.rs b/imgui/src/io.rs index 46f933a..9f5bece 100644 --- a/imgui/src/io.rs +++ b/imgui/src/io.rs @@ -99,8 +99,10 @@ bitflags! { const RENDERER_HAS_VTX_OFFSET = sys::ImGuiBackendFlags_RendererHasVtxOffset; #[cfg(feature = "docking")] + /// Set if the platform backend supports viewports. const PLATFORM_HAS_VIEWPORTS = sys::ImGuiBackendFlags_PlatformHasViewports; #[cfg(feature = "docking")] + /// Set if the renderer backend supports viewports. const RENDERER_HAS_VIEWPORTS = sys::ImGuiBackendFlags_RendererHasViewports; } } diff --git a/imgui/src/platform_io.rs b/imgui/src/platform_io.rs index 889cbf8..d5c0523 100644 --- a/imgui/src/platform_io.rs +++ b/imgui/src/platform_io.rs @@ -8,6 +8,7 @@ use crate::{ Io, ViewportFlags, }; +/// Holds the information needed to enable multiple viewports. #[repr(C)] pub struct PlatformIo { pub(crate) platform_create_window: Option, @@ -38,6 +39,8 @@ pub struct PlatformIo { pub(crate) renderer_render_window: Option, pub(crate) renderer_swap_buffers: Option, + /// Holds information about the available monitors. + /// Should be initialized and updated by the [`PlatformViewportBackend`]. pub monitors: ImVector, pub(crate) viewports: ImVector<*mut Viewport>, @@ -97,20 +100,55 @@ fn test_platform_io_memory_layout() { assert_field_offset!(viewports, Viewports); } +/// Trait holding functions needed when the platform integration supports viewports. +/// +/// Register it via [`Context::set_platform_backend()`](crate::context::Context::set_platform_backend()) pub trait PlatformViewportBackend: 'static { + /// Called by imgui when a new [`Viewport`] is created. + /// + /// # Notes + /// This function should initiate the creation of a platform window. + /// The window should be invisible. fn create_window(&mut self, viewport: &mut Viewport); + /// Called by imgui when a [`Viewport`] is about to be destroyed. + /// + /// # Notes + /// This function should initiate the destruction of the platform window. fn destroy_window(&mut self, viewport: &mut Viewport); + /// Called by imgui to make a [`Viewport`] visible. fn show_window(&mut self, viewport: &mut Viewport); + /// Called by imgui to reposition a [`Viewport`]. + /// + /// # Notes + /// `pos` specifies the position of the windows content area (excluding title bar etc.) fn set_window_pos(&mut self, viewport: &mut Viewport, pos: [f32; 2]); + /// Called by imgui to get the position of a [`Viewport`]. + /// + /// # Notes + /// You should return the position of the window's content area (excluding title bar etc.) fn get_window_pos(&mut self, viewport: &mut Viewport) -> [f32; 2]; + /// Called by imgui to set the size of a [`Viewport`]. + /// + /// # Notes + /// `size` specifies the size of the window's content area (excluding title bar etc.) fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]); + /// Called by imgui to get the size of a [`Viewport`]. + /// + /// # Notes + /// you should return the size of the window's content area (excluding title bar etc.) fn get_window_size(&mut self, viewport: &mut Viewport) -> [f32; 2]; + /// Called by imgui to make a [`Viewport`] steal the focus. fn set_window_focus(&mut self, viewport: &mut Viewport); + /// Called by imgui to query whether a [`Viewport`] is in focus. fn get_window_focus(&mut self, viewport: &mut Viewport) -> bool; + /// Called by imgui to query whether a [`Viewport`] is minimized. fn get_window_minimized(&mut self, viewport: &mut Viewport) -> bool; + /// Called by imgui to set a [`Viewport`] title. fn set_window_title(&mut self, viewport: &mut Viewport, title: &str); + /// Called by imgui to set the opacity of an entire [`Viewport`]. + /// + /// If your backend does not support opactiy, it is safe to just do nothing in this function. fn set_window_alpha(&mut self, viewport: &mut Viewport, alpha: f32); - fn update_window(&mut self, viewport: &mut Viewport); fn render_window(&mut self, viewport: &mut Viewport); fn swap_buffers(&mut self, viewport: &mut Viewport); @@ -122,15 +160,19 @@ pub trait PlatformViewportBackend: 'static { ) -> i32; } +/// Used to get the current Contexts [`PlatformViewportContext`]. fn get_platform_ctx() -> &'static mut PlatformViewportContext { unsafe { + // should be safe as it is impossible to call any imgui function on a non-active context. &mut *((*(sys::igGetIO() as *const Io)).backend_platform_user_data as *mut PlatformViewportContext) } } +/// Used to get the current Contexts [`RendererViewportContext`]. fn get_renderer_ctx() -> &'static mut RendererViewportContext { unsafe { + // should be safe as it is impossible to call any imgui function on a non-active context. &mut *((*(sys::igGetIO() as *const Io)).backend_platform_user_data as *mut RendererViewportContext) } @@ -226,6 +268,7 @@ pub(crate) extern "C" fn platform_create_vk_surface( }) } +/// The default [`PlatformViewportBackend`], does nothing. pub(crate) struct DummyPlatformViewportBackend {} impl PlatformViewportBackend for DummyPlatformViewportBackend { fn create_window(&mut self, _viewport: &mut Viewport) { @@ -298,6 +341,7 @@ impl PlatformViewportBackend for DummyPlatformViewportBackend { } } +/// Just holds a [`PlatformViewportBackend`]. pub(crate) struct PlatformViewportContext { pub(crate) backend: Box, } @@ -310,9 +354,15 @@ impl PlatformViewportContext { } } +/// Trait that holds optional functions for a rendering backend to support multiple viewports. +/// +/// It is completely fine to not use this Backend at all, as all functions are optional. pub trait RendererViewportBackend: 'static { + /// Called after [`PlatformViewportBackend::create_window()`]. fn create_window(&mut self, viewport: &mut Viewport); + /// Called before [`PlatformViewportBackend::destroy_window()`]. fn destroy_window(&mut self, viewport: &mut Viewport); + /// Called after [`PlatformViewportBackend::set_window_size()`]. fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]); fn render_window(&mut self, viewport: &mut Viewport); fn swap_buffers(&mut self, viewport: &mut Viewport); @@ -340,6 +390,7 @@ pub(crate) extern "C" fn renderer_swap_buffers(viewport: *mut Viewport, _arg: *m ctx.backend.swap_buffers(unsafe { &mut *viewport }); } +/// The default [`RendererViewportBackend`], does nothing. pub(crate) struct DummyRendererViewportBackend {} impl RendererViewportBackend for DummyRendererViewportBackend { fn create_window(&mut self, _viewport: &mut Viewport) { @@ -363,6 +414,7 @@ impl RendererViewportBackend for DummyRendererViewportBackend { } } +/// Just holds a [`RendererViewportBackend`]. pub(crate) struct RendererViewportContext { pub(crate) backend: Box, } @@ -375,9 +427,12 @@ impl RendererViewportContext { } } +/// Describes an ImGui Viewport. #[repr(C)] pub struct Viewport { + /// The unique ID of this Viewport. pub id: crate::Id, + /// Flags that describe how the Viewport should behave. pub flags: ViewportFlags, pub pos: [f32; 2], pub size: [f32; 2], @@ -397,6 +452,7 @@ pub struct Viewport { } impl Viewport { + /// Returns the draw data of the respective Viewport. pub fn draw_data(&self) -> &crate::DrawData { unsafe { &*self.draw_data } } @@ -443,11 +499,20 @@ fn test_viewport_memory_layout() { assert_field_offset!(platform_request_close, PlatformRequestClose); } +/// Describes a monitor that can be used by ImGui. #[repr(C)] pub struct PlatformMonitor { + /// Position of the monitor on the virtual desktop. pub main_pos: [f32; 2], + /// Size of the monitor on the virtual desktop. pub main_size: [f32; 2], + /// Working position of the monitor, should exclude task bar etc. + /// + /// Set to `main_pos` if not known. pub work_pos: [f32; 2], + /// Working size of the monitor, should exclude task bar etc. + /// + /// Set to `work_size` if not known. pub work_size: [f32; 2], pub dpi_scale: f32, }