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