dear_imgui_reflect/
lib.rs

1//! Reflection-based helpers for dear-imgui-rs.
2//!
3//! This crate provides traits, derive macros, and helpers to automatically
4//! generate Dear ImGui widgets for your Rust types, in the spirit of the
5//! C++ ImReflect library.
6//!
7//! At a high level:
8//! - derive [`ImGuiReflect`](trait.ImGuiReflect.html) for your structs/enums;
9//! - call [`input`] or [`ImGuiReflectExt::input_reflect`] each frame to render
10//!   an editor for a value;
11//! - optionally customize container / numeric behavior via
12//!   [`ReflectSettings`] and [`MemberSettings`];
13//! - optionally collect structural change events with
14//!   [`input_with_response`].
15//!
16//! The goal is to let you build "data inspector" style UIs quickly without
17//! hand-writing widgets for every field.
18//!
19//! # Quick start
20//!
21//! Derive [`ImGuiReflect`] for your type and use `input_reflect`:
22//!
23//! ```no_run
24//! use dear_imgui_reflect as reflect;
25//! use reflect::ImGuiReflectExt;
26//!
27//! #[derive(reflect::ImGuiReflect, Default)]
28//! struct Player {
29//!     #[imgui(slider, min = 0.0, max = 100.0)]
30//!     health: f32,
31//!     #[imgui(multiline, lines = 3)]
32//!     notes: String,
33//!     inventory: Vec<String>,
34//! }
35//!
36//! fn draw_ui(ui: &reflect::imgui::Ui, player: &mut Player) {
37//!     // Returns true if any field changed this frame.
38//!     if ui.input_reflect("Player", player) {
39//!         // React to edits (save, mark dirty, etc).
40//!     }
41//! }
42//! ```
43//!
44//! # Supported patterns (what this crate is good at)
45//!
46//! This crate is designed around a few common use cases:
47//!
48//! - **Configuration panels / settings windows**:
49//!   derive [`ImGuiReflect`] for your config structs and call
50//!   [`ImGuiReflectExt::input_reflect`] each frame to build a live editor.
51//! - **Game/engine inspectors**:
52//!   derive [`ImGuiReflect`] for components or resource types, then render
53//!   inspectors for the currently selected entity/resource inside a docked
54//!   window.
55//! - **Collection-heavy UIs**:
56//!   use the built-in support for `Vec<T>`, `[T; N]`, `Option<T>`,
57//!   `HashMap<String, V>` and `BTreeMap<String, V>` to build list views,
58//!   property bags and key/value editors with insertion/removal/reordering.
59//! - **Tooling and data browsers**:
60//!   combine [`input_with_response`] and [`ReflectResponse`] to track when
61//!   items are inserted/removed/reordered, and synchronize those changes
62//!   back into your engine or persistence layer.
63//! - **Math-heavy editors**:
64//!   enable the `glam` or `mint` features to edit vector, quaternion and
65//!   matrix types using familiar ImGui `input_float*` widgets.
66//!
67//! The derive macro understands a subset of field attributes inspired by
68//! ImReflect, such as:
69//! - `#[imgui(skip)]` – do not generate any UI for this field;
70//! - `#[imgui(name = "Custom Label")]` – override the field label;
71//! - numeric helpers like
72//!   `#[imgui(slider, min = 0.0, max = 1.0, format = "%.2f")]`,
73//!   `#[imgui(as_drag, speed = 0.1)]`, `#[imgui(as_input, step = 1)]`;
74//! - text helpers like `#[imgui(multiline, lines = 4, hint = "Search...")]`,
75//!   `#[imgui(read_only)]`, `#[imgui(display_only)]`;
76//! - tuple layout helpers such as
77//!   `#[imgui(tuple_render = "grid", tuple_columns = 3)]`.
78//!
79//! See the documentation on the re-exported [`ImGuiReflect` derive macro]
80//! for the full list of supported attributes and validation rules.
81//!
82//! # Settings and per-field overrides
83//!
84//! The global [`ReflectSettings`] object controls how generic containers
85//! are rendered (for example, whether `Vec<T>` is insertable/reorderable, or
86//! how numeric sliders behave). You can adjust it at startup:
87//!
88//! ```no_run
89//! use dear_imgui_reflect as reflect;
90//!
91//! fn configure_reflect() {
92//!     reflect::with_settings(|s| {
93//!         // Make all Vec<T> non-reorderable by default.
94//!         s.vec_mut().reorderable = false;
95//!
96//!         // Use a 0..1 slider for all f32 values by default.
97//!         let f32_settings = s.numerics_f32().clone().slider_0_to_1(2); // "%.2f"
98//!         *s.numerics_f32_mut() = f32_settings;
99//!     });
100//! }
101//! ```
102//!
103//! For finer control, [`MemberSettings`] lets you override behavior for a
104//! specific field, identified by type and field name:
105//!
106//! ```no_run
107//! # use dear_imgui_reflect as reflect;
108//! #
109//! #[derive(reflect::ImGuiReflect)]
110//! struct Settings {
111//!     weights: Vec<f32>,
112//! }
113//!
114//! fn configure_per_field() {
115//!     reflect::with_settings(|s| {
116//!         // For Settings::weights, allow reordering only.
117//!         s.for_member::<Settings>("weights")
118//!             .vec_reorder_only()
119//!             .numerics_f32_slider_0_to_1(3);
120//!     });
121//! }
122//! ```
123//!
124//! The [`with_settings_scope`] helper lets you temporarily override global
125//! settings for a single panel or widget subtree and automatically restore
126//! the previous configuration afterwards.
127//!
128//! # Collecting structural change events
129//!
130//! The [`input`] and [`ImGuiReflectExt::input_reflect`] helpers return a
131//! simple `bool` indicating whether any field changed. If you also want to
132//! react to container-structure changes (insert/remove/reorder/rename), use
133//! [`input_with_response`] and inspect the resulting [`ReflectResponse`]:
134//!
135//! ```no_run
136//! use dear_imgui_reflect as reflect;
137//!
138//! #[derive(reflect::ImGuiReflect, Default)]
139//! struct AppState {
140//!     tags: Vec<String>,
141//! }
142//!
143//! fn draw_tags(ui: &reflect::imgui::Ui, state: &mut AppState) {
144//!     let mut resp = reflect::ReflectResponse::default();
145//!     let _changed = reflect::input_with_response(ui, "Tags", state, &mut resp);
146//!
147//!     for event in resp.events() {
148//!         match event {
149//!             reflect::ReflectEvent::VecInserted { path, index } => {
150//!                 // path is "tags" when generated via the derive macro.
151//!                 println!("Inserted element at {index} in {:?}", path);
152//!             }
153//!             _ => {}
154//!         }
155//!     }
156//! }
157//! ```
158//!
159//! # Math integrations
160//!
161//! When the `glam` feature is enabled, this crate implements [`ImGuiValue`]
162//! for `glam::Vec2/Vec3/Vec4`, `glam::Quat`, and `glam::Mat4`, using the
163//! corresponding `input_float*` widgets for inspection and editing.
164//!
165//! When the `mint` feature is enabled, `mint::Vector2/Vector3/Vector4<f32>`
166//! are also editable via `input_float*` controls. This is useful when your
167//! engine uses `mint` as a math interop layer.
168//!
169//! # Example: simple inspector-style UI
170//!
171//! The following example shows how you might use `dear-imgui-reflect` to
172//! build a small "inspector" for a list of game entities. Each entity is a
173//! struct with nested fields, and the inspector lets you select an entity
174//! from a list and edit its properties in place:
175//!
176//! ```no_run
177//! use dear_imgui_reflect as reflect;
178//! use reflect::ImGuiReflectExt;
179//!
180//! #[derive(reflect::ImGuiReflect, Default)]
181//! struct Transform {
182//!     #[imgui(tuple_render = "grid", tuple_columns = 3)]
183//!     position: (f32, f32, f32),
184//!     #[imgui(tuple_render = "grid", tuple_columns = 3)]
185//!     rotation_euler: (f32, f32, f32),
186//!     #[imgui(slider, min = 0.1, max = 10.0)]
187//!     uniform_scale: f32,
188//! }
189//!
190//! #[derive(reflect::ImGuiReflect, Default)]
191//! struct Enemy {
192//!     #[imgui(name = "Name")]
193//!     name: String,
194//!     #[imgui(slider, min = 0, max = 100)]
195//!     health: i32,
196//!     #[imgui(slider, min = 0.0, max = 1.0)]
197//!     aggression: f32,
198//!     #[imgui(name = "Waypoints")]
199//!     patrol_points: Vec<(f32, f32)>,
200//!     transform: Transform,
201//! }
202//!
203//! #[derive(Default)]
204//! struct EnemyInspector {
205//!     enemies: Vec<Enemy>,
206//!     selected: usize,
207//! }
208//!
209//! impl EnemyInspector {
210//!     fn ui(&mut self, ui: &reflect::imgui::Ui) {
211//!         ui.window("Enemies").build(|| {
212//!             // Left side: list of enemies with selection.
213//!             ui.child_window("EnemyList")
214//!                 .size([200.0, 0.0])
215//!                 .build(ui, || {
216//!                     for (i, enemy) in self.enemies.iter().enumerate() {
217//!                         let label = format!("{i}: {}", enemy.name);
218//!                         if ui.selectable_config(&label)
219//!                             .selected(self.selected == i)
220//!                             .build()
221//!                         {
222//!                             self.selected = i;
223//!                         }
224//!                     }
225//!                 });
226//!
227//!             ui.same_line();
228//!
229//!             // Right side: reflected editor for the selected enemy.
230//!             ui.child_window("EnemyInspector")
231//!                 .size([0.0, 0.0])
232//!                 .build(ui, || {
233//!                     if let Some(enemy) = self.enemies.get_mut(self.selected) {
234//!                         ui.input_reflect("Enemy", enemy);
235//!                     } else {
236//!                         ui.text("No enemy selected");
237//!                     }
238//!                 });
239//!         });
240//!     }
241//! }
242//! ```
243//!
244//! This pattern generalizes well to component-based engines, material
245//! editors, and other data-driven UIs: derive [`ImGuiReflect`] for the
246//! types you care about, then compose editors using `input_reflect` inside
247//! whatever layout best fits your application.
248
249#![deny(rust_2018_idioms)]
250#![deny(missing_docs)]
251#![allow(clippy::needless_lifetimes)]
252
253use std::rc::Rc;
254use std::sync::Arc;
255
256/// Re-export the dear-imgui-rs crate for convenience.
257///
258/// Users can write `use dear_imgui_reflect::imgui::*;` instead of depending
259/// on `dear-imgui-rs` directly if they only need basic types.
260pub use dear_imgui_rs as imgui;
261
262mod containers;
263mod response;
264mod settings;
265mod values;
266
267pub use containers::{
268    imgui_array_with_settings, imgui_btree_map_with_settings, imgui_hash_map_with_settings,
269    imgui_vec_with_settings,
270};
271pub use response::{ReflectEvent, ReflectResponse, with_field_path, with_field_path_static};
272pub use settings::{
273    ArraySettings, BoolSettings, BoolStyle, MapSettings, MemberSettings, NumericDefaultRange,
274    NumericRange, NumericTypeSettings, NumericWidgetKind, ReflectSettings, TupleRenderMode,
275    TupleSettings, VecSettings, current_settings, with_settings, with_settings_scope,
276};
277pub use values::imgui_tuple_body;
278
279/// Trait for values that can render themselves as a single ImGui input widget.
280///
281/// This is implemented for common primitive types and can be implemented
282/// manually for your own types. Most users will interact with
283/// [`ImGuiReflect`](trait.ImGuiReflect.html) instead of this trait directly.
284pub trait ImGuiValue {
285    /// Draw a widget for this value.
286    ///
287    /// Returns `true` if the value was modified.
288    fn imgui_value(ui: &imgui::Ui, label: &str, value: &mut Self) -> bool;
289}
290
291/// Trait for complex types (structs/enums) that can generate ImGui controls
292/// for all of their fields.
293///
294/// You can derive this trait with `#[derive(ImGuiReflect)]` from this crate.
295pub trait ImGuiReflect {
296    /// Draw an ImGui editor for this value with the given label.
297    ///
298    /// Returns `true` if any field was modified.
299    fn imgui_reflect(&mut self, ui: &imgui::Ui, label: &str) -> bool;
300}
301
302/// Blanket implementation: any type that implements [`ImGuiReflect`] can also
303/// be used wherever an [`ImGuiValue`] is expected.
304impl<T: ImGuiReflect> ImGuiValue for T {
305    fn imgui_value(ui: &imgui::Ui, label: &str, value: &mut Self) -> bool {
306        value.imgui_reflect(ui, label)
307    }
308}
309
310/// Transparent reflection for boxed values.
311///
312/// This allows `Box<T>` where `T: ImGuiReflect` to be edited like `T` itself,
313/// matching ImReflect's behavior for smart pointers that simply forward to
314/// the pointed-to value when engaged.
315impl<T: ImGuiReflect> ImGuiReflect for Box<T> {
316    fn imgui_reflect(&mut self, ui: &imgui::Ui, label: &str) -> bool {
317        self.as_mut().imgui_reflect(ui, label)
318    }
319}
320
321/// Transparent reflection for reference-counted values (`Rc<T>`).
322///
323/// When there is exactly one strong reference, this forwards editing to the
324/// inner `T`. Otherwise, it renders a read-only marker indicating that the
325/// value is shared and cannot be safely mutated.
326impl<T: ImGuiReflect> ImGuiReflect for Rc<T> {
327    fn imgui_reflect(&mut self, ui: &imgui::Ui, label: &str) -> bool {
328        if let Some(inner) = Rc::get_mut(self) {
329            inner.imgui_reflect(ui, label)
330        } else {
331            ui.text(label);
332            ui.same_line();
333            ui.text("<Rc shared (read-only)>");
334            false
335        }
336    }
337}
338
339/// Transparent reflection for atomically reference-counted values (`Arc<T>`).
340///
341/// When there is exactly one strong reference, this forwards editing to the
342/// inner `T`. Otherwise, it renders a read-only marker indicating that the
343/// value is shared and cannot be safely mutated.
344impl<T: ImGuiReflect> ImGuiReflect for Arc<T> {
345    fn imgui_reflect(&mut self, ui: &imgui::Ui, label: &str) -> bool {
346        if let Some(inner) = Arc::get_mut(self) {
347            inner.imgui_reflect(ui, label)
348        } else {
349            ui.text(label);
350            ui.same_line();
351            ui.text("<Arc shared (read-only)>");
352            false
353        }
354    }
355}
356
357/// Render ImGui controls for a value that implements [`ImGuiReflect`].
358///
359/// This is the main entry point mirroring the C++ `ImReflect::Input` API.
360pub fn input<T: ImGuiReflect>(ui: &imgui::Ui, label: &str, value: &mut T) -> bool {
361    value.imgui_reflect(ui, label)
362}
363
364/// Variant of [`input`] that additionally collects container-level change events
365/// into the provided [`ReflectResponse`].
366///
367/// The returned boolean is identical to [`input`]: `true` if any field was
368/// modified. Container editors emit structural change events into `response`
369/// while this function is executing. User-defined `ImGuiValue`/`ImGuiReflect`
370/// implementations that only rely on the existing APIs will continue to work,
371/// but will not automatically populate `response` unless they call into
372/// `dear-imgui-reflect`'s container helpers.
373pub fn input_with_response<T: ImGuiReflect>(
374    ui: &imgui::Ui,
375    label: &str,
376    value: &mut T,
377    response: &mut ReflectResponse,
378) -> bool {
379    response::with_response(response, || input(ui, label, value))
380}
381
382/// Extension methods on `Ui` for reflection-based widgets.
383pub trait ImGuiReflectExt {
384    /// Render a reflected editor for a value.
385    ///
386    /// Returns `true` if any field changed.
387    fn input_reflect<T: ImGuiReflect>(&self, label: &str, value: &mut T) -> bool;
388}
389
390impl ImGuiReflectExt for imgui::Ui {
391    fn input_reflect<T: ImGuiReflect>(&self, label: &str, value: &mut T) -> bool {
392        input(self, label, value)
393    }
394}
395
396/// Derive macro for [`ImGuiReflect`], re-exported for convenience.
397///
398/// The macro understands a set of `#[imgui(...)]` field attributes that
399/// configure per-field behavior (labels, sliders vs drags, multiline text,
400/// tuple layout, etc). See the macro documentation on docs.rs for the full
401/// list and examples.
402#[cfg(feature = "derive")]
403pub use dear_imgui_reflect_derive::ImGuiReflect;