Skip to main content

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        /// Clamp on input when editing via CTRL+Click or direct text input.
385        ///
386        /// This is one of the two bits combined by `ALWAYS_CLAMP`.
387        const CLAMP_ON_INPUT = sys::ImGuiSliderFlags_ClampOnInput as i32;
388        /// Clamp zero-range sliders to avoid a zero-sized range.
389        ///
390        /// This is one of the two bits combined by `ALWAYS_CLAMP`.
391        const CLAMP_ZERO_RANGE = sys::ImGuiSliderFlags_ClampZeroRange as i32;
392        /// Disable small "smart" speed tweaks for very small/large ranges.
393        ///
394        /// Useful when precise, linear dragging behavior is desired.
395        const NO_SPEED_TWEAKS = sys::ImGuiSliderFlags_NoSpeedTweaks as i32;
396        /// Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
397        const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
398        /// Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
399        const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
400        /// Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits)
401        const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
402        /// Disable CTRL+Click or Enter key allowing to input text directly into the widget
403        const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
404    }
405}
406
407impl Ui {
408    /// Creates a new slider widget. Returns true if the value has been edited.
409    pub fn slider<T: AsRef<str>, K: DataTypeKind>(
410        &self,
411        label: T,
412        min: K,
413        max: K,
414        value: &mut K,
415    ) -> bool {
416        self.slider_config(label, min, max).build(value)
417    }
418
419    /// Creates a new unbuilt Slider.
420    pub fn slider_config<T: AsRef<str>, K: DataTypeKind>(
421        &self,
422        label: T,
423        min: K,
424        max: K,
425    ) -> Slider<'_, T, K> {
426        Slider {
427            ui: self,
428            label,
429            min,
430            max,
431            display_format: Option::<&'static str>::None,
432            flags: SliderFlags::NONE,
433        }
434    }
435
436    /// Creates a float slider
437    #[doc(alias = "SliderFloat")]
438    pub fn slider_f32(&self, label: impl AsRef<str>, value: &mut f32, min: f32, max: f32) -> bool {
439        self.slider_config(label, min, max).build(value)
440    }
441
442    /// Creates an integer slider
443    #[doc(alias = "SliderInt")]
444    pub fn slider_i32(&self, label: impl AsRef<str>, value: &mut i32, min: i32, max: i32) -> bool {
445        self.slider_config(label, min, max).build(value)
446    }
447
448    /// Creates a float2 slider
449    #[doc(alias = "SliderFloat2")]
450    pub fn slider_float2(
451        &self,
452        label: impl AsRef<str>,
453        value: &mut [f32; 2],
454        min: f32,
455        max: f32,
456    ) -> bool {
457        self.slider_config(label, min, max)
458            .build_array(value.as_mut_slice())
459    }
460
461    /// Creates a float3 slider
462    #[doc(alias = "SliderFloat3")]
463    pub fn slider_float3(
464        &self,
465        label: impl AsRef<str>,
466        value: &mut [f32; 3],
467        min: f32,
468        max: f32,
469    ) -> bool {
470        self.slider_config(label, min, max)
471            .build_array(value.as_mut_slice())
472    }
473
474    /// Creates a float4 slider
475    #[doc(alias = "SliderFloat4")]
476    pub fn slider_float4(
477        &self,
478        label: impl AsRef<str>,
479        value: &mut [f32; 4],
480        min: f32,
481        max: f32,
482    ) -> bool {
483        self.slider_config(label, min, max)
484            .build_array(value.as_mut_slice())
485    }
486
487    /// Creates an int2 slider
488    #[doc(alias = "SliderInt2")]
489    pub fn slider_int2(
490        &self,
491        label: impl AsRef<str>,
492        value: &mut [i32; 2],
493        min: i32,
494        max: i32,
495    ) -> bool {
496        self.slider_config(label, min, max)
497            .build_array(value.as_mut_slice())
498    }
499
500    /// Creates an int3 slider
501    #[doc(alias = "SliderInt3")]
502    pub fn slider_int3(
503        &self,
504        label: impl AsRef<str>,
505        value: &mut [i32; 3],
506        min: i32,
507        max: i32,
508    ) -> bool {
509        self.slider_config(label, min, max)
510            .build_array(value.as_mut_slice())
511    }
512
513    /// Creates an int4 slider
514    #[doc(alias = "SliderInt4")]
515    pub fn slider_int4(
516        &self,
517        label: impl AsRef<str>,
518        value: &mut [i32; 4],
519        min: i32,
520        max: i32,
521    ) -> bool {
522        self.slider_config(label, min, max)
523            .build_array(value.as_mut_slice())
524    }
525
526    /// Creates a vertical slider
527    #[doc(alias = "VSliderFloat")]
528    pub fn v_slider_f32(
529        &self,
530        label: impl AsRef<str>,
531        size: impl Into<[f32; 2]>,
532        value: &mut f32,
533        min: f32,
534        max: f32,
535    ) -> bool {
536        VerticalSlider::new(label, size, min, max).build(self, value)
537    }
538
539    /// Creates a vertical integer slider
540    #[doc(alias = "VSliderInt")]
541    pub fn v_slider_i32(
542        &self,
543        label: impl AsRef<str>,
544        size: impl Into<[f32; 2]>,
545        value: &mut i32,
546        min: i32,
547        max: i32,
548    ) -> bool {
549        VerticalSlider::new(label, size, min, max).build(self, value)
550    }
551
552    /// Creates an angle slider (value in radians)
553    #[doc(alias = "SliderAngle")]
554    pub fn slider_angle(&self, label: impl AsRef<str>, value_rad: &mut f32) -> bool {
555        AngleSlider::new(label).build(self, value_rad)
556    }
557}