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;