dear_imgui_rs/widget/
slider.rs

1//! Sliders
2//!
3//! Scalar and range sliders for numeric input with builder-based configuration
4//! of speed, format and clamping.
5//!
6#![allow(
7    clippy::cast_possible_truncation,
8    clippy::cast_sign_loss,
9    clippy::as_conversions
10)]
11use crate::Ui;
12use crate::internal::DataTypeKind;
13use crate::sys;
14use std::ffi::c_void;
15
16/// Builder for slider widgets
17#[derive(Clone, Debug)]
18#[must_use]
19pub struct Slider<'ui, Label, Data, Format = &'static str> {
20    ui: &'ui Ui,
21    label: Label,
22    min: Data,
23    max: Data,
24    display_format: Option<Format>,
25    flags: SliderFlags,
26}
27
28impl<'ui, Label, Data> Slider<'ui, Label, Data>
29where
30    Label: AsRef<str>,
31    Data: DataTypeKind,
32{
33    /// Creates a new slider builder
34    #[doc(alias = "SliderScalar", alias = "SliderScalarN")]
35    #[deprecated(note = "Use `Ui::slider` or `Ui::slider_config`.", since = "0.1.0")]
36    pub fn new(ui: &'ui Ui, label: Label, min: Data, max: Data) -> Self {
37        Self {
38            ui,
39            label,
40            min,
41            max,
42            display_format: None,
43            flags: SliderFlags::NONE,
44        }
45    }
46}
47
48impl<'ui, Label, Data, Format> Slider<'ui, Label, Data, Format>
49where
50    Label: AsRef<str>,
51    Data: DataTypeKind,
52    Format: AsRef<str>,
53{
54    /// Sets the range inclusively, such that both values given
55    /// are valid values which the slider can be dragged to.
56    ///
57    /// ```no_run
58    /// # use dear_imgui_rs::*;
59    /// # let mut ctx = Context::create();
60    /// # let ui = ctx.frame();
61    /// ui.slider_config("Example", i8::MIN, i8::MAX)
62    ///     .range(4, 8)
63    ///     // Remember to call .build()
64    ///     ;
65    /// ```
66    ///
67    /// It is safe, though up to C++ Dear ImGui, on how to handle when
68    /// `min > max`.
69    ///
70    /// Note for f32 and f64 sliders, Dear ImGui limits the available
71    /// range to half their full range (e.g `f32::MIN/2.0 .. f32::MAX/2.0`)
72    /// Specifying a value above this will cause an abort.
73    /// For large ranged values, consider using input widgets instead
74    #[inline]
75    pub fn range(mut self, min: Data, max: Data) -> Self {
76        self.min = min;
77        self.max = max;
78        self
79    }
80
81    /// Sets the display format using *a C-style printf string*
82    #[inline]
83    pub fn display_format<Format2: AsRef<str>>(
84        self,
85        display_format: Format2,
86    ) -> Slider<'ui, Label, Data, Format2> {
87        Slider {
88            ui: self.ui,
89            label: self.label,
90            min: self.min,
91            max: self.max,
92            display_format: Some(display_format),
93            flags: self.flags,
94        }
95    }
96
97    /// Replaces all current settings with the given flags
98    #[inline]
99    pub fn flags(mut self, flags: SliderFlags) -> Self {
100        self.flags = flags;
101        self
102    }
103
104    /// Builds a slider that is bound to the given value.
105    ///
106    /// Returns true if the slider value was changed.
107    pub fn build(self, value: &mut Data) -> bool {
108        unsafe {
109            let (label, display_format) = self
110                .ui
111                .scratch_txt_with_opt(self.label, self.display_format);
112
113            sys::igSliderScalar(
114                label,
115                Data::KIND as i32,
116                value as *mut Data as *mut c_void,
117                &self.min as *const Data as *const c_void,
118                &self.max as *const Data as *const c_void,
119                display_format,
120                self.flags.bits(),
121            )
122        }
123    }
124
125    /// Builds a horizontal array of multiple sliders attached to the given slice.
126    ///
127    /// Returns true if any slider value was changed.
128    pub fn build_array(self, values: &mut [Data]) -> bool {
129        unsafe {
130            let (label, display_format) = self
131                .ui
132                .scratch_txt_with_opt(self.label, self.display_format);
133
134            sys::igSliderScalarN(
135                label,
136                Data::KIND as i32,
137                values.as_mut_ptr() as *mut c_void,
138                values.len() as i32,
139                &self.min as *const Data as *const c_void,
140                &self.max as *const Data as *const c_void,
141                display_format,
142                self.flags.bits(),
143            )
144        }
145    }
146}
147
148/// Builder for a vertical slider widget.
149#[derive(Clone, Debug)]
150#[must_use]
151pub struct VerticalSlider<Label, Data, Format = &'static str> {
152    label: Label,
153    size: [f32; 2],
154    min: Data,
155    max: Data,
156    display_format: Option<Format>,
157    flags: SliderFlags,
158}
159
160impl<Label, Data> VerticalSlider<Label, Data>
161where
162    Label: AsRef<str>,
163    Data: DataTypeKind,
164{
165    /// Constructs a new vertical slider builder with the given size and range.
166    ///
167    /// ```no_run
168    /// # use dear_imgui_rs::*;
169    /// # let mut ctx = Context::create();
170    /// # let ui = ctx.frame();
171    /// VerticalSlider::new("Example", [20.0, 20.0], i8::MIN, i8::MAX)
172    ///     .range(4, 8)
173    ///     // Remember to call .build(&ui)
174    ///     ;
175    /// ```
176    ///
177    /// It is safe, though up to C++ Dear ImGui, on how to handle when
178    /// `min > max`.
179    #[doc(alias = "VSliderScalar")]
180    pub fn new(label: Label, size: impl Into<[f32; 2]>, min: Data, max: Data) -> Self {
181        VerticalSlider {
182            label,
183            size: size.into(),
184            min,
185            max,
186            display_format: None,
187            flags: SliderFlags::NONE,
188        }
189    }
190}
191
192impl<Label, Data, Format> VerticalSlider<Label, Data, Format>
193where
194    Label: AsRef<str>,
195    Data: DataTypeKind,
196    Format: AsRef<str>,
197{
198    /// Sets the range for the vertical slider.
199    ///
200    /// ```no_run
201    /// # use dear_imgui_rs::*;
202    /// # let mut ctx = Context::create();
203    /// # let ui = ctx.frame();
204    /// VerticalSlider::new("Example", [20.0, 20.0], i8::MIN, i8::MAX)
205    ///     .range(4, 8)
206    ///     // Remember to call .build(&ui)
207    ///     ;
208    /// ```
209    ///
210    /// It is safe, though up to C++ Dear ImGui, on how to handle when
211    /// `min > max`.
212    #[inline]
213    pub fn range(mut self, min: Data, max: Data) -> Self {
214        self.min = min;
215        self.max = max;
216        self
217    }
218
219    /// Sets the display format using *a C-style printf string*
220    #[inline]
221    pub fn display_format<Format2: AsRef<str>>(
222        self,
223        display_format: Format2,
224    ) -> VerticalSlider<Label, Data, Format2> {
225        VerticalSlider {
226            label: self.label,
227            size: self.size,
228            min: self.min,
229            max: self.max,
230            display_format: Some(display_format),
231            flags: self.flags,
232        }
233    }
234
235    /// Replaces all current settings with the given flags
236    #[inline]
237    pub fn flags(mut self, flags: SliderFlags) -> Self {
238        self.flags = flags;
239        self
240    }
241
242    /// Builds a vertical slider that is bound to the given value.
243    ///
244    /// Returns true if the slider value was changed.
245    pub fn build(self, ui: &Ui, value: &mut Data) -> bool {
246        unsafe {
247            let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
248            let size = sys::ImVec2::new(self.size[0], self.size[1]);
249
250            sys::igVSliderScalar(
251                label,
252                size,
253                Data::KIND as i32,
254                value as *mut Data as *mut c_void,
255                &self.min as *const Data as *const c_void,
256                &self.max as *const Data as *const c_void,
257                display_format,
258                self.flags.bits(),
259            )
260        }
261    }
262}
263
264/// Builder for an angle slider widget.
265#[derive(Copy, Clone, Debug)]
266#[must_use]
267pub struct AngleSlider<Label, Format = &'static str> {
268    label: Label,
269    min_degrees: f32,
270    max_degrees: f32,
271    display_format: Format,
272    flags: SliderFlags,
273}
274
275impl<Label> AngleSlider<Label>
276where
277    Label: AsRef<str>,
278{
279    /// Constructs a new angle slider builder, where its minimum defaults to -360.0 and
280    /// maximum defaults to 360.0
281    #[doc(alias = "SliderAngle")]
282    pub fn new(label: Label) -> Self {
283        AngleSlider {
284            label,
285            min_degrees: -360.0,
286            max_degrees: 360.0,
287            display_format: "%.0f deg",
288            flags: SliderFlags::NONE,
289        }
290    }
291}
292
293impl<Label, Format> AngleSlider<Label, Format>
294where
295    Label: AsRef<str>,
296    Format: AsRef<str>,
297{
298    /// Sets the range in degrees (inclusive)
299    /// ```no_run
300    /// # use dear_imgui_rs::*;
301    /// # let mut ctx = Context::create();
302    /// # let ui = ctx.frame();
303    /// AngleSlider::new("Example")
304    ///     .range_degrees(-20.0, 20.0)
305    ///     // Remember to call .build(&ui)
306    ///     ;
307    /// ```
308    ///
309    /// It is safe, though up to C++ Dear ImGui, on how to handle when
310    /// `min > max`.
311    #[inline]
312    pub fn range_degrees(mut self, min_degrees: f32, max_degrees: f32) -> Self {
313        self.min_degrees = min_degrees;
314        self.max_degrees = max_degrees;
315        self
316    }
317
318    /// Sets the minimum value (in degrees)
319    #[inline]
320    pub fn min_degrees(mut self, min_degrees: f32) -> Self {
321        self.min_degrees = min_degrees;
322        self
323    }
324
325    /// Sets the maximum value (in degrees)
326    #[inline]
327    pub fn max_degrees(mut self, max_degrees: f32) -> Self {
328        self.max_degrees = max_degrees;
329        self
330    }
331
332    /// Sets the display format using *a C-style printf string*
333    #[inline]
334    pub fn display_format<Format2: AsRef<str>>(
335        self,
336        display_format: Format2,
337    ) -> AngleSlider<Label, Format2> {
338        AngleSlider {
339            label: self.label,
340            min_degrees: self.min_degrees,
341            max_degrees: self.max_degrees,
342            display_format,
343            flags: self.flags,
344        }
345    }
346
347    /// Replaces all current settings with the given flags
348    #[inline]
349    pub fn flags(mut self, flags: SliderFlags) -> Self {
350        self.flags = flags;
351        self
352    }
353
354    /// Builds an angle slider that is bound to the given value (in radians).
355    ///
356    /// Returns true if the slider value was changed.
357    pub fn build(self, ui: &Ui, value_rad: &mut f32) -> bool {
358        unsafe {
359            let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format);
360
361            sys::igSliderAngle(
362                label,
363                value_rad as *mut _,
364                self.min_degrees,
365                self.max_degrees,
366                display_format,
367                self.flags.bits(),
368            )
369        }
370    }
371}
372
373bitflags::bitflags! {
374    /// Flags for slider widgets
375    #[repr(transparent)]
376    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
377    pub struct SliderFlags: i32 {
378        /// No flags
379        const NONE = 0;
380        /// Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
381        const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
382        /// Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
383        const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
384        /// Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits)
385        const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
386        /// Disable CTRL+Click or Enter key allowing to input text directly into the widget
387        const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
388    }
389}
390
391impl Ui {
392    /// Creates a new slider widget. Returns true if the value has been edited.
393    pub fn slider<T: AsRef<str>, K: DataTypeKind>(
394        &self,
395        label: T,
396        min: K,
397        max: K,
398        value: &mut K,
399    ) -> bool {
400        self.slider_config(label, min, max).build(value)
401    }
402
403    /// Creates a new unbuilt Slider.
404    pub fn slider_config<T: AsRef<str>, K: DataTypeKind>(
405        &self,
406        label: T,
407        min: K,
408        max: K,
409    ) -> Slider<'_, T, K> {
410        Slider {
411            ui: self,
412            label,
413            min,
414            max,
415            display_format: Option::<&'static str>::None,
416            flags: SliderFlags::NONE,
417        }
418    }
419
420    /// Creates a float slider
421    #[doc(alias = "SliderFloat")]
422    pub fn slider_f32(&self, label: impl AsRef<str>, value: &mut f32, min: f32, max: f32) -> bool {
423        self.slider_config(label, min, max).build(value)
424    }
425
426    /// Creates an integer slider
427    #[doc(alias = "SliderInt")]
428    pub fn slider_i32(&self, label: impl AsRef<str>, value: &mut i32, min: i32, max: i32) -> bool {
429        self.slider_config(label, min, max).build(value)
430    }
431
432    /// Creates a vertical slider
433    #[doc(alias = "VSliderFloat")]
434    pub fn v_slider_f32(
435        &self,
436        label: impl AsRef<str>,
437        size: impl Into<[f32; 2]>,
438        value: &mut f32,
439        min: f32,
440        max: f32,
441    ) -> bool {
442        VerticalSlider::new(label, size, min, max).build(self, value)
443    }
444
445    /// Creates a vertical integer slider
446    #[doc(alias = "VSliderInt")]
447    pub fn v_slider_i32(
448        &self,
449        label: impl AsRef<str>,
450        size: impl Into<[f32; 2]>,
451        value: &mut i32,
452        min: i32,
453        max: i32,
454    ) -> bool {
455        VerticalSlider::new(label, size, min, max).build(self, value)
456    }
457
458    /// Creates an angle slider (value in radians)
459    #[doc(alias = "SliderAngle")]
460    pub fn slider_angle(&self, label: impl AsRef<str>, value_rad: &mut f32) -> bool {
461        AngleSlider::new(label).build(self, value_rad)
462    }
463}