1use std::os::raw::c_void;
7use std::ptr;
8
9use crate::Ui;
10use crate::internal::{DataTypeKind, component_count_i32};
11use crate::sys;
12use crate::widget::slider::SliderFlags;
13
14fn validate_drag_flags(caller: &str, flags: DragFlags) {
15 let unsupported = flags.bits() & !DragFlags::all().bits();
16 assert!(
17 unsupported == 0,
18 "{caller} received unsupported ImGuiSliderFlags bits: 0x{unsupported:X}"
19 );
20}
21
22bitflags::bitflags! {
23 #[repr(transparent)]
28 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29 pub struct DragFlags: i32 {
30 const NONE = 0;
32 const WRAP_AROUND = sys::ImGuiSliderFlags_WrapAround as i32;
34 const CLAMP_ON_INPUT = sys::ImGuiSliderFlags_ClampOnInput as i32;
36 const CLAMP_ZERO_RANGE = sys::ImGuiSliderFlags_ClampZeroRange as i32;
38 const NO_SPEED_TWEAKS = sys::ImGuiSliderFlags_NoSpeedTweaks as i32;
40 const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
42 const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
44 const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
46 const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
48 const COLOR_MARKERS = sys::ImGuiSliderFlags_ColorMarkers as i32;
52 }
53}
54
55impl From<SliderFlags> for DragFlags {
56 fn from(flags: SliderFlags) -> Self {
57 Self::from_bits_retain(flags.bits())
58 }
59}
60
61impl Ui {
62 pub fn drag<T: AsRef<str>, K: DataTypeKind>(&self, label: T, value: &mut K) -> bool {
64 Drag::new(label).build(self, value)
65 }
66
67 pub fn drag_config<T: AsRef<str>, K: DataTypeKind>(&self, label: T) -> Drag<K, T> {
69 Drag::new(label)
70 }
71
72 #[doc(alias = "DragFloat2")]
74 pub fn drag_float2(&self, label: impl AsRef<str>, values: &mut [f32; 2]) -> bool {
75 unsafe {
76 let label_cstr = self.scratch_txt(label);
77 sys::igDragFloat2(
78 label_cstr,
79 values.as_mut_ptr(),
80 1.0,
81 0.0,
82 0.0,
83 ptr::null(),
84 0,
85 )
86 }
87 }
88
89 #[doc(alias = "DragFloat3")]
91 pub fn drag_float3(&self, label: impl AsRef<str>, values: &mut [f32; 3]) -> bool {
92 unsafe {
93 let label_cstr = self.scratch_txt(label);
94 sys::igDragFloat3(
95 label_cstr,
96 values.as_mut_ptr(),
97 1.0,
98 0.0,
99 0.0,
100 ptr::null(),
101 0,
102 )
103 }
104 }
105
106 #[doc(alias = "DragFloat4")]
108 pub fn drag_float4(&self, label: impl AsRef<str>, values: &mut [f32; 4]) -> bool {
109 unsafe {
110 let label_cstr = self.scratch_txt(label);
111 sys::igDragFloat4(
112 label_cstr,
113 values.as_mut_ptr(),
114 1.0,
115 0.0,
116 0.0,
117 ptr::null(),
118 0,
119 )
120 }
121 }
122
123 #[doc(alias = "DragInt2")]
125 pub fn drag_int2(&self, label: impl AsRef<str>, values: &mut [i32; 2]) -> bool {
126 unsafe {
127 let label_cstr = self.scratch_txt(label);
128 sys::igDragInt2(label_cstr, values.as_mut_ptr(), 1.0, 0, 0, ptr::null(), 0)
129 }
130 }
131
132 #[doc(alias = "DragInt3")]
134 pub fn drag_int3(&self, label: impl AsRef<str>, values: &mut [i32; 3]) -> bool {
135 unsafe {
136 let label_cstr = self.scratch_txt(label);
137 sys::igDragInt3(label_cstr, values.as_mut_ptr(), 1.0, 0, 0, ptr::null(), 0)
138 }
139 }
140
141 #[doc(alias = "DragInt4")]
143 pub fn drag_int4(&self, label: impl AsRef<str>, values: &mut [i32; 4]) -> bool {
144 unsafe {
145 let label_cstr = self.scratch_txt(label);
146 sys::igDragInt4(label_cstr, values.as_mut_ptr(), 1.0, 0, 0, ptr::null(), 0)
147 }
148 }
149}
150
151#[derive(Clone, Debug)]
153#[must_use]
154pub struct Drag<T, L, F = &'static str> {
155 label: L,
156 speed: f32,
157 min: Option<T>,
158 max: Option<T>,
159 display_format: Option<F>,
160 flags: DragFlags,
161}
162
163impl<L: AsRef<str>, T: DataTypeKind> Drag<T, L> {
164 #[doc(alias = "DragScalar", alias = "DragScalarN")]
166 pub fn new(label: L) -> Self {
167 Drag {
168 label,
169 speed: 1.0,
170 min: None,
171 max: None,
172 display_format: None,
173 flags: DragFlags::empty(),
174 }
175 }
176}
177
178impl<L: AsRef<str>, T: DataTypeKind, F: AsRef<str>> Drag<T, L, F> {
179 pub fn range(mut self, min: T, max: T) -> Self {
181 self.min = Some(min);
182 self.max = Some(max);
183 self
184 }
185
186 pub fn speed(mut self, speed: f32) -> Self {
190 self.speed = speed;
191 self
192 }
193
194 pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> Drag<T, L, F2> {
196 Drag {
197 label: self.label,
198 speed: self.speed,
199 min: self.min,
200 max: self.max,
201 display_format: Some(display_format),
202 flags: self.flags,
203 }
204 }
205
206 pub fn flags(mut self, flags: impl Into<DragFlags>) -> Self {
208 self.flags = flags.into();
209 self
210 }
211
212 pub fn build(self, ui: &Ui, value: &mut T) -> bool {
216 validate_drag_flags("Drag::build()", self.flags);
217 unsafe {
218 let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
219
220 sys::igDragScalar(
221 one,
222 T::KIND as i32,
223 value as *mut T as *mut c_void,
224 self.speed,
225 self.min
226 .as_ref()
227 .map(|min| min as *const T)
228 .unwrap_or(ptr::null()) as *const c_void,
229 self.max
230 .as_ref()
231 .map(|max| max as *const T)
232 .unwrap_or(ptr::null()) as *const c_void,
233 two,
234 self.flags.bits(),
235 )
236 }
237 }
238
239 pub fn build_array(self, ui: &Ui, values: &mut [T]) -> bool {
243 validate_drag_flags("Drag::build_array()", self.flags);
244 let count = component_count_i32("Drag::build_array()", values.len());
245 if self.flags.contains(DragFlags::COLOR_MARKERS) {
246 assert!(
247 count <= 4,
248 "Drag::build_array() supports at most 4 components with COLOR_MARKERS"
249 );
250 }
251 unsafe {
252 let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format);
253
254 sys::igDragScalarN(
255 one,
256 T::KIND as i32,
257 values.as_mut_ptr() as *mut c_void,
258 count,
259 self.speed,
260 self.min
261 .as_ref()
262 .map(|min| min as *const T)
263 .unwrap_or(ptr::null()) as *const c_void,
264 self.max
265 .as_ref()
266 .map(|max| max as *const T)
267 .unwrap_or(ptr::null()) as *const c_void,
268 two,
269 self.flags.bits(),
270 )
271 }
272 }
273}
274
275#[derive(Clone, Debug)]
277#[must_use]
278pub struct DragRange<T, L, F = &'static str, M = &'static str> {
279 label: L,
280 speed: f32,
281 min: Option<T>,
282 max: Option<T>,
283 display_format: Option<F>,
284 max_display_format: Option<M>,
285 flags: DragFlags,
286}
287
288impl<T: DataTypeKind, L: AsRef<str>> DragRange<T, L> {
289 #[doc(alias = "DragIntRange2", alias = "DragFloatRange2")]
291 pub fn new(label: L) -> DragRange<T, L> {
292 DragRange {
293 label,
294 speed: 1.0,
295 min: None,
296 max: None,
297 display_format: None,
298 max_display_format: None,
299 flags: DragFlags::NONE,
300 }
301 }
302}
303
304impl<T, L, F, M> DragRange<T, L, F, M>
305where
306 T: DataTypeKind,
307 L: AsRef<str>,
308 F: AsRef<str>,
309 M: AsRef<str>,
310{
311 pub fn range(mut self, min: T, max: T) -> Self {
313 self.min = Some(min);
314 self.max = Some(max);
315 self
316 }
317
318 pub fn speed(mut self, speed: f32) -> Self {
322 self.speed = speed;
323 self
324 }
325
326 pub fn display_format<F2: AsRef<str>>(self, display_format: F2) -> DragRange<T, L, F2, M> {
328 DragRange {
329 label: self.label,
330 speed: self.speed,
331 min: self.min,
332 max: self.max,
333 display_format: Some(display_format),
334 max_display_format: self.max_display_format,
335 flags: self.flags,
336 }
337 }
338
339 pub fn max_display_format<M2: AsRef<str>>(
341 self,
342 max_display_format: M2,
343 ) -> DragRange<T, L, F, M2> {
344 DragRange {
345 label: self.label,
346 speed: self.speed,
347 min: self.min,
348 max: self.max,
349 display_format: self.display_format,
350 max_display_format: Some(max_display_format),
351 flags: self.flags,
352 }
353 }
354
355 pub fn flags(mut self, flags: impl Into<DragFlags>) -> Self {
357 self.flags = flags.into();
358 self
359 }
360}
361
362impl<L, F, M> DragRange<f32, L, F, M>
363where
364 L: AsRef<str>,
365 F: AsRef<str>,
366 M: AsRef<str>,
367{
368 #[doc(alias = "DragFloatRange2")]
372 pub fn build(self, ui: &Ui, min: &mut f32, max: &mut f32) -> bool {
373 validate_drag_flags("DragRange::build()", self.flags);
374 unsafe {
375 let buffer = &mut *ui.scratch_buffer().get();
376 buffer.refresh_buffer();
377
378 let label_start = buffer.push(self.label);
379 let display_format = self.display_format.as_ref().map(|v| buffer.push(v));
380 let max_display_format = self.max_display_format.as_ref().map(|v| buffer.push(v));
381
382 let label = buffer.offset(label_start);
383 let display_format = display_format
384 .map(|v| buffer.offset(v))
385 .unwrap_or_else(std::ptr::null);
386 let max_display_format = max_display_format
387 .map(|v| buffer.offset(v))
388 .unwrap_or_else(std::ptr::null);
389
390 sys::igDragFloatRange2(
391 label,
392 min as *mut f32,
393 max as *mut f32,
394 self.speed,
395 self.min.unwrap_or(0.0),
396 self.max.unwrap_or(0.0),
397 display_format,
398 max_display_format,
399 self.flags.bits(),
400 )
401 }
402 }
403}
404
405impl<L, F, M> DragRange<i32, L, F, M>
406where
407 L: AsRef<str>,
408 F: AsRef<str>,
409 M: AsRef<str>,
410{
411 #[doc(alias = "DragIntRange2")]
415 pub fn build(self, ui: &Ui, min: &mut i32, max: &mut i32) -> bool {
416 validate_drag_flags("DragRange::build()", self.flags);
417 unsafe {
418 let buffer = &mut *ui.scratch_buffer().get();
419 buffer.refresh_buffer();
420
421 let label_start = buffer.push(self.label);
422 let display_format = self.display_format.as_ref().map(|v| buffer.push(v));
423 let max_display_format = self.max_display_format.as_ref().map(|v| buffer.push(v));
424
425 let label = buffer.offset(label_start);
426 let display_format = display_format
427 .map(|v| buffer.offset(v))
428 .unwrap_or_else(std::ptr::null);
429 let max_display_format = max_display_format
430 .map(|v| buffer.offset(v))
431 .unwrap_or_else(std::ptr::null);
432
433 sys::igDragIntRange2(
434 label,
435 min as *mut i32,
436 max as *mut i32,
437 self.speed,
438 self.min.unwrap_or(0),
439 self.max.unwrap_or(0),
440 display_format,
441 max_display_format,
442 self.flags.bits(),
443 )
444 }
445 }
446}