1#![allow(
7 clippy::cast_possible_truncation,
8 clippy::cast_sign_loss,
9 clippy::as_conversions
10)]
11use crate::Ui;
12use crate::internal::{DataType, DataTypeKind, component_count_i32};
13use crate::sys;
14use std::ffi::c_void;
15
16fn validate_slider_flags(caller: &str, flags: SliderFlags) {
17 let bits = flags.bits();
18 assert!(
19 bits & (sys::ImGuiSliderFlags_WrapAround as i32) == 0,
20 "{caller} does not support ImGuiSliderFlags_WrapAround; use DragFlags::WRAP_AROUND with drag widgets"
21 );
22 let unsupported = bits & !SliderFlags::all().bits();
23 assert!(
24 unsupported == 0,
25 "{caller} received unsupported ImGuiSliderFlags bits: 0x{unsupported:X}"
26 );
27}
28
29fn validate_slider_range<Data: DataTypeKind>(caller: &str, min: &Data, max: &Data) {
30 match Data::KIND {
31 DataType::I8 | DataType::U8 | DataType::I16 | DataType::U16 => {}
32 DataType::I32 => {
33 let min = unsafe { *(min as *const Data as *const i32) };
34 let max = unsafe { *(max as *const Data as *const i32) };
35 let lower = i32::MIN / 2;
36 let upper = i32::MAX / 2;
37 assert!(
38 (lower..=upper).contains(&min) && (lower..=upper).contains(&max),
39 "{caller} i32/isize range endpoints must stay within i32::MIN/2..=i32::MAX/2"
40 );
41 }
42 DataType::U32 => {
43 let min = unsafe { *(min as *const Data as *const u32) };
44 let max = unsafe { *(max as *const Data as *const u32) };
45 let upper = u32::MAX / 2;
46 assert!(
47 min <= upper && max <= upper,
48 "{caller} u32/usize range endpoints must be <= u32::MAX/2"
49 );
50 }
51 DataType::I64 => {
52 let min = unsafe { *(min as *const Data as *const i64) };
53 let max = unsafe { *(max as *const Data as *const i64) };
54 let lower = i64::MIN / 2;
55 let upper = i64::MAX / 2;
56 assert!(
57 (lower..=upper).contains(&min) && (lower..=upper).contains(&max),
58 "{caller} i64/isize range endpoints must stay within i64::MIN/2..=i64::MAX/2"
59 );
60 }
61 DataType::U64 => {
62 let min = unsafe { *(min as *const Data as *const u64) };
63 let max = unsafe { *(max as *const Data as *const u64) };
64 let upper = u64::MAX / 2;
65 assert!(
66 min <= upper && max <= upper,
67 "{caller} u64/usize range endpoints must be <= u64::MAX/2"
68 );
69 }
70 DataType::F32 => {
71 let min = unsafe { *(min as *const Data as *const f32) };
72 let max = unsafe { *(max as *const Data as *const f32) };
73 assert!(
74 min.is_finite()
75 && max.is_finite()
76 && (-f32::MAX / 2.0..=f32::MAX / 2.0).contains(&min)
77 && (-f32::MAX / 2.0..=f32::MAX / 2.0).contains(&max),
78 "{caller} f32 range endpoints must be finite and stay within -f32::MAX/2..=f32::MAX/2"
79 );
80 }
81 DataType::F64 => {
82 let min = unsafe { *(min as *const Data as *const f64) };
83 let max = unsafe { *(max as *const Data as *const f64) };
84 assert!(
85 min.is_finite()
86 && max.is_finite()
87 && (-f64::MAX / 2.0..=f64::MAX / 2.0).contains(&min)
88 && (-f64::MAX / 2.0..=f64::MAX / 2.0).contains(&max),
89 "{caller} f64 range endpoints must be finite and stay within -f64::MAX/2..=f64::MAX/2"
90 );
91 }
92 }
93}
94
95fn validate_slider_preconditions<Data: DataTypeKind>(
96 caller: &str,
97 min: &Data,
98 max: &Data,
99 flags: SliderFlags,
100) {
101 validate_slider_flags(caller, flags);
102 validate_slider_range(caller, min, max);
103}
104
105#[derive(Clone, Debug)]
107#[must_use]
108pub struct Slider<'ui, Label, Data, Format = &'static str> {
109 ui: &'ui Ui,
110 label: Label,
111 min: Data,
112 max: Data,
113 display_format: Option<Format>,
114 flags: SliderFlags,
115}
116
117impl<'ui, Label, Data> Slider<'ui, Label, Data>
118where
119 Label: AsRef<str>,
120 Data: DataTypeKind,
121{
122 #[doc(alias = "SliderScalar", alias = "SliderScalarN")]
124 #[deprecated(note = "Use `Ui::slider` or `Ui::slider_config`.", since = "0.1.0")]
125 pub fn new(ui: &'ui Ui, label: Label, min: Data, max: Data) -> Self {
126 Self {
127 ui,
128 label,
129 min,
130 max,
131 display_format: None,
132 flags: SliderFlags::NONE,
133 }
134 }
135}
136
137impl<'ui, Label, Data, Format> Slider<'ui, Label, Data, Format>
138where
139 Label: AsRef<str>,
140 Data: DataTypeKind,
141 Format: AsRef<str>,
142{
143 #[inline]
165 pub fn range(mut self, min: Data, max: Data) -> Self {
166 self.min = min;
167 self.max = max;
168 self
169 }
170
171 #[inline]
173 pub fn display_format<Format2: AsRef<str>>(
174 self,
175 display_format: Format2,
176 ) -> Slider<'ui, Label, Data, Format2> {
177 Slider {
178 ui: self.ui,
179 label: self.label,
180 min: self.min,
181 max: self.max,
182 display_format: Some(display_format),
183 flags: self.flags,
184 }
185 }
186
187 #[inline]
189 pub fn flags(mut self, flags: SliderFlags) -> Self {
190 self.flags = flags;
191 self
192 }
193
194 pub fn build(self, value: &mut Data) -> bool {
198 validate_slider_preconditions("Slider::build()", &self.min, &self.max, self.flags);
199 unsafe {
200 let (label, display_format) = self
201 .ui
202 .scratch_txt_with_opt(self.label, self.display_format);
203
204 sys::igSliderScalar(
205 label,
206 Data::KIND as i32,
207 value as *mut Data as *mut c_void,
208 &self.min as *const Data as *const c_void,
209 &self.max as *const Data as *const c_void,
210 display_format,
211 self.flags.bits(),
212 )
213 }
214 }
215
216 pub fn build_array(self, values: &mut [Data]) -> bool {
220 validate_slider_preconditions("Slider::build_array()", &self.min, &self.max, self.flags);
221 let count = component_count_i32("Slider::build_array()", values.len());
222 if self.flags.contains(SliderFlags::COLOR_MARKERS) {
223 assert!(
224 count <= 4,
225 "Slider::build_array() supports at most 4 components with COLOR_MARKERS"
226 );
227 }
228 unsafe {
229 let (label, display_format) = self
230 .ui
231 .scratch_txt_with_opt(self.label, self.display_format);
232
233 sys::igSliderScalarN(
234 label,
235 Data::KIND as i32,
236 values.as_mut_ptr() as *mut c_void,
237 count,
238 &self.min as *const Data as *const c_void,
239 &self.max as *const Data as *const c_void,
240 display_format,
241 self.flags.bits(),
242 )
243 }
244 }
245}
246
247#[derive(Clone, Debug)]
249#[must_use]
250pub struct VerticalSlider<Label, Data, Format = &'static str> {
251 label: Label,
252 size: [f32; 2],
253 min: Data,
254 max: Data,
255 display_format: Option<Format>,
256 flags: SliderFlags,
257}
258
259impl<Label, Data> VerticalSlider<Label, Data>
260where
261 Label: AsRef<str>,
262 Data: DataTypeKind,
263{
264 #[doc(alias = "VSliderScalar")]
279 pub fn new(label: Label, size: impl Into<[f32; 2]>, min: Data, max: Data) -> Self {
280 VerticalSlider {
281 label,
282 size: size.into(),
283 min,
284 max,
285 display_format: None,
286 flags: SliderFlags::NONE,
287 }
288 }
289}
290
291impl<Label, Data, Format> VerticalSlider<Label, Data, Format>
292where
293 Label: AsRef<str>,
294 Data: DataTypeKind,
295 Format: AsRef<str>,
296{
297 #[inline]
312 pub fn range(mut self, min: Data, max: Data) -> Self {
313 self.min = min;
314 self.max = max;
315 self
316 }
317
318 #[inline]
320 pub fn display_format<Format2: AsRef<str>>(
321 self,
322 display_format: Format2,
323 ) -> VerticalSlider<Label, Data, Format2> {
324 VerticalSlider {
325 label: self.label,
326 size: self.size,
327 min: self.min,
328 max: self.max,
329 display_format: Some(display_format),
330 flags: self.flags,
331 }
332 }
333
334 #[inline]
336 pub fn flags(mut self, flags: SliderFlags) -> Self {
337 self.flags = flags;
338 self
339 }
340
341 pub fn build(self, ui: &Ui, value: &mut Data) -> bool {
345 validate_slider_preconditions("VerticalSlider::build()", &self.min, &self.max, self.flags);
346 unsafe {
347 let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
348 let size = sys::ImVec2::new(self.size[0], self.size[1]);
349
350 sys::igVSliderScalar(
351 label,
352 size,
353 Data::KIND as i32,
354 value as *mut Data as *mut c_void,
355 &self.min as *const Data as *const c_void,
356 &self.max as *const Data as *const c_void,
357 display_format,
358 self.flags.bits(),
359 )
360 }
361 }
362}
363
364#[derive(Copy, Clone, Debug)]
366#[must_use]
367pub struct AngleSlider<Label, Format = &'static str> {
368 label: Label,
369 min_degrees: f32,
370 max_degrees: f32,
371 display_format: Format,
372 flags: SliderFlags,
373}
374
375impl<Label> AngleSlider<Label>
376where
377 Label: AsRef<str>,
378{
379 #[doc(alias = "SliderAngle")]
382 pub fn new(label: Label) -> Self {
383 AngleSlider {
384 label,
385 min_degrees: -360.0,
386 max_degrees: 360.0,
387 display_format: "%.0f deg",
388 flags: SliderFlags::NONE,
389 }
390 }
391}
392
393impl<Label, Format> AngleSlider<Label, Format>
394where
395 Label: AsRef<str>,
396 Format: AsRef<str>,
397{
398 #[inline]
412 pub fn range_degrees(mut self, min_degrees: f32, max_degrees: f32) -> Self {
413 self.min_degrees = min_degrees;
414 self.max_degrees = max_degrees;
415 self
416 }
417
418 #[inline]
420 pub fn min_degrees(mut self, min_degrees: f32) -> Self {
421 self.min_degrees = min_degrees;
422 self
423 }
424
425 #[inline]
427 pub fn max_degrees(mut self, max_degrees: f32) -> Self {
428 self.max_degrees = max_degrees;
429 self
430 }
431
432 #[inline]
434 pub fn display_format<Format2: AsRef<str>>(
435 self,
436 display_format: Format2,
437 ) -> AngleSlider<Label, Format2> {
438 AngleSlider {
439 label: self.label,
440 min_degrees: self.min_degrees,
441 max_degrees: self.max_degrees,
442 display_format,
443 flags: self.flags,
444 }
445 }
446
447 #[inline]
449 pub fn flags(mut self, flags: SliderFlags) -> Self {
450 self.flags = flags;
451 self
452 }
453
454 pub fn build(self, ui: &Ui, value_rad: &mut f32) -> bool {
458 validate_slider_flags("AngleSlider::build()", self.flags);
459 validate_slider_range("AngleSlider::build()", &self.min_degrees, &self.max_degrees);
460 unsafe {
461 let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format);
462
463 sys::igSliderAngle(
464 label,
465 value_rad as *mut _,
466 self.min_degrees,
467 self.max_degrees,
468 display_format,
469 self.flags.bits(),
470 )
471 }
472 }
473}
474
475bitflags::bitflags! {
476 #[repr(transparent)]
478 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
479 pub struct SliderFlags: i32 {
480 const NONE = 0;
482 const CLAMP_ON_INPUT = sys::ImGuiSliderFlags_ClampOnInput as i32;
486 const CLAMP_ZERO_RANGE = sys::ImGuiSliderFlags_ClampZeroRange as i32;
490 const NO_SPEED_TWEAKS = sys::ImGuiSliderFlags_NoSpeedTweaks as i32;
494 const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
496 const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
498 const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
500 const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
502 const COLOR_MARKERS = sys::ImGuiSliderFlags_ColorMarkers as i32;
506 }
507}
508
509impl Ui {
510 pub fn slider<T: AsRef<str>, K: DataTypeKind>(
512 &self,
513 label: T,
514 min: K,
515 max: K,
516 value: &mut K,
517 ) -> bool {
518 self.slider_config(label, min, max).build(value)
519 }
520
521 pub fn slider_config<T: AsRef<str>, K: DataTypeKind>(
523 &self,
524 label: T,
525 min: K,
526 max: K,
527 ) -> Slider<'_, T, K> {
528 Slider {
529 ui: self,
530 label,
531 min,
532 max,
533 display_format: Option::<&'static str>::None,
534 flags: SliderFlags::NONE,
535 }
536 }
537
538 #[doc(alias = "SliderFloat")]
540 pub fn slider_f32(&self, label: impl AsRef<str>, value: &mut f32, min: f32, max: f32) -> bool {
541 self.slider_config(label, min, max).build(value)
542 }
543
544 #[doc(alias = "SliderInt")]
546 pub fn slider_i32(&self, label: impl AsRef<str>, value: &mut i32, min: i32, max: i32) -> bool {
547 self.slider_config(label, min, max).build(value)
548 }
549
550 #[doc(alias = "SliderFloat2")]
552 pub fn slider_float2(
553 &self,
554 label: impl AsRef<str>,
555 value: &mut [f32; 2],
556 min: f32,
557 max: f32,
558 ) -> bool {
559 self.slider_config(label, min, max)
560 .build_array(value.as_mut_slice())
561 }
562
563 #[doc(alias = "SliderFloat3")]
565 pub fn slider_float3(
566 &self,
567 label: impl AsRef<str>,
568 value: &mut [f32; 3],
569 min: f32,
570 max: f32,
571 ) -> bool {
572 self.slider_config(label, min, max)
573 .build_array(value.as_mut_slice())
574 }
575
576 #[doc(alias = "SliderFloat4")]
578 pub fn slider_float4(
579 &self,
580 label: impl AsRef<str>,
581 value: &mut [f32; 4],
582 min: f32,
583 max: f32,
584 ) -> bool {
585 self.slider_config(label, min, max)
586 .build_array(value.as_mut_slice())
587 }
588
589 #[doc(alias = "SliderInt2")]
591 pub fn slider_int2(
592 &self,
593 label: impl AsRef<str>,
594 value: &mut [i32; 2],
595 min: i32,
596 max: i32,
597 ) -> bool {
598 self.slider_config(label, min, max)
599 .build_array(value.as_mut_slice())
600 }
601
602 #[doc(alias = "SliderInt3")]
604 pub fn slider_int3(
605 &self,
606 label: impl AsRef<str>,
607 value: &mut [i32; 3],
608 min: i32,
609 max: i32,
610 ) -> bool {
611 self.slider_config(label, min, max)
612 .build_array(value.as_mut_slice())
613 }
614
615 #[doc(alias = "SliderInt4")]
617 pub fn slider_int4(
618 &self,
619 label: impl AsRef<str>,
620 value: &mut [i32; 4],
621 min: i32,
622 max: i32,
623 ) -> bool {
624 self.slider_config(label, min, max)
625 .build_array(value.as_mut_slice())
626 }
627
628 #[doc(alias = "VSliderFloat")]
630 pub fn v_slider_f32(
631 &self,
632 label: impl AsRef<str>,
633 size: impl Into<[f32; 2]>,
634 value: &mut f32,
635 min: f32,
636 max: f32,
637 ) -> bool {
638 VerticalSlider::new(label, size, min, max).build(self, value)
639 }
640
641 #[doc(alias = "VSliderInt")]
643 pub fn v_slider_i32(
644 &self,
645 label: impl AsRef<str>,
646 size: impl Into<[f32; 2]>,
647 value: &mut i32,
648 min: i32,
649 max: i32,
650 ) -> bool {
651 VerticalSlider::new(label, size, min, max).build(self, value)
652 }
653
654 #[doc(alias = "SliderAngle")]
656 pub fn slider_angle(&self, label: impl AsRef<str>, value_rad: &mut f32) -> bool {
657 AngleSlider::new(label).build(self, value_rad)
658 }
659}