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}