Skip to main content

dear_imgui_rs/widget/
drag.rs

1//! Drag slider widgets for numeric input
2//!
3//! Drag sliders allow users to modify numeric values by dragging with the mouse.
4//! They provide a more intuitive way to adjust values compared to text input.
5
6use std::os::raw::c_void;
7use std::ptr;
8
9use crate::Ui;
10use crate::internal::DataTypeKind;
11use crate::sys;
12use crate::widget::slider::SliderFlags;
13
14bitflags::bitflags! {
15    /// Flags for drag widgets.
16    ///
17    /// Dear ImGui shares the underlying `ImGuiSliderFlags` enum between drag
18    /// and slider widgets, but `WRAP_AROUND` is supported by DragXXX only.
19    #[repr(transparent)]
20    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
21    pub struct DragFlags: i32 {
22        /// No flags.
23        const NONE = 0;
24        /// Wrap the value around when exceeding the current range.
25        const WRAP_AROUND = sys::ImGuiSliderFlags_WrapAround as i32;
26        /// Clamp on input when editing via CTRL+Click or direct text input.
27        const CLAMP_ON_INPUT = sys::ImGuiSliderFlags_ClampOnInput as i32;
28        /// Clamp zero-range drags to avoid a zero-sized range.
29        const CLAMP_ZERO_RANGE = sys::ImGuiSliderFlags_ClampZeroRange as i32;
30        /// Disable small "smart" speed tweaks for very small/large ranges.
31        const NO_SPEED_TWEAKS = sys::ImGuiSliderFlags_NoSpeedTweaks as i32;
32        /// Clamp value to min/max bounds when input manually with CTRL+Click.
33        const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
34        /// Make the widget logarithmic.
35        const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
36        /// Disable rounding underlying value to match the display format.
37        const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
38        /// Disable CTRL+Click or Enter key allowing direct text input.
39        const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
40    }
41}
42
43impl From<SliderFlags> for DragFlags {
44    fn from(flags: SliderFlags) -> Self {
45        Self::from_bits_retain(flags.bits())
46    }
47}
48
49impl Ui {
50    /// Creates a new drag slider widget. Returns true if the value has been edited.
51    pub fn drag<T: AsRef<str>, K: DataTypeKind>(&self, label: T, value: &mut K) -> bool {
52        Drag::new(label).build(self, value)
53    }
54
55    /// Creates a new unbuilt Drag.
56    pub fn drag_config<T: AsRef<str>, K: DataTypeKind>(&self, label: T) -> Drag<K, T> {
57        Drag::new(label)
58    }
59
60    /// Creates a drag float2 slider (2 floats)
61    #[doc(alias = "DragFloat2")]
62    pub fn drag_float2(&self, label: impl AsRef<str>, values: &mut [f32; 2]) -> bool {
63        unsafe {
64            let label_cstr = self.scratch_txt(label);
65            sys::igDragFloat2(
66                label_cstr,
67                values.as_mut_ptr(),
68                1.0,
69                0.0,
70                0.0,
71                ptr::null(),
72                0,
73            )
74        }
75    }
76
77    /// Creates a drag float3 slider (3 floats)
78    #[doc(alias = "DragFloat3")]
79    pub fn drag_float3(&self, label: impl AsRef<str>, values: &mut [f32; 3]) -> bool {
80        unsafe {
81            let label_cstr = self.scratch_txt(label);
82            sys::igDragFloat3(
83                label_cstr,
84                values.as_mut_ptr(),
85                1.0,
86                0.0,
87                0.0,
88                ptr::null(),
89                0,
90            )
91        }
92    }
93
94    /// Creates a drag float4 slider (4 floats)
95    #[doc(alias = "DragFloat4")]
96    pub fn drag_float4(&self, label: impl AsRef<str>, values: &mut [f32; 4]) -> bool {
97        unsafe {
98            let label_cstr = self.scratch_txt(label);
99            sys::igDragFloat4(
100                label_cstr,
101                values.as_mut_ptr(),
102                1.0,
103                0.0,
104                0.0,
105                ptr::null(),
106                0,
107            )
108        }
109    }
110
111    /// Creates a drag int2 slider (2 ints)
112    #[doc(alias = "DragInt2")]
113    pub fn drag_int2(&self, label: impl AsRef<str>, values: &mut [i32; 2]) -> bool {
114        unsafe {
115            let label_cstr = self.scratch_txt(label);
116            sys::igDragInt2(label_cstr, values.as_mut_ptr(), 1.0, 0, 0, ptr::null(), 0)
117        }
118    }
119
120    /// Creates a drag int3 slider (3 ints)
121    #[doc(alias = "DragInt3")]
122    pub fn drag_int3(&self, label: impl AsRef<str>, values: &mut [i32; 3]) -> bool {
123        unsafe {
124            let label_cstr = self.scratch_txt(label);
125            sys::igDragInt3(label_cstr, values.as_mut_ptr(), 1.0, 0, 0, ptr::null(), 0)
126        }
127    }
128
129    /// Creates a drag int4 slider (4 ints)
130    #[doc(alias = "DragInt4")]
131    pub fn drag_int4(&self, label: impl AsRef<str>, values: &mut [i32; 4]) -> bool {
132        unsafe {
133            let label_cstr = self.scratch_txt(label);
134            sys::igDragInt4(label_cstr, values.as_mut_ptr(), 1.0, 0, 0, ptr::null(), 0)
135        }
136    }
137}
138
139/// Builder for a drag slider widget
140#[derive(Clone, Debug)]
141#[must_use]
142pub struct Drag<T, L, F = &'static str> {
143    label: L,
144    speed: f32,
145    min: Option<T>,
146    max: Option<T>,
147    display_format: Option<F>,
148    flags: DragFlags,
149}
150
151impl<L: AsRef<str>, T: DataTypeKind> Drag<T, L> {
152    /// Constructs a new drag slider builder
153    #[doc(alias = "DragScalar", alias = "DragScalarN")]
154    pub fn new(label: L) -> Self {
155        Drag {
156            label,
157            speed: 1.0,
158            min: None,
159            max: None,
160            display_format: None,
161            flags: DragFlags::empty(),
162        }
163    }
164}
165
166impl<L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> Drag<T, L, F> {
167    /// Sets the range (inclusive)
168    pub fn range(mut self, min: T, max: T) -> Self {
169        self.min = Some(min);
170        self.max = Some(max);
171        self
172    }
173
174    /// Sets the value increment for a movement of one pixel
175    ///
176    /// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1
177    pub fn speed(mut self, speed: f32) -> Self {
178        self.speed = speed;
179        self
180    }
181
182    /// Sets the display format using *a C-style printf string*
183    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> Drag<T, L, F2> {
184        Drag {
185            label: self.label,
186            speed: self.speed,
187            min: self.min,
188            max: self.max,
189            display_format: Some(display_format),
190            flags: self.flags,
191        }
192    }
193
194    /// Replaces all current settings with the given flags
195    pub fn flags(mut self, flags: impl Into<DragFlags>) -> Self {
196        self.flags = flags.into();
197        self
198    }
199
200    /// Builds a drag slider that is bound to the given value
201    ///
202    /// Returns true if the slider value was changed
203    pub fn build(self, ui: &Ui, value: &mut T) -> bool {
204        unsafe {
205            let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
206
207            sys::igDragScalar(
208                one,
209                T::KIND as i32,
210                value as *mut T as *mut c_void,
211                self.speed,
212                self.min
213                    .as_ref()
214                    .map(|min| min as *const T)
215                    .unwrap_or(ptr::null()) as *const c_void,
216                self.max
217                    .as_ref()
218                    .map(|max| max as *const T)
219                    .unwrap_or(ptr::null()) as *const c_void,
220                two,
221                self.flags.bits(),
222            )
223        }
224    }
225
226    /// Builds a horizontal array of multiple drag sliders attached to the given slice
227    ///
228    /// Returns true if any slider value was changed
229    pub fn build_array(self, ui: &Ui, values: &mut [T]) -> bool {
230        let count = match i32::try_from(values.len()) {
231            Ok(n) => n,
232            Err(_) => return false,
233        };
234        unsafe {
235            let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
236
237            sys::igDragScalarN(
238                one,
239                T::KIND as i32,
240                values.as_mut_ptr() as *mut c_void,
241                count,
242                self.speed,
243                self.min
244                    .as_ref()
245                    .map(|min| min as *const T)
246                    .unwrap_or(ptr::null()) as *const c_void,
247                self.max
248                    .as_ref()
249                    .map(|max| max as *const T)
250                    .unwrap_or(ptr::null()) as *const c_void,
251                two,
252                self.flags.bits(),
253            )
254        }
255    }
256}
257
258/// Builder for a drag range slider widget
259#[derive(Clone, Debug)]
260#[must_use]
261pub struct DragRange<T, L, F = &'static str, M = &'static str> {
262    label: L,
263    speed: f32,
264    min: Option<T>,
265    max: Option<T>,
266    display_format: Option<F>,
267    max_display_format: Option<M>,
268    flags: DragFlags,
269}
270
271impl<T: DataTypeKind, L: AsRef<str>> DragRange<T, L> {
272    /// Constructs a new drag range slider builder
273    #[doc(alias = "DragIntRange2", alias = "DragFloatRange2")]
274    pub fn new(label: L) -> DragRange<T, L> {
275        DragRange {
276            label,
277            speed: 1.0,
278            min: None,
279            max: None,
280            display_format: None,
281            max_display_format: None,
282            flags: DragFlags::NONE,
283        }
284    }
285}
286
287impl<T, L, F, M> DragRange<T, L, F, M>
288where
289    T: DataTypeKind,
290    L: AsRef<str>,
291    F: AsRef<str>,
292    M: AsRef<str>,
293{
294    /// Sets the range (inclusive)
295    pub fn range(mut self, min: T, max: T) -> Self {
296        self.min = Some(min);
297        self.max = Some(max);
298        self
299    }
300
301    /// Sets the value increment for a movement of one pixel
302    ///
303    /// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1
304    pub fn speed(mut self, speed: f32) -> Self {
305        self.speed = speed;
306        self
307    }
308
309    /// Sets the display format using *a C-style printf string*
310    pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> DragRange<T, L, F2, M> {
311        DragRange {
312            label: self.label,
313            speed: self.speed,
314            min: self.min,
315            max: self.max,
316            display_format: Some(display_format),
317            max_display_format: self.max_display_format,
318            flags: self.flags,
319        }
320    }
321
322    /// Sets the display format for the max value using *a C-style printf string*
323    pub fn max_display_format<M2: AsRef<str>>(
324        self,
325        max_display_format: M2,
326    ) -> DragRange<T, L, F, M2> {
327        DragRange {
328            label: self.label,
329            speed: self.speed,
330            min: self.min,
331            max: self.max,
332            display_format: self.display_format,
333            max_display_format: Some(max_display_format),
334            flags: self.flags,
335        }
336    }
337
338    /// Replaces all current settings with the given flags
339    pub fn flags(mut self, flags: impl Into<DragFlags>) -> Self {
340        self.flags = flags.into();
341        self
342    }
343}
344
345impl<L, F, M> DragRange<f32, L, F, M>
346where
347    L: AsRef<str>,
348    F: AsRef<str>,
349    M: AsRef<str>,
350{
351    /// Builds a drag range slider that is bound to the given min/max values
352    ///
353    /// Returns true if the slider value was changed
354    #[doc(alias = "DragFloatRange2")]
355    pub fn build(self, ui: &Ui, min: &mut f32, max: &mut f32) -> bool {
356        unsafe {
357            let buffer = &mut *ui.scratch_buffer().get();
358            buffer.refresh_buffer();
359
360            let label_start = buffer.push(self.label);
361            let display_format = self.display_format.as_ref().map(|v| buffer.push(v));
362            let max_display_format = self.max_display_format.as_ref().map(|v| buffer.push(v));
363
364            let label = buffer.offset(label_start);
365            let display_format = display_format
366                .map(|v| buffer.offset(v))
367                .unwrap_or_else(std::ptr::null);
368            let max_display_format = max_display_format
369                .map(|v| buffer.offset(v))
370                .unwrap_or_else(std::ptr::null);
371
372            sys::igDragFloatRange2(
373                label,
374                min as *mut f32,
375                max as *mut f32,
376                self.speed,
377                self.min.unwrap_or(0.0),
378                self.max.unwrap_or(0.0),
379                display_format,
380                max_display_format,
381                self.flags.bits(),
382            )
383        }
384    }
385}
386
387impl<L, F, M> DragRange<i32, L, F, M>
388where
389    L: AsRef<str>,
390    F: AsRef<str>,
391    M: AsRef<str>,
392{
393    /// Builds a drag range slider that is bound to the given min/max values
394    ///
395    /// Returns true if the slider value was changed
396    #[doc(alias = "DragIntRange2")]
397    pub fn build(self, ui: &Ui, min: &mut i32, max: &mut i32) -> bool {
398        unsafe {
399            let buffer = &mut *ui.scratch_buffer().get();
400            buffer.refresh_buffer();
401
402            let label_start = buffer.push(self.label);
403            let display_format = self.display_format.as_ref().map(|v| buffer.push(v));
404            let max_display_format = self.max_display_format.as_ref().map(|v| buffer.push(v));
405
406            let label = buffer.offset(label_start);
407            let display_format = display_format
408                .map(|v| buffer.offset(v))
409                .unwrap_or_else(std::ptr::null);
410            let max_display_format = max_display_format
411                .map(|v| buffer.offset(v))
412                .unwrap_or_else(std::ptr::null);
413
414            sys::igDragIntRange2(
415                label,
416                min as *mut i32,
417                max as *mut i32,
418                self.speed,
419                self.min.unwrap_or(0),
420                self.max.unwrap_or(0),
421                display_format,
422                max_display_format,
423                self.flags.bits(),
424            )
425        }
426    }
427}