mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 05:28:35 +00:00
340 lines
11 KiB
Rust
340 lines
11 KiB
Rust
//! Example demonstrating multi-viewport rendering support in imgui-rs.
|
|
//!
|
|
//! This example shows how to:
|
|
//! - Enable viewport support
|
|
//! - Iterate over all viewports
|
|
//! - Safely render each viewport's DrawData
|
|
//! - Implement platform and renderer viewport backends
|
|
|
|
use imgui::*;
|
|
use std::collections::HashMap;
|
|
|
|
/// Example renderer that can handle multiple viewports
|
|
struct MyRenderer {
|
|
// In a real implementation, you'd store per-viewport rendering state here
|
|
viewport_renderers: HashMap<Id, ViewportRenderer>,
|
|
}
|
|
|
|
struct ViewportRenderer {
|
|
// Per-viewport rendering resources (e.g., framebuffers, textures)
|
|
_id: Id,
|
|
}
|
|
|
|
impl MyRenderer {
|
|
fn new() -> Self {
|
|
Self {
|
|
viewport_renderers: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Render the main viewport (backward compatible method)
|
|
fn render_main(&mut self, draw_data: &DrawData) {
|
|
println!("Rendering main viewport");
|
|
// In a real implementation, you'd render the draw data here
|
|
self.render_draw_data(draw_data);
|
|
}
|
|
|
|
/// Render a specific viewport
|
|
fn render_viewport(&mut self, viewport_id: Id, draw_data: &DrawData) {
|
|
println!("Rendering viewport: {:?}", viewport_id);
|
|
|
|
// Get or create renderer for this viewport
|
|
let _renderer = self.viewport_renderers
|
|
.entry(viewport_id)
|
|
.or_insert_with(|| ViewportRenderer { _id: viewport_id });
|
|
|
|
// In a real implementation, you'd render the draw data here
|
|
self.render_draw_data(draw_data);
|
|
}
|
|
|
|
fn render_draw_data(&self, draw_data: &DrawData) {
|
|
println!(
|
|
" DrawData: {} draw lists, {} vertices, {} indices",
|
|
draw_data.draw_lists_count(),
|
|
draw_data.total_vtx_count,
|
|
draw_data.total_idx_count
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Example platform backend implementation
|
|
#[cfg(feature = "docking")]
|
|
struct MyPlatformBackend {
|
|
// Platform-specific window handles would be stored here
|
|
windows: HashMap<Id, PlatformWindow>,
|
|
}
|
|
|
|
#[cfg(feature = "docking")]
|
|
struct PlatformWindow {
|
|
_id: Id,
|
|
// In a real implementation, this would be an OS window handle
|
|
}
|
|
|
|
#[cfg(feature = "docking")]
|
|
impl PlatformViewportBackend for MyPlatformBackend {
|
|
fn create_window(&mut self, viewport: &mut Viewport) {
|
|
println!("Creating window for viewport: {:?}", viewport.id);
|
|
self.windows.insert(
|
|
viewport.id,
|
|
PlatformWindow { _id: viewport.id }
|
|
);
|
|
}
|
|
|
|
fn destroy_window(&mut self, viewport: &mut Viewport) {
|
|
println!("Destroying window for viewport: {:?}", viewport.id);
|
|
self.windows.remove(&viewport.id);
|
|
}
|
|
|
|
fn show_window(&mut self, viewport: &mut Viewport) {
|
|
println!("Showing window for viewport: {:?}", viewport.id);
|
|
}
|
|
|
|
fn set_window_pos(&mut self, viewport: &mut Viewport, pos: [f32; 2]) {
|
|
println!("Setting window position for viewport {:?}: {:?}", viewport.id, pos);
|
|
}
|
|
|
|
fn get_window_pos(&mut self, viewport: &mut Viewport) -> [f32; 2] {
|
|
viewport.pos
|
|
}
|
|
|
|
fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]) {
|
|
println!("Setting window size for viewport {:?}: {:?}", viewport.id, size);
|
|
}
|
|
|
|
fn get_window_size(&mut self, viewport: &mut Viewport) -> [f32; 2] {
|
|
viewport.size
|
|
}
|
|
|
|
fn set_window_focus(&mut self, viewport: &mut Viewport) {
|
|
println!("Setting focus to viewport: {:?}", viewport.id);
|
|
}
|
|
|
|
fn get_window_focus(&mut self, _viewport: &mut Viewport) -> bool {
|
|
// In a real implementation, query OS for window focus
|
|
true
|
|
}
|
|
|
|
fn get_window_minimized(&mut self, _viewport: &mut Viewport) -> bool {
|
|
// In a real implementation, query OS for window state
|
|
false
|
|
}
|
|
|
|
fn set_window_title(&mut self, viewport: &mut Viewport, title: &str) {
|
|
println!("Setting window title for viewport {:?}: {}", viewport.id, title);
|
|
}
|
|
|
|
fn set_window_alpha(&mut self, viewport: &mut Viewport, alpha: f32) {
|
|
println!("Setting window alpha for viewport {:?}: {}", viewport.id, alpha);
|
|
}
|
|
|
|
fn update_window(&mut self, viewport: &mut Viewport) {
|
|
// Update platform window state
|
|
println!("Updating window for viewport: {:?}", viewport.id);
|
|
}
|
|
|
|
fn render_window(&mut self, viewport: &mut Viewport) {
|
|
// Platform-specific rendering setup
|
|
println!("Platform render for viewport: {:?}", viewport.id);
|
|
}
|
|
|
|
fn swap_buffers(&mut self, viewport: &mut Viewport) {
|
|
// Swap buffers for the viewport's window
|
|
println!("Swapping buffers for viewport: {:?}", viewport.id);
|
|
}
|
|
|
|
fn create_vk_surface(
|
|
&mut self,
|
|
_viewport: &mut Viewport,
|
|
_instance: u64,
|
|
_out_surface: &mut u64,
|
|
) -> i32 {
|
|
// For Vulkan backends
|
|
0
|
|
}
|
|
}
|
|
|
|
/// Main rendering function that handles all viewports
|
|
#[cfg(feature = "docking")]
|
|
fn render_all_viewports(ctx: &mut Context, renderer: &mut MyRenderer) {
|
|
// Method 1: Render main viewport using backward-compatible API
|
|
let main_draw_data = ctx.render();
|
|
renderer.render_main(main_draw_data);
|
|
|
|
// Update platform windows (creates/destroys OS windows as needed)
|
|
// Must be called AFTER render()
|
|
ctx.update_platform_windows();
|
|
|
|
// Method 2: Iterate over all viewports
|
|
for viewport in ctx.viewports() {
|
|
// Skip main viewport as we already rendered it
|
|
if viewport.is_main() {
|
|
continue;
|
|
}
|
|
|
|
// Only render if viewport has content
|
|
if let Some(draw_data) = viewport.draw_data() {
|
|
renderer.render_viewport(viewport.id, draw_data);
|
|
} else {
|
|
println!("Viewport {:?} has no content to render", viewport.id);
|
|
}
|
|
}
|
|
|
|
// Let imgui handle platform window presentation
|
|
ctx.render_platform_windows_default();
|
|
}
|
|
|
|
/// Alternative approach: render all viewports uniformly
|
|
#[cfg(feature = "docking")]
|
|
fn render_all_viewports_uniform(ctx: &mut Context, renderer: &mut MyRenderer) {
|
|
// First, we need to call render() to generate draw data
|
|
let _main_draw_data = ctx.render();
|
|
|
|
// Update platform windows (must be called AFTER render())
|
|
ctx.update_platform_windows();
|
|
|
|
// Now iterate over ALL viewports including main
|
|
for viewport in ctx.viewports() {
|
|
if let Some(draw_data) = viewport.draw_data() {
|
|
if viewport.is_main() {
|
|
println!("Rendering main viewport uniformly");
|
|
renderer.render_main(draw_data);
|
|
} else {
|
|
renderer.render_viewport(viewport.id, draw_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.render_platform_windows_default();
|
|
}
|
|
|
|
/// Example showing error handling for viewport rendering
|
|
#[cfg(feature = "docking")]
|
|
fn render_with_error_handling(ctx: &mut Context, renderer: &mut MyRenderer) -> Result<(), String> {
|
|
let main_draw_data = ctx.render();
|
|
renderer.render_main(main_draw_data);
|
|
|
|
// Update platform windows (must be called AFTER render())
|
|
ctx.update_platform_windows();
|
|
|
|
for viewport in ctx.viewports() {
|
|
if viewport.is_main() {
|
|
continue;
|
|
}
|
|
|
|
match viewport.draw_data() {
|
|
Some(draw_data) => {
|
|
renderer.render_viewport(viewport.id, draw_data);
|
|
}
|
|
None => {
|
|
// This is not an error - viewport might just have no visible content
|
|
println!("Viewport {:?} has no draw data (might be hidden)", viewport.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.render_platform_windows_default();
|
|
Ok(())
|
|
}
|
|
|
|
fn main() {
|
|
println!("Multi-viewport rendering example");
|
|
|
|
#[cfg(feature = "docking")]
|
|
{
|
|
// Create imgui context
|
|
let mut ctx = Context::create();
|
|
|
|
// Initialize IO properly for new_frame()
|
|
let io = ctx.io_mut();
|
|
io.display_size = [1280.0, 720.0];
|
|
io.delta_time = 1.0 / 60.0;
|
|
|
|
// Enable viewports before first frame
|
|
ctx.io_mut().config_flags |= ConfigFlags::VIEWPORTS_ENABLE;
|
|
|
|
// Build font atlas
|
|
ctx.fonts().build_rgba32_texture();
|
|
|
|
// Set up platform backend
|
|
let platform_backend = MyPlatformBackend {
|
|
windows: HashMap::new(),
|
|
};
|
|
ctx.set_platform_backend(platform_backend);
|
|
|
|
// Create renderer
|
|
let mut renderer = MyRenderer::new();
|
|
|
|
// Simulate a frame
|
|
println!("\n--- Frame 1: Basic viewport rendering ---");
|
|
ctx.new_frame();
|
|
// ... build UI here ...
|
|
render_all_viewports(&mut ctx, &mut renderer);
|
|
|
|
println!("\n--- Frame 2: Uniform viewport rendering ---");
|
|
ctx.new_frame();
|
|
// ... build UI here ...
|
|
render_all_viewports_uniform(&mut ctx, &mut renderer);
|
|
|
|
println!("\n--- Frame 3: With error handling ---");
|
|
ctx.new_frame();
|
|
// ... build UI here ...
|
|
if let Err(e) = render_with_error_handling(&mut ctx, &mut renderer) {
|
|
eprintln!("Rendering error: {}", e);
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "docking"))]
|
|
{
|
|
println!("This example requires the 'docking' feature to be enabled.");
|
|
println!("Run with: cargo run --example viewport_rendering --features docking");
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
#[cfg(feature = "docking")]
|
|
fn test_viewport_helpers() {
|
|
let mut ctx = Context::create();
|
|
|
|
// Initialize IO properly
|
|
let io = ctx.io_mut();
|
|
io.display_size = [1280.0, 720.0];
|
|
io.delta_time = 1.0 / 60.0;
|
|
ctx.fonts().build_rgba32_texture();
|
|
|
|
// Must be within a frame to access viewport data
|
|
ctx.new_frame();
|
|
|
|
// Test main viewport
|
|
let main_viewport = ctx.main_viewport();
|
|
assert!(main_viewport.is_main());
|
|
|
|
// Test viewport iteration
|
|
let viewport_count = ctx.viewports().count();
|
|
assert!(viewport_count >= 1); // At least main viewport exists
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(feature = "docking")]
|
|
fn test_null_draw_data_safety() {
|
|
let mut ctx = Context::create();
|
|
|
|
// Initialize IO properly
|
|
let io = ctx.io_mut();
|
|
io.display_size = [1280.0, 720.0];
|
|
io.delta_time = 1.0 / 60.0;
|
|
ctx.fonts().build_rgba32_texture();
|
|
|
|
// Must be within a frame to access viewport data
|
|
ctx.new_frame();
|
|
|
|
// Without rendering, viewports might not have draw data
|
|
for viewport in ctx.viewports() {
|
|
// This should not panic even if draw_data is null
|
|
let _draw_data = viewport.draw_data();
|
|
}
|
|
}
|
|
} |