From e5d67bf7e36671d290b19064f58bc11616c05b8b Mon Sep 17 00:00:00 2001 From: HampusM Date: Fri, 5 Jun 2026 21:50:56 +0200 Subject: feat(engine): add imgui ui support --- engine/src/ui/imgui.rs | 853 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 853 insertions(+) create mode 100644 engine/src/ui/imgui.rs (limited to 'engine/src/ui/imgui.rs') diff --git a/engine/src/ui/imgui.rs b/engine/src/ui/imgui.rs new file mode 100644 index 0000000..ad59555 --- /dev/null +++ b/engine/src/ui/imgui.rs @@ -0,0 +1,853 @@ +use std::path::{Path, PathBuf}; +use std::sync::LazyLock; + +use dear_imgui_rs::texture::TextureFormat as ImguiTextureFormat; +use dear_imgui_rs::{ + DrawCmd as ImguiDrawCmd, + Key as ImguiKey, + MouseButton as ImguiMouseButton, + TextureId as ImguiTextureId, +}; +use ecs::component::local::Local; +use ecs::component::Component; +use ecs::event::component::{Changed, EventMatchExt}; +use ecs::pair::Pair; +use ecs::query::term::With; +use ecs::sole::Single; +use ecs::system::initializable::Initializable; +use ecs::system::observer::Observe; +use ecs::system::Into; +use ecs::{Component, Query, Sole}; + +use crate::asset::{Assets, Handle as AssetHandle, Label as AssetLabel}; +use crate::data_types::dimens::Dimens; +use crate::delta_time::DeltaTime; +use crate::image::{ + ColorType as ImageColorType, + FromBytesError as ImageFromBytesError, + Image, +}; +use crate::input::keyboard::{Key, Keyboard}; +use crate::input::mouse::{Button as MouseButton, Buttons as MouseButtons, Mouse}; +use crate::mesh::vertex_buffer::{ + NamedVertexAttr as MeshNamedVertexAttr, + VertexAttrInfo as MeshVertexAttrInfo, + VertexBuffer as MeshVertexBuffer, +}; +use crate::mesh::{ + Mesh, + VertexAttrType as MeshVertexAttrType, + POSITION_VERTEX_ATTRIB_NAME, +}; +use crate::projection::{ + ClipVolume as ProjectionClipVolume, + Orthographic as OrthographicProjection, +}; +use crate::rendering::blending::{ + Config as RenderingBlendingConfiguration, + Equation as RenderingBlendingEquation, + Factor as RenderingBlendingFactor, +}; +use crate::rendering::object::{Id as RenderingObjectId, Store as RenderingObjectStore}; +use crate::rendering::{ + Command as RenderingCommand, + DrawMeshOptions, + DrawProperties, + MeshUsage, + RenderPass, + RenderPasses, + SurfaceId, + SurfaceSpec, + PRE_RENDER_PHASE, +}; +use crate::shader::cursor::{BindingValue as ShaderBindingValue, Cursor as ShaderCursor}; +use crate::shader::{ + Context as ShaderContext, + EntrypointFlags as ShaderEntrypointFlags, + ModuleSource as ShaderModuleSource, +}; +use crate::texture::{ + Filtering as TextureFiltering, + Properties as TextureProperties, + Texture, + Wrapping as TextureWrapping, +}; +use crate::vector::Vec2; +use crate::windowing::window::Window; + +mod reexports +{ + pub use dear_imgui_rs as dear_imgui; +} + +pub use reexports::*; + +pub static SHADER_ASSET_LABEL: LazyLock = LazyLock::new(|| AssetLabel { + path: Path::new("").into(), + name: Some("imgui_shader".into()), +}); + +/// Imgui context +#[derive(Sole)] +pub struct Context +{ + pub enabled: bool, + ctx: inner_context_wrapper::InnerContextWrapper, +} + +impl Context +{ + pub fn frame(&mut self) -> Option<&mut dear_imgui_rs::Ui> + { + if !self.enabled { + return None; + } + + self.ctx.get_frame() + } +} + +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct Extension +{ + start_enabled: bool, + settings_ini_file_path: Option, +} + +impl Extension +{ + pub fn with_start_enabled(mut self, start_enabled: bool) -> Self + { + self.start_enabled = start_enabled; + self + } + + pub fn with_settings_ini_file( + mut self, + settings_ini_file_path: Option, + ) -> Self + { + self.settings_ini_file_path = settings_ini_file_path; + self + } +} + +impl ecs::extension::Extension for Extension +{ + fn collect(self, mut collector: ecs::extension::Collector<'_>) + { + let mut context = Context { + enabled: self.start_enabled, + ctx: inner_context_wrapper::InnerContextWrapper::new(), + }; + + if let Err(err) = context + .ctx + .set_settings_ini_file_path(self.settings_ini_file_path) + { + tracing::error!("Failed to set path of imgui settings ini file: {err}"); + } + + collector.add_sole(context).ok(); + + collector.add_system( + *PRE_RENDER_PHASE, + update.into_system().initialize((State::default(),)), + ); + + collector.add_observer(handle_window_changed); + } +} + +#[tracing::instrument(skip_all)] +fn handle_window_changed( + observe: Observe>, + mut context: Single, +) +{ + let Some(event_match) = observe + .iter() + .find(|event_match| event_match.get_entity().has_component(TargetWindow::id())) + else { + return; + }; + + let window = event_match.get_ent_target_comp(); + + let hidpi_factor = window.scale_factor().round(); + + context + .ctx + .get_io_mut() + .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); + + let window_size = window.inner_size(); + + let window_size = Dimens { + width: window_size.width as f32, + height: window_size.height as f32, + }; + + let window_logical_size = window_size / (hidpi_factor as f32); + + context + .ctx + .get_io_mut() + .set_display_size([window_logical_size.width, window_logical_size.height]); +} + +fn update( + target_window_query: Query<(&Window, &SurfaceSpec), (With,)>, + mut context: Single, + delta_time: Single, + mut assets: Single, + mut render_passes: Single, + renderer_object_store: Single, + shader_context: Single, + keyboard: Single, + mouse: Single, + mouse_buttons: Single, + mut state: Local, +) +{ + let Some((window, window_surface_spec)) = target_window_query.iter().next() else { + return; + }; + + let curr_state = std::mem::replace(&mut *state, State::Unreachable); + + match curr_state { + State::NotInitialized => { + let Some((font_texture_asset, shader_asset, mesh_obj_id, mesh)) = + initialize_context( + &mut context, + &window, + window_surface_spec.id, + &mut assets, + &mut render_passes, + &shader_context, + ) + else { + *state = State::NotInitialized; + return; + }; + + *state = State::Initializing { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + } + State::Initializing { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + } => { + let Some(font_texture_obj) = renderer_object_store + .get_obj(&RenderingObjectId::Asset(font_texture_asset.id())) + else { + *state = State::Initializing { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + return; + }; + + context + .ctx + .get_font_atlas_mut() + .set_texture_id(ImguiTextureId::new(font_texture_obj.as_raw().into())); + + context + .ctx + .get_io_mut() + .set_delta_time(delta_time.duration.as_secs() as f32); + + context.ctx.new_frame(); + + *state = State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + } + State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mut mesh, + } => { + if !context.enabled { + *state = State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + return; + } + + context + .ctx + .get_io_mut() + .set_delta_time(delta_time.duration.as_secs_f32()); + + update_inputs(&mut context, &window, &keyboard, &mouse, &mouse_buttons); + + add_drawing_render_pass( + &mut context, + &mut render_passes, + &shader_context, + window_surface_spec.id, + font_texture_asset.clone(), + shader_asset.clone(), + mesh_obj_id, + &mut mesh, + ); + + context.ctx.new_frame(); + + *state = State::Ready { + font_texture_asset, + shader_asset, + mesh_obj_id, + mesh, + }; + } + State::Unreachable => unreachable!(), + } +} + +#[derive(Debug, Component)] +pub struct TargetWindow; + +#[derive(Debug, Default, Component)] +enum State +{ + #[default] + NotInitialized, + Initializing + { + font_texture_asset: AssetHandle, + shader_asset: AssetHandle, + mesh_obj_id: RenderingObjectId, + mesh: Mesh, + }, + Ready + { + font_texture_asset: AssetHandle, + shader_asset: AssetHandle, + mesh_obj_id: RenderingObjectId, + mesh: Mesh, + }, + Unreachable, +} + +fn initialize_context( + context: &mut Context, + window: &Window, + window_surface_id: SurfaceId, + assets: &mut Assets, + render_passes: &mut RenderPasses, + shader_context: &ShaderContext, +) -> Option<( + AssetHandle, + AssetHandle, + RenderingObjectId, + Mesh, +)> +{ + let shader_asset = if let Some(shader_asset) = + assets.get_handle_to_loaded::(SHADER_ASSET_LABEL.clone()) + { + shader_asset + } else { + assets.store_with_label( + SHADER_ASSET_LABEL.clone(), + ShaderModuleSource { + name: "imgui_shader.slang".into(), + file_path: Path::new("@engine/imgui_shader").into(), + source: include_str!("../../res/imgui_shader.slang").into(), + link_entrypoints: ShaderEntrypointFlags::VERTEX + | ShaderEntrypointFlags::FRAGMENT, + }, + ); + + // We want to wait with initializing until the shader have been processed by the + // shader context + return None; + }; + + let hidpi_factor = window.scale_factor().round(); + + context + .ctx + .get_io_mut() + .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); + + let window_size = window.inner_size(); + + let window_size = Dimens { + width: window_size.width as f32, + height: window_size.height as f32, + }; + + let window_logical_size = window_size / (hidpi_factor as f32); + + context + .ctx + .get_io_mut() + .set_display_size([window_logical_size.width, window_logical_size.height]); + + let mut fonts = context.ctx.get_font_atlas_mut(); + + if !fonts.is_built() { + fonts.build(); + } + + let Some(font_texture_data) = fonts.tex_data_mut() else { + tracing::error!("No texture data exists for font"); + return None; + }; + + let font_texture = match create_font_texture(font_texture_data) { + Ok(font_texture) => font_texture, + Err(err) => { + tracing::error!("Failed to create font texture: {err}"); + return None; + } + }; + + let font_texture_asset = assets.store_with_name("imgui_font_texture", font_texture); + + let mesh_obj_id = RenderingObjectId::new_sequential(); + + let mesh = Mesh::builder() + .vertices(MeshVertexBuffer::with_capacity( + &[ + MeshVertexAttrInfo { + name: POSITION_VERTEX_ATTRIB_NAME.into(), + ty: MeshVertexAttrType::Float32Array { length: 3 }, + }, + MeshVertexAttrInfo { + name: "color".into(), + ty: MeshVertexAttrType::Float32Array { length: 4 }, + }, + MeshVertexAttrInfo { + name: "texture_coords".into(), + ty: MeshVertexAttrType::Float32Array { length: 2 }, + }, + ], + 128, + )) + .indices([]) + .build(); + + render_passes.passes.push_back(RenderPass { + commands: vec![ + RenderingCommand::MakeCurrent(window_surface_id), + RenderingCommand::CreateTexture(font_texture_asset.clone()), + RenderingCommand::CreateShaderProgram( + RenderingObjectId::Asset(shader_asset.id()), + shader_context + .get_program(&shader_asset.id()) + .expect("Not possible") + .clone(), + ), + RenderingCommand::ActivateShader(RenderingObjectId::Asset(shader_asset.id())), + RenderingCommand::CreateMesh { + obj_id: mesh_obj_id, + mesh: Some(mesh.clone()), + usage: MeshUsage::Stream, + }, + ], + draw_properties: DrawProperties::default(), + }); + + Some((font_texture_asset, shader_asset, mesh_obj_id, mesh)) +} + +fn update_inputs( + context: &mut Context, + window: &Window, + keyboard: &Keyboard, + mouse: &Mouse, + mouse_buttons: &MouseButtons, +) +{ + for (key, key_state) in keyboard.new_key_states() { + if let Some(key) = key_to_imgui_key(key) { + context + .ctx + .get_io_mut() + .add_key_event(key, key_state.is_pressed()); + } + } + + for character in keyboard.text_keys().chars() { + if character == '\u{7f}' { + continue; + } + + context.ctx.get_io_mut().add_input_character(character); + } + + let mouse_pos = mouse.position / window.scale_factor(); + + context + .ctx + .get_io_mut() + .add_mouse_pos_event([mouse_pos.x as f32, mouse_pos.y as f32]); + + for (mouse_button, mouse_button_state) in mouse_buttons.all_current() { + if let Some(mouse_button) = mouse_button_to_imgui_mouse_button(mouse_button) { + context + .ctx + .get_io_mut() + .add_mouse_button_event(mouse_button, mouse_button_state.is_pressed()); + } + } +} + +fn add_drawing_render_pass( + context: &mut Context, + render_passes: &mut RenderPasses, + shader_context: &ShaderContext, + window_surface_id: SurfaceId, + font_texture_asset: AssetHandle, + shader_asset: AssetHandle, + mesh_obj_id: RenderingObjectId, + mesh: &mut Mesh, +) +{ + let render_pass = render_passes.passes.push_back_mut(RenderPass { + commands: Vec::new(), + draw_properties: DrawProperties { + blending_enabled: true, + blending_config: RenderingBlendingConfiguration { + equation: RenderingBlendingEquation::Add, + source_factor: RenderingBlendingFactor::SrcAlpha, + destination_factor: RenderingBlendingFactor::OneMinusSrcAlpha, + }, + depth_test_enabled: false, + face_culling_enabled: false, + ..Default::default() + }, + }); + + let shader_program = shader_context + .get_program(&shader_asset.id()) + .expect("Not possible"); + + let shader_cursor = ShaderCursor::new( + shader_program + .reflection(0) + .unwrap() + .global_params_var_layout() + .unwrap(), + ); + + let draw_data = context.ctx.render(); + + let [display_width, display_height] = draw_data.display_size; + + // let [scale_width, scale_height] = draw_data.framebuffer_scale; + + render_pass.commands.extend([ + RenderingCommand::MakeCurrent(window_surface_id), + RenderingCommand::ActivateShader(RenderingObjectId::Asset(shader_asset.id())), + RenderingCommand::SetShaderBinding( + shader_cursor + .field("Uniforms") + .field("projection") + .into_binding_location(), + ShaderBindingValue::FMat4x4( + OrthographicProjection::builder() + .near(1.0) + .far(-1.0) + .viewport_origin(Vec2 { x: 0.0, y: 1.0 }) + .size(crate::projection::OrthographicSize::WindowSize) + .build() + .to_matrix_rh( + Dimens { + width: display_width, + height: -display_height, + }, + ProjectionClipVolume::NegOneToOne, + ), + ), + ), + RenderingCommand::SetShaderBinding( + shader_cursor.field("main_texture").into_binding_location(), + ShaderBindingValue::Texture(font_texture_asset), + ), + ]); + + for draw_list in draw_data.draw_lists() { + mesh.vertex_buf_mut().clear(); + + for vertex in draw_list.vtx_buffer() { + mesh.vertex_buf_mut().push(( + MeshNamedVertexAttr::<[f32; 3]> { + name: POSITION_VERTEX_ATTRIB_NAME, + value: [vertex.pos[0], vertex.pos[1], 0.0], + }, + MeshNamedVertexAttr { + name: "color", + value: vertex.rgba().map(|elem| (elem as f32) / 255.0), + }, + MeshNamedVertexAttr { + name: "texture_coords", + value: vertex.uv, + }, + )); + } + + mesh.set_indices(draw_list.idx_buffer().iter().map(|index| (*index).into())); + + render_pass.commands.push(RenderingCommand::UpdateMesh { + obj_id: mesh_obj_id, + mesh: mesh.clone(), + usage: MeshUsage::Stream, + }); + + for command in draw_list.commands() { + match command { + ImguiDrawCmd::Elements { count, cmd_params, raw_cmd: _ } => { + render_pass.commands.push(RenderingCommand::DrawMesh( + mesh_obj_id, + DrawMeshOptions::builder() + .element_offset(cmd_params.idx_offset.try_into().unwrap()) + .vertex_offset(cmd_params.vtx_offset.try_into().unwrap()) + .element_cnt(count.try_into().unwrap()) + .build(), + )); + } + _ => {} + } + } + } +} + +fn create_font_texture( + font_texture_data: &dear_imgui_rs::texture::TextureData, +) -> Result +{ + Ok(Texture { + image: Image::try_from_bytes( + font_texture_data + .pixels() + .expect("Font texture data does not contain any pixels"), + Dimens { + width: font_texture_data.width().try_into().expect( + "Font texture width value does not fit in 32-bit unsigned int", + ), + height: font_texture_data.height().try_into().expect( + "Font texture height value does not fit in 32-bit unsigned int", + ), + }, + match font_texture_data.format() { + ImguiTextureFormat::RGBA32 => ImageColorType::Rgba8, + ImguiTextureFormat::Alpha8 => unimplemented!(), + }, + )?, + properties: TextureProperties::builder() + .minifying_filter(TextureFiltering::Linear) + .magnifying_filter(TextureFiltering::Linear) + .wrap(TextureWrapping::ClampToBorder) + .build(), + }) +} + +fn key_to_imgui_key(key: Key) -> Option +{ + match key { + Key::Backquote => Some(ImguiKey::GraveAccent), + Key::Backslash => Some(ImguiKey::Backslash), + Key::BracketLeft => Some(ImguiKey::LeftBracket), + Key::BracketRight => Some(ImguiKey::RightBracket), + Key::Comma => Some(ImguiKey::Comma), + Key::Digit0 => Some(ImguiKey::Key0), + Key::Digit1 => Some(ImguiKey::Key1), + Key::Digit2 => Some(ImguiKey::Key2), + Key::Digit3 => Some(ImguiKey::Key3), + Key::Digit4 => Some(ImguiKey::Key4), + Key::Digit5 => Some(ImguiKey::Key5), + Key::Digit6 => Some(ImguiKey::Key6), + Key::Digit7 => Some(ImguiKey::Key7), + Key::Digit8 => Some(ImguiKey::Key8), + Key::Digit9 => Some(ImguiKey::Key9), + Key::Equal => Some(ImguiKey::Equal), + Key::A => Some(ImguiKey::A), + Key::B => Some(ImguiKey::B), + Key::C => Some(ImguiKey::C), + Key::D => Some(ImguiKey::D), + Key::E => Some(ImguiKey::E), + Key::F => Some(ImguiKey::F), + Key::G => Some(ImguiKey::G), + Key::H => Some(ImguiKey::H), + Key::I => Some(ImguiKey::I), + Key::J => Some(ImguiKey::J), + Key::K => Some(ImguiKey::K), + Key::L => Some(ImguiKey::L), + Key::M => Some(ImguiKey::M), + Key::N => Some(ImguiKey::N), + Key::O => Some(ImguiKey::O), + Key::P => Some(ImguiKey::P), + Key::Q => Some(ImguiKey::Q), + Key::R => Some(ImguiKey::R), + Key::S => Some(ImguiKey::S), + Key::T => Some(ImguiKey::T), + Key::U => Some(ImguiKey::U), + Key::V => Some(ImguiKey::V), + Key::W => Some(ImguiKey::W), + Key::X => Some(ImguiKey::X), + Key::Y => Some(ImguiKey::Y), + Key::Z => Some(ImguiKey::Z), + Key::Minus => Some(ImguiKey::Minus), + Key::Period => Some(ImguiKey::Period), + Key::Quote => Some(ImguiKey::Apostrophe), + Key::Semicolon => Some(ImguiKey::Semicolon), + Key::Slash => Some(ImguiKey::Slash), + Key::AltLeft => Some(ImguiKey::LeftAlt), + Key::AltRight => Some(ImguiKey::RightAlt), + Key::Backspace => Some(ImguiKey::Backspace), + Key::CapsLock => Some(ImguiKey::CapsLock), + Key::ControlLeft => Some(ImguiKey::LeftCtrl), + Key::ControlRight => Some(ImguiKey::RightCtrl), + Key::Enter => Some(ImguiKey::Enter), + Key::SuperLeft => Some(ImguiKey::LeftSuper), + Key::SuperRight => Some(ImguiKey::RightSuper), + Key::ShiftLeft => Some(ImguiKey::LeftShift), + Key::ShiftRight => Some(ImguiKey::RightShift), + Key::Space => Some(ImguiKey::Space), + Key::Tab => Some(ImguiKey::Tab), + Key::Delete => Some(ImguiKey::Delete), + Key::End => Some(ImguiKey::End), + Key::Home => Some(ImguiKey::Home), + Key::Insert => Some(ImguiKey::Insert), + Key::PageDown => Some(ImguiKey::PageDown), + Key::PageUp => Some(ImguiKey::PageUp), + Key::ArrowDown => Some(ImguiKey::DownArrow), + Key::ArrowLeft => Some(ImguiKey::LeftArrow), + Key::ArrowRight => Some(ImguiKey::RightArrow), + Key::ArrowUp => Some(ImguiKey::UpArrow), + Key::NumLock => Some(ImguiKey::NumLock), + Key::Numpad0 => Some(ImguiKey::Keypad0), + Key::Numpad1 => Some(ImguiKey::Keypad1), + Key::Numpad2 => Some(ImguiKey::Keypad2), + Key::Numpad3 => Some(ImguiKey::Keypad3), + Key::Numpad4 => Some(ImguiKey::Keypad4), + Key::Numpad5 => Some(ImguiKey::Keypad5), + Key::Numpad6 => Some(ImguiKey::Keypad6), + Key::Numpad7 => Some(ImguiKey::Keypad7), + Key::Numpad8 => Some(ImguiKey::Keypad8), + Key::Numpad9 => Some(ImguiKey::Keypad9), + Key::NumpadAdd => Some(ImguiKey::KeypadAdd), + Key::NumpadDecimal => Some(ImguiKey::KeypadDecimal), + Key::NumpadDivide => Some(ImguiKey::KeypadDivide), + Key::NumpadEnter => Some(ImguiKey::KeypadEnter), + Key::NumpadEqual => Some(ImguiKey::KeypadEqual), + Key::NumpadMultiply => Some(ImguiKey::KeypadMultiply), + Key::NumpadSubtract => Some(ImguiKey::KeypadSubtract), + Key::Escape => Some(ImguiKey::Escape), + Key::PrintScreen => Some(ImguiKey::PrintScreen), + Key::ScrollLock => Some(ImguiKey::ScrollLock), + Key::Pause => Some(ImguiKey::Pause), + Key::Meta => Some(ImguiKey::ModSuper), + Key::F1 => Some(ImguiKey::F1), + Key::F2 => Some(ImguiKey::F2), + Key::F3 => Some(ImguiKey::F3), + Key::F4 => Some(ImguiKey::F4), + Key::F5 => Some(ImguiKey::F5), + Key::F6 => Some(ImguiKey::F6), + Key::F7 => Some(ImguiKey::F7), + Key::F8 => Some(ImguiKey::F8), + Key::F9 => Some(ImguiKey::F9), + Key::F10 => Some(ImguiKey::F10), + Key::F11 => Some(ImguiKey::F11), + Key::F12 => Some(ImguiKey::F12), + _ => None, + } +} + +fn mouse_button_to_imgui_mouse_button( + mouse_button: MouseButton, +) -> Option +{ + match mouse_button { + MouseButton::Left | MouseButton::Other(0) => Some(ImguiMouseButton::Left), + MouseButton::Right | MouseButton::Other(1) => Some(ImguiMouseButton::Right), + MouseButton::Middle | MouseButton::Other(2) => Some(ImguiMouseButton::Middle), + MouseButton::Other(3) => Some(ImguiMouseButton::Extra1), + MouseButton::Other(4) => Some(ImguiMouseButton::Extra2), + _ => None, + } +} + +mod inner_context_wrapper +{ + use std::path::PathBuf; + use std::pin::Pin; + use std::ptr::null_mut; + + pub struct InnerContextWrapper + { + ctx: Pin>, + frame: *mut dear_imgui_rs::Ui, + } + + impl InnerContextWrapper + { + pub fn new() -> Self + { + let mut ctx = Box::pin(dear_imgui_rs::Context::create()); + + let _ = ctx.set_platform_name(Some("engine-windowing")); + let _ = ctx.set_renderer_name(Some("engine-rendering")); + + Self { ctx, frame: null_mut() } + } + + pub fn set_settings_ini_file_path( + &mut self, + path: Option, + ) -> Result<(), dear_imgui_rs::ImGuiError> + { + self.ctx.set_ini_filename(path) + } + + pub fn get_io_mut(&mut self) -> &mut dear_imgui_rs::Io + { + self.ctx.io_mut() + } + + pub fn get_font_atlas_mut(&mut self) -> dear_imgui_rs::fonts::FontAtlas + { + self.ctx.font_atlas_mut() + } + + pub fn render(&mut self) -> &dear_imgui_rs::DrawData + { + self.ctx.render() + } + + pub fn new_frame(&mut self) + { + let frame = &raw mut *self.ctx.frame(); + + self.frame = frame; + } + + pub fn get_frame(&mut self) -> Option<&mut dear_imgui_rs::Ui> + { + unsafe { self.frame.as_mut() } + } + } +} -- cgit v1.2.3-18-g5258