dear_imgui/widget/
slider.rs

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