nexus\api/
gui.rs

1//! [ImGui](https://github.com/ocornut/imgui) rendering via [`imgui-rs`](crate::imgui).
2
3use crate::{util::str_to_c, AddonApi, RendererApi, Revertible, UiApi};
4use std::ffi::{c_char, c_void};
5
6/// ImGui version.
7// TODO: is this still correct?
8pub const IMGUI_VERSION: u32 = 18000;
9
10/// Type of render callback.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(
14    feature = "strum",
15    derive(
16        strum::AsRefStr,
17        strum::Display,
18        strum::EnumCount,
19        strum::EnumIter,
20        strum::IntoStaticStr,
21        strum::VariantArray,
22        strum::VariantNames
23    )
24)]
25#[repr(C)]
26pub enum RenderType {
27    /// Before ImGui frame is initialized.
28    PreRender,
29
30    /// During ImGui frame.
31    Render,
32
33    /// After ImGui frame was ended.
34    PostRender,
35
36    /// During ImGui frame, appended to options window.
37    OptionsRender,
38}
39
40pub type RawGuiRender = extern "C-unwind" fn();
41
42pub type RawGuiAddRender =
43    unsafe extern "C-unwind" fn(render_type: RenderType, render_callback: RawGuiRender);
44
45pub type RawGuiRemRender = unsafe extern "C-unwind" fn(render_callback: RawGuiRender);
46
47pub type RawGuiRegisterCloseOnEscape =
48    unsafe extern "C-unwind" fn(window_name: *const c_char, is_visible: *mut bool);
49
50pub type RawGuiDeregisterCloseOnEscape = unsafe extern "C-unwind" fn(window_name: *const c_char);
51
52pub type ImguiMalloc = unsafe extern "C" fn(size: usize, user_data: *mut c_void) -> *mut c_void;
53
54pub type ImguiFree = unsafe extern "C" fn(ptr: *mut c_void, user_data: *mut c_void);
55
56/// Registers a new ImGui render callback of the given [`RenderType`].
57///
58/// Returns a [`Revertible`] to revert the register.
59///
60/// # Usage
61/// ```no_run
62/// # use nexus::gui::*;
63/// let render_callback = render!(|ui| ui.text("Hello World"));
64/// register_render(RenderType::Render, render_callback).revert_on_unload();
65/// ```
66#[inline]
67pub fn register_render(
68    render_type: RenderType,
69    callback: RawGuiRender,
70) -> Revertible<impl Fn() + Send + Sync + Clone + 'static> {
71    let RendererApi {
72        register,
73        deregister,
74        ..
75    } = AddonApi::get().renderer;
76    unsafe { register(render_type, callback) };
77    let revert = move || unsafe { deregister(callback) };
78    revert.into()
79}
80
81/// Unregisters a previously registered ImGui render callback.
82#[inline]
83pub fn unregister_render(callback: RawGuiRender) {
84    let RendererApi { deregister, .. } = AddonApi::get().renderer;
85    unsafe { deregister(callback) }
86}
87
88/// Macro to wrap an ImGui render callback.
89///
90/// Generates a [`RawGuiRender`] wrapper around the passed callback.
91///
92/// # Usage
93/// ```no_run
94/// # use nexus::gui::*;
95/// let render_callback: RawGuiRender = render!(|ui| ui.text("Hello World"));
96/// ```
97#[macro_export]
98macro_rules! render {
99    ( $callback:expr $(,)? ) => {{
100        const __CALLBACK: fn(&$crate::imgui::Ui) = $callback;
101
102        extern "C-unwind" fn __render_callback_wrapper() {
103            unsafe { $crate::__macro::with_ui(__CALLBACK) }
104        }
105
106        __render_callback_wrapper
107    }};
108}
109
110pub use render;
111
112/// Registers a window name to get its bool toggled when escape is pressed.
113pub fn register_close_on_escape(window_name: impl AsRef<str>, opened: &mut bool) {
114    let UiApi {
115        register_close_on_escape,
116        ..
117    } = AddonApi::get().ui;
118    let window_name = str_to_c(window_name, "failed to convert window name");
119    unsafe { register_close_on_escape(window_name.as_ptr(), opened) }
120}
121
122///  Deregisters a window name to listen to on escape.
123pub fn deregister_close_on_escape(window_name: impl AsRef<str>) {
124    let UiApi {
125        deregister_close_on_escape,
126        ..
127    } = AddonApi::get().ui;
128    let window_name = str_to_c(window_name, "failed to convert window name");
129    unsafe { deregister_close_on_escape(window_name.as_ptr()) }
130}