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        let count = match i32::try_from(values.len()) {
130            Ok(n) => n,
131            Err(_) => return false,
132        };
133        unsafe {
134            let (label, display_format) = self
135                .ui
136                .scratch_txt_with_opt(self.label, self.display_format);
137
138            sys::igSliderScalarN(
139                label,
140                Data::KIND as i32,
141                values.as_mut_ptr() as *mut c_void,
142                count,
143                &self.min as *const Data as *const c_void,
144                &self.max as *const Data as *const c_void,
145                display_format,
146                self.flags.bits(),
147            )
148        }
149    }
150}
151
152/// Builder for a vertical slider widget.
153#[derive(Clone, Debug)]
154#[must_use]
155pub struct VerticalSlider<Label, Data, Format = &'static str> {
156    label: Label,
157    size: [f32; 2],
158    min: Data,
159    max: Data,
160    display_format: Option<Format>,
161    flags: SliderFlags,
162}
163
164impl<Label, Data> VerticalSlider<Label, Data>
165where
166    Label: AsRef<str>,
167    Data: DataTypeKind,
168{
169    /// Constructs a new vertical slider builder with the given size and range.
170    ///
171    /// ```no_run
172    /// # use dear_imgui_rs::*;
173    /// # let mut ctx = Context::create();
174    /// # let ui = ctx.frame();
175    /// VerticalSlider::new("Example", [20.0, 20.0], i8::MIN, i8::MAX)
176    ///     .range(4, 8)
177    ///     // Remember to call .build(&ui)
178    ///     ;
179    /// ```
180    ///
181    /// It is safe, though up to C++ Dear ImGui, on how to handle when
182    /// `min > max`.
183    #[doc(alias = "VSliderScalar")]
184    pub fn new(label: Label, size: impl Into<[f32; 2]>, min: Data, max: Data) -> Self {
185        VerticalSlider {
186            label,
187            size: size.into(),
188            min,
189            max,
190            display_format: None,
191            flags: SliderFlags::NONE,
192        }
193    }
194}
195
196impl<Label, Data, Format> VerticalSlider<Label, Data, Format>
197where
198    Label: AsRef<str>,
199    Data: DataTypeKind,
200    Format: AsRef<str>,
201{
202    /// Sets the range for the vertical slider.
203    ///
204    /// ```no_run
205    /// # use dear_imgui_rs::*;
206    /// # let mut ctx = Context::create();
207    /// # let ui = ctx.frame();
208    /// VerticalSlider::new("Example", [20.0, 20.0], i8::MIN, i8::MAX)
209    ///     .range(4, 8)
210    ///     // Remember to call .build(&ui)
211    ///     ;
212    /// ```
213    ///
214    /// It is safe, though up to C++ Dear ImGui, on how to handle when
215    /// `min > max`.
216    #[inline]
217    pub fn range(mut self, min: Data, max: Data) -> Self {
218        self.min = min;
219        self.max = max;
220        self
221    }
222
223    /// Sets the display format using *a C-style printf string*
224    #[inline]
225    pub fn display_format<Format2: AsRef<str>>(
226        self,
227        display_format: Format2,
228    ) -> VerticalSlider<Label, Data, Format2> {
229        VerticalSlider {
230            label: self.label,
231            size: self.size,
232            min: self.min,
233            max: self.max,
234            display_format: Some(display_format),
235            flags: self.flags,
236        }
237    }
238
239    /// Replaces all current settings with the given flags
240    #[inline]
241    pub fn flags(mut self, flags: SliderFlags) -> Self {
242        self.flags = flags;
243        self
244    }
245
246    /// Builds a vertical slider that is bound to the given value.
247    ///
248    /// Returns true if the slider value was changed.
249    pub fn build(self, ui: &Ui, value: &mut Data) -> bool {
250        unsafe {
251            let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
252            let size = sys::ImVec2::new(self.size[0], self.size[1]);
253
254            sys::igVSliderScalar(
255                label,
256                size,
257                Data::KIND as i32,
258                value as *mut Data as *mut c_void,
259                &self.min as *const Data as *const c_void,
260                &self.max as *const Data as *const c_void,
261                display_format,
262                self.flags.bits(),
263            )
264        }
265    }
266}
267
268/// Builder for an angle slider widget.
269#[derive(Copy, Clone, Debug)]
270#[must_use]
271pub struct AngleSlider<Label, Format = &'static str> {
272    label: Label,
273    min_degrees: f32,
274    max_degrees: f32,
275    display_format: Format,
276    flags: SliderFlags,
277}
278
279impl<Label> AngleSlider<Label>
280where
281    Label: AsRef<str>,
282{
283    /// Constructs a new angle slider builder, where its minimum defaults to -360.0 and
284    /// maximum defaults to 360.0
285    #[doc(alias = "SliderAngle")]
286    pub fn new(label: Label) -> Self {
287        AngleSlider {
288            label,
289            min_degrees: -360.0,
290            max_degrees: 360.0,
291            display_format: "%.0f deg",
292            flags: SliderFlags::NONE,
293        }
294    }
295}
296
297impl<Label, Format> AngleSlider<Label, Format>
298where
299    Label: AsRef<str>,
300    Format: AsRef<str>,
301{
302    /// Sets the range in degrees (inclusive)
303    /// ```no_run
304    /// # use dear_imgui_rs::*;
305    /// # let mut ctx = Context::create();
306    /// # let ui = ctx.frame();
307    /// AngleSlider::new("Example")
308    ///     .range_degrees(-20.0, 20.0)
309    ///     // Remember to call .build(&ui)
310    ///     ;
311    /// ```
312    ///
313    /// It is safe, though up to C++ Dear ImGui, on how to handle when
314    /// `min > max`.
315    #[inline]
316    pub fn range_degrees(mut self, min_degrees: f32, max_degrees: f32) -> Self {
317        self.min_degrees = min_degrees;
318        self.max_degrees = max_degrees;
319        self
320    }
321
322    /// Sets the minimum value (in degrees)
323    #[inline]
324    pub fn min_degrees(mut self, min_degrees: f32) -> Self {
325        self.min_degrees = min_degrees;
326        self
327    }
328
329    /// Sets the maximum value (in degrees)
330    #[inline]
331    pub fn max_degrees(mut self, max_degrees: f32) -> Self {
332        self.max_degrees = max_degrees;
333        self
334    }
335
336    /// Sets the display format using *a C-style printf string*
337    #[inline]
338    pub fn display_format<Format2: AsRef<str>>(
339        self,
340        display_format: Format2,
341    ) -> AngleSlider<Label, Format2> {
342        AngleSlider {
343            label: self.label,
344            min_degrees: self.min_degrees,
345            max_degrees: self.max_degrees,
346            display_format,
347            flags: self.flags,
348        }
349    }
350
351    /// Replaces all current settings with the given flags
352    #[inline]
353    pub fn flags(mut self, flags: SliderFlags) -> Self {
354        self.flags = flags;
355        self
356    }
357
358    /// Builds an angle slider that is bound to the given value (in radians).
359    ///
360    /// Returns true if the slider value was changed.
361    pub fn build(self, ui: &Ui, value_rad: &mut f32) -> bool {
362        unsafe {
363            let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format);
364
365            sys::igSliderAngle(
366                label,
367                value_rad as *mut _,
368                self.min_degrees,
369                self.max_degrees,
370                display_format,
371                self.flags.bits(),
372            )
373        }
374    }
375}
376
377bitflags::bitflags! {
378    /// Flags for slider widgets
379    #[repr(transparent)]
380    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
381    pub struct SliderFlags: i32 {
382        /// No flags
383        const NONE = 0;
384        /// Wrap the value around when exceeding the current range.
385        ///
386        /// Corresponds to `ImGuiSliderFlags_WrapAround`.
387        const WRAP_AROUND = sys::ImGuiSliderFlags_WrapAround as i32;
388        /// Clamp on input when editing via CTRL+Click or direct text input.
389        ///
390        /// This is one of the two bits combined by `ALWAYS_CLAMP`.
391        const CLAMP_ON_INPUT = sys::ImGuiSliderFlags_ClampOnInput as i32;
392        /// Clamp zero-range sliders to avoid a zero-sized range.
393        ///
394        /// This is one of the two bits combined by `ALWAYS_CLAMP`.
395        const CLAMP_ZERO_RANGE = sys::ImGuiSliderFlags_ClampZeroRange as i32;
396        /// Disable small "smart" speed tweaks for very small/large ranges.
397        ///
398        /// Useful when precise, linear dragging behavior is desired.
399        const NO_SPEED_TWEAKS = sys::ImGuiSliderFlags_NoSpeedTweaks as i32;
400        /// Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
401        const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
402        /// Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
403        const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
404        /// Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits)
405        const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
406        /// Disable CTRL+Click or Enter key allowing to input text directly into the widget
407        const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
408    }
409}
410
411impl Ui {
412    /// Creates a new slider widget. Returns true if the value has been edited.
413    pub fn slider<T: AsRef<str>, K: DataTypeKind>(
414        &self,
415        label: T,
416        min: K,
417        max: K,
418        value: &mut K,
419    ) -> bool {
420        self.slider_config(label, min, max).build(value)
421    }
422
423    /// Creates a new unbuilt Slider.
424    pub fn slider_config<T: AsRef<str>, K: DataTypeKind>(
425        &self,
426        label: T,
427        min: K,
428        max: K,
429    ) -> Slider<'_, T, K> {
430        Slider {
431            ui: self,
432            label,
433            min,
434            max,
435            display_format: Option::<&'static str>::None,
436            flags: SliderFlags::NONE,
437        }
438    }
439
440    /// Creates a float slider
441    #[doc(alias = "SliderFloat")]
442    pub fn slider_f32(&self, label: impl AsRef<str>, value: &mut f32, min: f32, max: f32) -> bool {
443        self.slider_config(label, min, max).build(value)
444    }
445
446    /// Creates an integer slider
447    #[doc(alias = "SliderInt")]
448    pub fn slider_i32(&self, label: impl AsRef<str>, value: &mut i32, min: i32, max: i32) -> bool {
449        self.slider_config(label, min, max).build(value)
450    }
451
452    /// Creates a vertical slider
453    #[doc(alias = "VSliderFloat")]
454    pub fn v_slider_f32(
455        &self,
456        label: impl AsRef<str>,
457        size: impl Into<[f32; 2]>,
458        value: &mut f32,
459        min: f32,
460        max: f32,
461    ) -> bool {
462        VerticalSlider::new(label, size, min, max).build(self, value)
463    }
464
465    /// Creates a vertical integer slider
466    #[doc(alias = "VSliderInt")]
467    pub fn v_slider_i32(
468        &self,
469        label: impl AsRef<str>,
470        size: impl Into<[f32; 2]>,
471        value: &mut i32,
472        min: i32,
473        max: i32,
474    ) -> bool {
475        VerticalSlider::new(label, size, min, max).build(self, value)
476    }
477
478    /// Creates an angle slider (value in radians)
479    #[doc(alias = "SliderAngle")]
480    pub fn slider_angle(&self, label: impl AsRef<str>, value_rad: &mut f32) -> bool {
481        AngleSlider::new(label).build(self, value_rad)
482    }
483}