summaryrefslogtreecommitdiff
path: root/engine/src/ui/imgui.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-06-05 21:50:56 +0200
committerHampusM <hampus@hampusmat.com>2026-06-05 21:50:56 +0200
commite5d67bf7e36671d290b19064f58bc11616c05b8b (patch)
treea529b876e4198bb470113837bfd6dca3fda3409e /engine/src/ui/imgui.rs
parent87015a4abcd45d6c69b289467c9e37426e1f0930 (diff)
feat(engine): add imgui ui supportHEADmaster
Diffstat (limited to 'engine/src/ui/imgui.rs')
-rw-r--r--engine/src/ui/imgui.rs853
1 files changed, 853 insertions, 0 deletions
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<AssetLabel> = 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<PathBuf>,
+}
+
+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<PathBuf>,
+ ) -> 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<Pair<Changed, Window>>,
+ mut context: Single<Context>,
+)
+{
+ 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<TargetWindow>,)>,
+ mut context: Single<Context>,
+ delta_time: Single<DeltaTime>,
+ mut assets: Single<Assets>,
+ mut render_passes: Single<RenderPasses>,
+ renderer_object_store: Single<RenderingObjectStore>,
+ shader_context: Single<ShaderContext>,
+ keyboard: Single<Keyboard>,
+ mouse: Single<Mouse>,
+ mouse_buttons: Single<MouseButtons>,
+ mut state: Local<State>,
+)
+{
+ 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<Texture>,
+ shader_asset: AssetHandle<ShaderModuleSource>,
+ mesh_obj_id: RenderingObjectId,
+ mesh: Mesh,
+ },
+ Ready
+ {
+ font_texture_asset: AssetHandle<Texture>,
+ shader_asset: AssetHandle<ShaderModuleSource>,
+ 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<Texture>,
+ AssetHandle<ShaderModuleSource>,
+ RenderingObjectId,
+ Mesh,
+)>
+{
+ let shader_asset = if let Some(shader_asset) =
+ assets.get_handle_to_loaded::<ShaderModuleSource>(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<Texture>,
+ shader_asset: AssetHandle<ShaderModuleSource>,
+ 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<Texture, ImageFromBytesError>
+{
+ 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<ImguiKey>
+{
+ 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<ImguiMouseButton>
+{
+ 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<Box<dear_imgui_rs::Context>>,
+ 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<PathBuf>,
+ ) -> 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() }
+ }
+ }
+}