dear_imgui_reflect/settings.rs
1//! Global and per-member settings for dear-imgui-reflect.
2//!
3//! This module defines [`ReflectSettings`] and [`MemberSettings`], along with
4//! container and numeric configuration types that mirror many concepts from
5//! ImReflect's `ImSettings` API.
6
7use std::any::TypeId;
8use std::collections::HashMap;
9use std::sync::{Mutex, OnceLock};
10
11/// Trait providing a default numeric range for slider widgets when no explicit
12/// `min`/`max` are given.
13///
14/// This mirrors the behavior of the C++ ImReflect library, which uses a
15/// "half-range" of the underlying numeric limits to avoid Dear ImGui's
16/// internal range restrictions for very large values.
17pub trait NumericDefaultRange {
18 /// Default minimum value for this numeric type.
19 fn default_min() -> Self;
20 /// Default maximum value for this numeric type.
21 fn default_max() -> Self;
22}
23
24macro_rules! impl_default_range_signed {
25 ($($ty:ty),* $(,)?) => {
26 $(
27 impl NumericDefaultRange for $ty {
28 fn default_min() -> Self {
29 // Use half-range to match ImReflect's behavior and avoid
30 // hitting Dear ImGui's internal limits for large ranges.
31 Self::MIN / 2
32 }
33
34 fn default_max() -> Self {
35 Self::MAX / 2
36 }
37 }
38 )*
39 };
40}
41
42macro_rules! impl_default_range_unsigned {
43 ($($ty:ty),* $(,)?) => {
44 $(
45 impl NumericDefaultRange for $ty {
46 fn default_min() -> Self {
47 0
48 }
49
50 fn default_max() -> Self {
51 Self::MAX / 2
52 }
53 }
54 )*
55 };
56}
57
58macro_rules! impl_default_range_float {
59 ($($ty:ty),* $(,)?) => {
60 $(
61 impl NumericDefaultRange for $ty {
62 fn default_min() -> Self {
63 Self::MIN / 2.0
64 }
65
66 fn default_max() -> Self {
67 Self::MAX / 2.0
68 }
69 }
70 )*
71 };
72}
73
74impl_default_range_signed!(i8, i16, i32, i64, isize);
75impl_default_range_unsigned!(u8, u16, u32, u64, usize);
76impl_default_range_float!(f32, f64);
77
78/// Settings that control how certain types are rendered by `dear-imgui-reflect`.
79///
80/// This mirrors some of the concepts from ImReflect's `ImSettings` type, but is
81/// intentionally smaller and focused on common container behaviors.
82#[derive(Clone, Debug, Default)]
83pub struct ReflectSettings {
84 vec: VecSettings,
85 bools: BoolSettings,
86 arrays: ArraySettings,
87 maps: MapSettings,
88 tuples: TupleSettings,
89 numerics_i32: NumericTypeSettings,
90 numerics_f32: NumericTypeSettings,
91 numerics_u32: NumericTypeSettings,
92 numerics_f64: NumericTypeSettings,
93 member_overrides: HashMap<TypeId, HashMap<String, MemberSettings>>,
94}
95
96impl ReflectSettings {
97 /// Creates a new settings object with reasonable defaults.
98 pub fn new() -> Self {
99 Self::default()
100 }
101
102 /// Settings that apply to all `Vec<T>` containers rendered via reflection.
103 pub fn vec(&self) -> &VecSettings {
104 &self.vec
105 }
106
107 /// Mutable access to settings that apply to all `Vec<T>` containers.
108 pub fn vec_mut(&mut self) -> &mut VecSettings {
109 &mut self.vec
110 }
111
112 /// Settings that apply to all `bool` fields rendered via reflection.
113 pub fn bools(&self) -> &BoolSettings {
114 &self.bools
115 }
116
117 /// Mutable access to settings that apply to all `bool` fields.
118 pub fn bools_mut(&mut self) -> &mut BoolSettings {
119 &mut self.bools
120 }
121
122 /// Settings that apply to fixed-size arrays rendered via reflection.
123 pub fn arrays(&self) -> &ArraySettings {
124 &self.arrays
125 }
126
127 /// Mutable access to settings that apply to fixed-size arrays.
128 pub fn arrays_mut(&mut self) -> &mut ArraySettings {
129 &mut self.arrays
130 }
131
132 /// Settings that apply to string-keyed maps rendered via reflection.
133 pub fn maps(&self) -> &MapSettings {
134 &self.maps
135 }
136
137 /// Mutable access to settings that apply to string-keyed maps.
138 pub fn maps_mut(&mut self) -> &mut MapSettings {
139 &mut self.maps
140 }
141
142 /// Settings that apply to tuple-like values rendered via reflection.
143 pub fn tuples(&self) -> &TupleSettings {
144 &self.tuples
145 }
146
147 /// Mutable access to settings that apply to tuple-like values.
148 pub fn tuples_mut(&mut self) -> &mut TupleSettings {
149 &mut self.tuples
150 }
151
152 /// Type-level numeric settings for `i32` values rendered via reflection.
153 pub fn numerics_i32(&self) -> &NumericTypeSettings {
154 &self.numerics_i32
155 }
156
157 /// Mutable access to type-level numeric settings for `i32` values.
158 pub fn numerics_i32_mut(&mut self) -> &mut NumericTypeSettings {
159 &mut self.numerics_i32
160 }
161
162 /// Type-level numeric settings for `f32` values rendered via reflection.
163 pub fn numerics_f32(&self) -> &NumericTypeSettings {
164 &self.numerics_f32
165 }
166
167 /// Mutable access to type-level numeric settings for `f32` values.
168 pub fn numerics_f32_mut(&mut self) -> &mut NumericTypeSettings {
169 &mut self.numerics_f32
170 }
171
172 /// Type-level numeric settings for `u32` values rendered via reflection.
173 pub fn numerics_u32(&self) -> &NumericTypeSettings {
174 &self.numerics_u32
175 }
176
177 /// Mutable access to type-level numeric settings for `u32` values.
178 pub fn numerics_u32_mut(&mut self) -> &mut NumericTypeSettings {
179 &mut self.numerics_u32
180 }
181
182 /// Type-level numeric settings for `f64` values rendered via reflection.
183 pub fn numerics_f64(&self) -> &NumericTypeSettings {
184 &self.numerics_f64
185 }
186
187 /// Mutable access to type-level numeric settings for `f64` values.
188 pub fn numerics_f64_mut(&mut self) -> &mut NumericTypeSettings {
189 &mut self.numerics_f64
190 }
191
192 /// Returns member-level settings for a given type and field name, if any.
193 ///
194 /// This provides an ImSettings-style per-member override analogous to
195 /// `push_member<&T::field>()` in ImReflect.
196 ///
197 /// Member keys follow the paths generated by the derive macro:
198 ///
199 /// - Named-field structs: `"field_name"`
200 /// - Tuple structs: `"0"`, `"1"`, ...
201 /// - Enums with payloads: `"Variant.field_name"` or `"Variant.0"`, `"Variant.1"`, ...
202 /// - Tuple element overrides: `"tuple_field[0]"`, `"tuple_field[1]"`, ...
203 pub fn member<T: 'static>(&self, field: &str) -> Option<&MemberSettings> {
204 self.member_overrides
205 .get(&TypeId::of::<T>())
206 .and_then(|map| map.get(field))
207 }
208
209 /// Returns a mutable handle to member-level settings for a given type and
210 /// field name, creating a default entry if it does not yet exist.
211 ///
212 /// See [`ReflectSettings::member`] for the expected member-key formats.
213 pub fn for_member<T: 'static>(&mut self, field: &str) -> &mut MemberSettings {
214 self.member_overrides
215 .entry(TypeId::of::<T>())
216 .or_default()
217 .entry(field.to_owned())
218 .or_default()
219 }
220}
221
222/// Per-member override settings layered on top of global [`ReflectSettings`].
223///
224/// This provides an ImSettings-style configuration surface for specific
225/// fields (members) of a reflected type, analogous to
226/// `ImSettings::push_member<&T::field>()` in ImReflect.
227#[derive(Clone, Debug, Default)]
228pub struct MemberSettings {
229 /// Whether this member should be rendered in a read-only (disabled) state.
230 pub read_only: bool,
231 /// Optional override for boolean style settings on this member.
232 pub bools: Option<BoolSettings>,
233 /// Optional override for tuple rendering settings on this member.
234 pub tuples: Option<TupleSettings>,
235 /// Optional override for map rendering settings on this member.
236 pub maps: Option<MapSettings>,
237 /// Optional override for vector rendering settings on this member.
238 pub vec: Option<VecSettings>,
239 /// Optional override for fixed-size array rendering settings on this member.
240 pub arrays: Option<ArraySettings>,
241 /// Optional numeric settings override for `i32` members.
242 pub numerics_i32: Option<NumericTypeSettings>,
243 /// Optional numeric settings override for `f32` members.
244 pub numerics_f32: Option<NumericTypeSettings>,
245 /// Optional numeric settings override for `u32` members.
246 pub numerics_u32: Option<NumericTypeSettings>,
247 /// Optional numeric settings override for `f64` members.
248 pub numerics_f64: Option<NumericTypeSettings>,
249}
250
251impl MemberSettings {
252 /// Convenience helper: mark a `Vec<T>` member as "reorder-only" (no
253 /// insertion/removal, but drag-to-reorder remains enabled).
254 pub fn vec_reorder_only(&mut self) -> &mut Self {
255 self.vec = Some(VecSettings::reorder_only());
256 self
257 }
258
259 /// Convenience helper: mark a `Vec<T>` member as "fixed" (no insertion,
260 /// removal, or reordering). The contents are still editable unless
261 /// combined with `read_only`.
262 pub fn vec_fixed(&mut self) -> &mut Self {
263 self.vec = Some(VecSettings::fixed());
264 self
265 }
266
267 /// Convenience helper: mark an array member as fixed-order (no reordering
268 /// of elements, but still rendered inside an optional dropdown).
269 pub fn arrays_fixed_order(&mut self) -> &mut Self {
270 self.arrays = Some(ArraySettings::fixed_order());
271 self
272 }
273
274 /// Convenience helper: mark a map member as "const-map" (no insertion or
275 /// removal, keys/values still editable when not read-only).
276 pub fn maps_const(&mut self) -> &mut Self {
277 self.maps = Some(MapSettings::const_map());
278 self
279 }
280
281 /// Convenience helper: explicitly mark a map member as fully editable
282 /// using default dropdown/table behavior.
283 pub fn maps_editable(&mut self) -> &mut Self {
284 self.maps = Some(MapSettings::editable());
285 self
286 }
287
288 /// Convenience helper: configure `f32` numerics on this member as a slider
289 /// in the range [0, 1] with clamping and `%.Nf` formatting.
290 pub fn numerics_f32_slider_0_to_1(&mut self, precision: u32) -> &mut Self {
291 self.numerics_f32 = Some(NumericTypeSettings::default().slider_0_to_1(precision));
292 self
293 }
294
295 /// Convenience helper: configure `f32` numerics on this member as a slider
296 /// in the range [-1, 1] with clamping and `%.Nf` formatting.
297 pub fn numerics_f32_slider_minus1_to_1(&mut self, precision: u32) -> &mut Self {
298 self.numerics_f32 = Some(NumericTypeSettings::default().slider_minus1_to_1(precision));
299 self
300 }
301
302 /// Convenience helper: configure `f32` numerics on this member as a drag
303 /// widget with a given speed and `%.Nf` formatting.
304 pub fn numerics_f32_drag_with_speed(&mut self, speed: f64, precision: u32) -> &mut Self {
305 self.numerics_f32 = Some(NumericTypeSettings::default().drag_with_speed(speed, precision));
306 self
307 }
308
309 /// Convenience helper: configure `f32` numerics on this member as a slider
310 /// in [0, 1] displayed as a percentage `%.Nf%%` with clamping.
311 pub fn numerics_f32_percentage_slider_0_to_1(&mut self, precision: u32) -> &mut Self {
312 self.numerics_f32 =
313 Some(NumericTypeSettings::default().percentage_slider_0_to_1(precision));
314 self
315 }
316
317 /// Convenience helper: configure `f64` numerics on this member as a slider
318 /// in the range [0, 1] with clamping and `%.Nf` formatting.
319 pub fn numerics_f64_slider_0_to_1(&mut self, precision: u32) -> &mut Self {
320 self.numerics_f64 = Some(NumericTypeSettings::default().slider_0_to_1(precision));
321 self
322 }
323
324 /// Convenience helper: configure `f64` numerics on this member as a slider
325 /// in the range [-1, 1] with clamping and `%.Nf` formatting.
326 pub fn numerics_f64_slider_minus1_to_1(&mut self, precision: u32) -> &mut Self {
327 self.numerics_f64 = Some(NumericTypeSettings::default().slider_minus1_to_1(precision));
328 self
329 }
330
331 /// Convenience helper: configure `f64` numerics on this member as a drag
332 /// widget with a given speed and `%.Nf` formatting.
333 pub fn numerics_f64_drag_with_speed(&mut self, speed: f64, precision: u32) -> &mut Self {
334 self.numerics_f64 = Some(NumericTypeSettings::default().drag_with_speed(speed, precision));
335 self
336 }
337
338 /// Convenience helper: configure `f64` numerics on this member as a slider
339 /// in [0, 1] displayed as a percentage `%.Nf%%` with clamping.
340 pub fn numerics_f64_percentage_slider_0_to_1(&mut self, precision: u32) -> &mut Self {
341 self.numerics_f64 =
342 Some(NumericTypeSettings::default().percentage_slider_0_to_1(precision));
343 self
344 }
345
346 /// Convenience helper: configure `i32` numerics on this member as an
347 /// input widget using decimal formatting (`%d`) and optional steps.
348 ///
349 /// Pass zero for `step` or `step_fast` to leave that parameter unset.
350 pub fn numerics_i32_input_decimal(&mut self, step: i32, step_fast: i32) -> &mut Self {
351 let mut numeric = NumericTypeSettings {
352 widget: NumericWidgetKind::Input,
353 ..NumericTypeSettings::default()
354 };
355 if step != 0 {
356 numeric.step = Some(step as f64);
357 }
358 if step_fast != 0 {
359 numeric.step_fast = Some(step_fast as f64);
360 }
361 numeric = numeric.with_decimal();
362 self.numerics_i32 = Some(numeric);
363 self
364 }
365
366 /// Convenience helper: configure `i32` numerics on this member as an
367 /// input widget using lowercase hexadecimal formatting (`%x`).
368 pub fn numerics_i32_input_hex(&mut self) -> &mut Self {
369 let numeric = NumericTypeSettings {
370 widget: NumericWidgetKind::Input,
371 ..NumericTypeSettings::default()
372 }
373 .with_hex(false);
374 self.numerics_i32 = Some(numeric);
375 self
376 }
377
378 /// Convenience helper: configure `i32` numerics on this member as a slider
379 /// over an explicit integer range, with optional clamping.
380 pub fn numerics_i32_slider_range(&mut self, min: i32, max: i32, clamp: bool) -> &mut Self {
381 let numeric = NumericTypeSettings {
382 widget: NumericWidgetKind::Slider,
383 range: NumericRange::Explicit {
384 min: min as f64,
385 max: max as f64,
386 },
387 clamp,
388 always_clamp: clamp,
389 ..NumericTypeSettings::default()
390 }
391 .with_decimal();
392 self.numerics_i32 = Some(numeric);
393 self
394 }
395
396 /// Convenience helper: configure `i32` numerics on this member as a slider
397 /// in the range [0, 100] with clamping.
398 pub fn numerics_i32_slider_0_to_100(&mut self) -> &mut Self {
399 self.numerics_i32_slider_range(0, 100, true)
400 }
401
402 /// Convenience helper: configure `u32` numerics on this member as an
403 /// input widget using unsigned decimal formatting (`%u`) and optional
404 /// steps. Pass zero to leave a step unset.
405 pub fn numerics_u32_input_decimal(&mut self, step: u32, step_fast: u32) -> &mut Self {
406 let mut numeric = NumericTypeSettings {
407 widget: NumericWidgetKind::Input,
408 ..NumericTypeSettings::default()
409 };
410 if step != 0 {
411 numeric.step = Some(step as f64);
412 }
413 if step_fast != 0 {
414 numeric.step_fast = Some(step_fast as f64);
415 }
416 numeric = numeric.with_unsigned();
417 self.numerics_u32 = Some(numeric);
418 self
419 }
420
421 /// Convenience helper: configure `u32` numerics on this member as an
422 /// input widget using lowercase hexadecimal formatting (`%x`).
423 pub fn numerics_u32_input_hex(&mut self) -> &mut Self {
424 let numeric = NumericTypeSettings {
425 widget: NumericWidgetKind::Input,
426 ..NumericTypeSettings::default()
427 }
428 .with_hex(false);
429 self.numerics_u32 = Some(numeric);
430 self
431 }
432
433 /// Convenience helper: configure `u32` numerics on this member as a slider
434 /// over an explicit integer range, with optional clamping.
435 pub fn numerics_u32_slider_range(&mut self, min: u32, max: u32, clamp: bool) -> &mut Self {
436 let numeric = NumericTypeSettings {
437 widget: NumericWidgetKind::Slider,
438 range: NumericRange::Explicit {
439 min: min as f64,
440 max: max as f64,
441 },
442 clamp,
443 always_clamp: clamp,
444 ..NumericTypeSettings::default()
445 }
446 .with_unsigned();
447 self.numerics_u32 = Some(numeric);
448 self
449 }
450
451 /// Convenience helper: configure `u32` numerics on this member as a slider
452 /// in the range [0, 100] with clamping.
453 pub fn numerics_u32_slider_0_to_100(&mut self) -> &mut Self {
454 self.numerics_u32_slider_range(0, 100, true)
455 }
456}
457
458/// Settings controlling how `Vec<T>` containers are edited.
459///
460/// These correspond conceptually to ImReflect's `insertable` / `removable` /
461/// `reorderable` mixins for `std::vector<T>`.
462#[derive(Clone, Debug)]
463pub struct VecSettings {
464 /// Whether insertion of new elements is allowed (via `+` button).
465 pub insertable: bool,
466 /// Whether removal of elements is allowed (via `-` button).
467 pub removable: bool,
468 /// Whether elements can be reordered using drag-and-drop handles.
469 pub reorderable: bool,
470 /// Whether the vector contents are wrapped in a collapsible tree node.
471 pub dropdown: bool,
472}
473
474impl Default for VecSettings {
475 fn default() -> Self {
476 Self::editable()
477 }
478}
479
480impl VecSettings {
481 /// Fully editable vector: insertion/removal and reordering enabled, wrapped
482 /// in a dropdown. This corresponds to the default ImReflect behavior for
483 /// `std::vector<T>`.
484 pub fn editable() -> Self {
485 Self {
486 insertable: true,
487 removable: true,
488 reorderable: true,
489 dropdown: true,
490 }
491 }
492
493 /// Reorder-only vector: disable insertion/removal, keep drag-to-reorder
494 /// handles enabled. This mirrors an ImReflect-style "reorderable only"
495 /// configuration.
496 pub fn reorder_only() -> Self {
497 Self {
498 insertable: false,
499 removable: false,
500 reorderable: true,
501 dropdown: true,
502 }
503 }
504
505 /// Fixed vector: no insertion, removal, or reordering. The contents are
506 /// still editable unless combined with `read_only`.
507 pub fn fixed() -> Self {
508 Self {
509 insertable: false,
510 removable: false,
511 reorderable: false,
512 dropdown: true,
513 }
514 }
515}
516
517/// Preferred widget style for boolean fields.
518#[derive(Clone, Copy, Debug)]
519pub enum BoolStyle {
520 /// Render using a standard ImGui checkbox.
521 Checkbox,
522 /// Render using a toggle button with text for true/false.
523 Button,
524 /// Render using two radio buttons (true/false).
525 Radio,
526 /// Render using a two-item dropdown (false/true).
527 Dropdown,
528}
529
530/// Settings controlling how `bool` fields are edited when no per-field
531/// attributes are provided.
532#[derive(Clone, Debug)]
533pub struct BoolSettings {
534 /// Default widget style for `bool` fields.
535 pub style: BoolStyle,
536}
537
538impl Default for BoolSettings {
539 fn default() -> Self {
540 Self {
541 style: BoolStyle::Checkbox,
542 }
543 }
544}
545
546/// Settings controlling how fixed-size arrays like `[T; N]` are edited.
547#[derive(Clone, Debug)]
548pub struct ArraySettings {
549 /// Whether the array contents are wrapped in a collapsible tree node.
550 pub dropdown: bool,
551 /// Whether elements can be reordered within the array.
552 pub reorderable: bool,
553}
554
555impl Default for ArraySettings {
556 fn default() -> Self {
557 Self {
558 dropdown: true,
559 reorderable: true,
560 }
561 }
562}
563
564impl ArraySettings {
565 /// Fully editable array: elements can be reordered via drag handles.
566 pub fn editable() -> Self {
567 Self {
568 dropdown: true,
569 reorderable: true,
570 }
571 }
572
573 /// Fixed-order array: reordering disabled, but still rendered in a
574 /// dropdown. This mirrors an ImReflect-style "no reorder" array.
575 pub fn fixed_order() -> Self {
576 Self {
577 dropdown: true,
578 reorderable: false,
579 }
580 }
581}
582
583/// Settings controlling how string-keyed maps like `HashMap<String, V>` and
584/// `BTreeMap<String, V>` are edited.
585#[derive(Clone, Debug)]
586pub struct MapSettings {
587 /// Whether the map contents are wrapped in a collapsible tree node.
588 pub dropdown: bool,
589 /// Whether insertion of new entries is allowed (via `+` button).
590 pub insertable: bool,
591 /// Whether removal of entries is allowed (via `-` button next to each row).
592 pub removable: bool,
593 /// Whether entries are rendered inside an ImGui table for better alignment.
594 pub use_table: bool,
595 /// Number of columns to use when `use_table` is true (at least 3).
596 ///
597 /// The first column is reserved for the row handle/context menu, the
598 /// second for the key, and the third for the value. Larger values widen
599 /// the table but currently do not change semantics.
600 pub columns: usize,
601}
602
603impl Default for MapSettings {
604 fn default() -> Self {
605 Self::editable()
606 }
607}
608
609impl MapSettings {
610 /// Fully editable map: insertion/removal enabled, optional table layout.
611 pub fn editable() -> Self {
612 Self {
613 dropdown: true,
614 insertable: true,
615 removable: true,
616 use_table: false,
617 columns: 3,
618 }
619 }
620
621 /// Const-map: insertion and removal disabled, values are still editable
622 /// unless combined with `read_only`. Uses a table layout by default for
623 /// better alignment.
624 pub fn const_map() -> Self {
625 Self {
626 dropdown: true,
627 insertable: false,
628 removable: false,
629 use_table: true,
630 columns: 3,
631 }
632 }
633}
634
635/// Preferred render mode for tuple-like values.
636#[derive(Clone, Copy, Debug)]
637pub enum TupleRenderMode {
638 /// Render all elements on a single line.
639 Line,
640 /// Render elements inside an ImGui table with multiple columns.
641 Grid,
642}
643
644/// Settings controlling how tuple-like values such as `(A, B)` and `(A, B, C)`
645/// are rendered.
646#[derive(Clone, Debug)]
647pub struct TupleSettings {
648 /// Whether the tuple contents are wrapped in a collapsible tree node.
649 pub dropdown: bool,
650 /// How tuple elements are laid out: line or grid.
651 pub render_mode: TupleRenderMode,
652 /// Number of columns to use in grid mode (clamped to at least 1 and at
653 /// most the number of tuple elements).
654 pub columns: usize,
655 /// Whether the outer label is rendered on the same line as the tuple
656 /// contents (line mode) or above them.
657 pub same_line: bool,
658 /// Optional minimum width for each element when rendered in grid mode.
659 pub min_width: Option<f32>,
660}
661
662impl Default for TupleSettings {
663 fn default() -> Self {
664 Self {
665 dropdown: false,
666 render_mode: TupleRenderMode::Line,
667 columns: 3,
668 same_line: true,
669 min_width: None,
670 }
671 }
672}
673
674/// Preferred widget style for numeric fields of a given primitive type.
675#[derive(Clone, Copy, Debug)]
676pub enum NumericWidgetKind {
677 /// Input-style widget (`InputScalar` / `input_int` / `input_float`).
678 Input,
679 /// Drag-style widget (`DragScalar`).
680 Drag,
681 /// Slider-style widget (`SliderScalar`).
682 Slider,
683}
684
685/// Range configuration for numeric sliders and drags.
686#[derive(Clone, Copy, Debug)]
687pub enum NumericRange {
688 /// No explicit range (only valid for input/drag widgets).
689 None,
690 /// Explicit minimum and maximum values (stored as `f64` and converted per type).
691 Explicit {
692 /// Minimum value in the range.
693 min: f64,
694 /// Maximum value in the range.
695 max: f64,
696 },
697 /// Use the default half-range for the numeric type when a slider is selected.
698 DefaultSlider,
699}
700
701/// Type-level settings controlling how a particular numeric primitive type is rendered.
702#[derive(Clone, Debug)]
703pub struct NumericTypeSettings {
704 /// Default widget kind for this numeric type.
705 pub widget: NumericWidgetKind,
706 /// Default range behavior for this numeric type.
707 pub range: NumericRange,
708 /// Default drag speed (for drag widgets), stored as `f64`.
709 pub speed: Option<f64>,
710 /// Default step size (for input widgets), stored as `f64`.
711 pub step: Option<f64>,
712 /// Default fast step size (for input widgets), stored as `f64`.
713 pub step_fast: Option<f64>,
714 /// Default printf-style numeric format, if any.
715 pub format: Option<String>,
716 /// Logarithmic scale flag for slider/drag widgets.
717 pub log: bool,
718 /// Post-edit manual clamp (our own helper, distinct from ImGui flags).
719 pub clamp: bool,
720 /// Always-clamp flag for slider/drag widgets.
721 pub always_clamp: bool,
722 /// Wrap-around flag for slider widgets.
723 pub wrap_around: bool,
724 /// Disable rounding to format for slider/drag widgets.
725 pub no_round_to_format: bool,
726 /// Disable direct text input on sliders.
727 pub no_input: bool,
728 /// Clamp when editing via text input.
729 pub clamp_on_input: bool,
730 /// Clamp zero-range behavior.
731 pub clamp_zero_range: bool,
732 /// Disable built-in speed tweaks for drag widgets.
733 pub no_speed_tweaks: bool,
734}
735
736impl Default for NumericTypeSettings {
737 fn default() -> Self {
738 Self {
739 widget: NumericWidgetKind::Input,
740 range: NumericRange::None,
741 speed: None,
742 step: None,
743 step_fast: None,
744 format: None,
745 log: false,
746 clamp: false,
747 always_clamp: false,
748 wrap_around: false,
749 no_round_to_format: false,
750 no_input: false,
751 clamp_on_input: false,
752 clamp_zero_range: false,
753 no_speed_tweaks: false,
754 }
755 }
756}
757
758impl NumericTypeSettings {
759 /// Set an explicit printf-style format string for this numeric type.
760 ///
761 /// This is a convenience helper for configuring the underlying ImGui
762 /// scalar/slider/drag widgets, equivalent to assigning `format` directly.
763 pub fn with_format<S: Into<String>>(mut self, fmt: S) -> Self {
764 self.format = Some(fmt.into());
765 self
766 }
767
768 /// Clear any explicit format and fall back to Dear ImGui's defaults.
769 pub fn without_format(mut self) -> Self {
770 self.format = None;
771 self
772 }
773
774 /// Decimal integer format (`%d`).
775 ///
776 /// Intended for signed integer types such as `i32`.
777 pub fn with_decimal(self) -> Self {
778 self.with_format("%d")
779 }
780
781 /// Unsigned decimal integer format (`%u`).
782 ///
783 /// Intended for unsigned integer types such as `u32`.
784 pub fn with_unsigned(self) -> Self {
785 self.with_format("%u")
786 }
787
788 /// Hexadecimal integer format (`%x` or `%X`).
789 pub fn with_hex(self, uppercase: bool) -> Self {
790 if uppercase {
791 self.with_format("%X")
792 } else {
793 self.with_format("%x")
794 }
795 }
796
797 /// Octal integer format (`%o`).
798 pub fn with_octal(self) -> Self {
799 self.with_format("%o")
800 }
801
802 /// Padded decimal integer format, e.g. width=4, pad_char='0' -> `%04d`.
803 ///
804 /// This is a small convenience for typical zero-padded integer displays;
805 /// callers should ensure the format matches the underlying numeric type.
806 pub fn with_int_padded(self, width: u32, pad_char: char) -> Self {
807 // Only the first character of `pad_char` is used; multi-codepoint
808 // characters will be truncated as in standard fmt! behavior.
809 self.with_format(format!("%{}{}", pad_char, width) + "d")
810 }
811
812 /// Character format (`%c`), useful for small integer types interpreted as chars.
813 pub fn with_char(self) -> Self {
814 self.with_format("%c")
815 }
816
817 /// Floating-point format `%.Nf`, e.g. precision=3 -> `%.3f`.
818 pub fn with_float(self, precision: u32) -> Self {
819 self.with_format(format!("%.{}f", precision))
820 }
821
822 /// Double format `%.Nlf`, primarily for parity with ImReflect's helpers.
823 pub fn with_double(self, precision: u32) -> Self {
824 self.with_format(format!("%.{}lf", precision))
825 }
826
827 /// Scientific notation `%.Ne` / `%.NE`.
828 pub fn with_scientific(self, precision: u32, uppercase: bool) -> Self {
829 let spec = if uppercase { 'E' } else { 'e' };
830 self.with_format(format!("%.{}{}", precision, spec))
831 }
832
833 /// Percentage format `%.Nf%%` (e.g. `12.3%`).
834 pub fn with_percentage(self, precision: u32) -> Self {
835 self.with_format(format!("%.{}f%%", precision))
836 }
837
838 /// Convenience preset: slider in the range [0, 1] with clamping and a
839 /// floating-point display format `%.Nf`.
840 ///
841 /// This is primarily intended for floating-point types (`f32` / `f64`).
842 pub fn slider_0_to_1(mut self, precision: u32) -> Self {
843 self.widget = NumericWidgetKind::Slider;
844 self.range = NumericRange::Explicit { min: 0.0, max: 1.0 };
845 self.clamp = true;
846 self.always_clamp = true;
847 self.with_float(precision)
848 }
849
850 /// Convenience preset: slider in the range [-1, 1] with clamping and a
851 /// floating-point display format `%.Nf`.
852 ///
853 /// This is primarily intended for floating-point types (`f32` / `f64`).
854 pub fn slider_minus1_to_1(mut self, precision: u32) -> Self {
855 self.widget = NumericWidgetKind::Slider;
856 self.range = NumericRange::Explicit {
857 min: -1.0,
858 max: 1.0,
859 };
860 self.clamp = true;
861 self.always_clamp = true;
862 self.with_float(precision)
863 }
864
865 /// Convenience preset: drag widget with a given speed and floating-point
866 /// display format `%.Nf`.
867 ///
868 /// This is primarily intended for floating-point types.
869 pub fn drag_with_speed(mut self, speed: f64, precision: u32) -> Self {
870 self.widget = NumericWidgetKind::Drag;
871 self.range = NumericRange::None;
872 self.speed = Some(speed);
873 self.with_float(precision)
874 }
875
876 /// Convenience preset: slider in [0, 1] displayed as a percentage
877 /// `%.Nf%%` with clamping.
878 ///
879 /// This expects the underlying numeric value to live in the 0..1 range.
880 pub fn percentage_slider_0_to_1(mut self, precision: u32) -> Self {
881 self.widget = NumericWidgetKind::Slider;
882 self.range = NumericRange::Explicit { min: 0.0, max: 1.0 };
883 self.clamp = true;
884 self.always_clamp = true;
885 self.with_percentage(precision)
886 }
887}
888
889static GLOBAL_SETTINGS: OnceLock<Mutex<ReflectSettings>> = OnceLock::new();
890
891fn settings_mutex() -> &'static Mutex<ReflectSettings> {
892 GLOBAL_SETTINGS.get_or_init(|| Mutex::new(ReflectSettings::default()))
893}
894
895/// Runs `f` with a shared reference to the current global `ReflectSettings`.
896///
897/// This helper avoids cloning the settings object on read-only access paths
898/// (such as container `ImGuiValue` implementations) while preserving the
899/// existing `current_settings()` API for callers that need an owned copy.
900pub(crate) fn with_settings_read<F, R>(f: F) -> R
901where
902 F: FnOnce(&ReflectSettings) -> R,
903{
904 let guard = settings_mutex()
905 .lock()
906 .unwrap_or_else(|err| err.into_inner());
907 f(&guard)
908}
909
910/// Returns a clone of the current global `ReflectSettings`.
911///
912/// This is used internally by container editors to honor type-level defaults.
913pub fn current_settings() -> ReflectSettings {
914 settings_mutex()
915 .lock()
916 .unwrap_or_else(|err| err.into_inner())
917 .clone()
918}
919
920/// Mutates the global `ReflectSettings` in place.
921///
922/// This provides an `ImSettings`-style entry point to configure type-level
923/// defaults for container editing behavior.
924pub fn with_settings<F, R>(f: F) -> R
925where
926 F: FnOnce(&mut ReflectSettings) -> R,
927{
928 let mut guard = settings_mutex()
929 .lock()
930 .unwrap_or_else(|err| err.into_inner());
931 f(&mut guard)
932}
933
934/// Executes a closure with a temporary modification of the global
935/// [`ReflectSettings`], restoring the previous settings afterwards.
936///
937/// This provides a lightweight, ImSettings-style "scope" mechanism for
938/// experiments or per-panel overrides without introducing a full push/pop
939/// stack API. A typical usage pattern is:
940///
941/// ```no_run
942/// # use dear_imgui_reflect as reflect;
943/// # use reflect::ImGuiReflectExt;
944/// #
945/// # #[derive(reflect::ImGuiReflect, Default)]
946/// # struct MyType {
947/// # items: Vec<i32>,
948/// # }
949/// #
950/// # fn ui_frame(ui: &reflect::imgui::Ui, value: &mut MyType) {
951/// reflect::with_settings_scope(|| {
952/// reflect::with_settings(|s| {
953/// s.for_member::<MyType>("items").vec_reorder_only();
954/// });
955/// ui.input_reflect("Debug Items", value);
956/// });
957/// # }
958/// ```
959///
960/// Inside the scope, `with_settings` can be used freely; once the closure
961/// returns, the previous `ReflectSettings` are restored.
962pub fn with_settings_scope<F, R>(f: F) -> R
963where
964 F: FnOnce() -> R,
965{
966 struct RestoreGuard {
967 saved: Option<ReflectSettings>,
968 }
969
970 impl Drop for RestoreGuard {
971 fn drop(&mut self) {
972 // Avoid panicking in Drop: if the mutex is poisoned, restoring the
973 // settings is best-effort only.
974 let Some(saved) = self.saved.take() else {
975 return;
976 };
977 if let Ok(mut guard) = settings_mutex().lock() {
978 *guard = saved;
979 }
980 }
981 }
982
983 // Snapshot current settings.
984 let saved = current_settings();
985 let _guard = RestoreGuard { saved: Some(saved) };
986 // Run user code, allowing it to call `with_settings` as needed.
987 f()
988}