#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::as_conversions
)]
use crate::Ui;
use crate::internal::{DataType, DataTypeKind, component_count_i32};
use crate::sys;
use std::ffi::c_void;
fn validate_slider_flags(caller: &str, flags: SliderFlags) {
let bits = flags.bits();
assert!(
bits & (sys::ImGuiSliderFlags_WrapAround as i32) == 0,
"{caller} does not support ImGuiSliderFlags_WrapAround; use DragFlags::WRAP_AROUND with drag widgets"
);
let unsupported = bits & !SliderFlags::all().bits();
assert!(
unsupported == 0,
"{caller} received unsupported ImGuiSliderFlags bits: 0x{unsupported:X}"
);
}
fn validate_slider_range<Data: DataTypeKind>(caller: &str, min: &Data, max: &Data) {
match Data::KIND {
DataType::I8 | DataType::U8 | DataType::I16 | DataType::U16 => {}
DataType::I32 => {
let min = unsafe { *(min as *const Data as *const i32) };
let max = unsafe { *(max as *const Data as *const i32) };
let lower = i32::MIN / 2;
let upper = i32::MAX / 2;
assert!(
(lower..=upper).contains(&min) && (lower..=upper).contains(&max),
"{caller} i32/isize range endpoints must stay within i32::MIN/2..=i32::MAX/2"
);
}
DataType::U32 => {
let min = unsafe { *(min as *const Data as *const u32) };
let max = unsafe { *(max as *const Data as *const u32) };
let upper = u32::MAX / 2;
assert!(
min <= upper && max <= upper,
"{caller} u32/usize range endpoints must be <= u32::MAX/2"
);
}
DataType::I64 => {
let min = unsafe { *(min as *const Data as *const i64) };
let max = unsafe { *(max as *const Data as *const i64) };
let lower = i64::MIN / 2;
let upper = i64::MAX / 2;
assert!(
(lower..=upper).contains(&min) && (lower..=upper).contains(&max),
"{caller} i64/isize range endpoints must stay within i64::MIN/2..=i64::MAX/2"
);
}
DataType::U64 => {
let min = unsafe { *(min as *const Data as *const u64) };
let max = unsafe { *(max as *const Data as *const u64) };
let upper = u64::MAX / 2;
assert!(
min <= upper && max <= upper,
"{caller} u64/usize range endpoints must be <= u64::MAX/2"
);
}
DataType::F32 => {
let min = unsafe { *(min as *const Data as *const f32) };
let max = unsafe { *(max as *const Data as *const f32) };
assert!(
min.is_finite()
&& max.is_finite()
&& (-f32::MAX / 2.0..=f32::MAX / 2.0).contains(&min)
&& (-f32::MAX / 2.0..=f32::MAX / 2.0).contains(&max),
"{caller} f32 range endpoints must be finite and stay within -f32::MAX/2..=f32::MAX/2"
);
}
DataType::F64 => {
let min = unsafe { *(min as *const Data as *const f64) };
let max = unsafe { *(max as *const Data as *const f64) };
assert!(
min.is_finite()
&& max.is_finite()
&& (-f64::MAX / 2.0..=f64::MAX / 2.0).contains(&min)
&& (-f64::MAX / 2.0..=f64::MAX / 2.0).contains(&max),
"{caller} f64 range endpoints must be finite and stay within -f64::MAX/2..=f64::MAX/2"
);
}
}
}
fn validate_slider_preconditions<Data: DataTypeKind>(
caller: &str,
min: &Data,
max: &Data,
flags: SliderFlags,
) {
validate_slider_flags(caller, flags);
validate_slider_range(caller, min, max);
}
#[derive(Clone, Debug)]
#[must_use]
pub struct Slider<'ui, Label, Data, Format = &'static str> {
ui: &'ui Ui,
label: Label,
min: Data,
max: Data,
display_format: Option<Format>,
flags: SliderFlags,
}
impl<'ui, Label, Data> Slider<'ui, Label, Data>
where
Label: AsRef<str>,
Data: DataTypeKind,
{
#[doc(alias = "SliderScalar", alias = "SliderScalarN")]
#[deprecated(note = "Use `Ui::slider` or `Ui::slider_config`.", since = "0.1.0")]
pub fn new(ui: &'ui Ui, label: Label, min: Data, max: Data) -> Self {
Self {
ui,
label,
min,
max,
display_format: None,
flags: SliderFlags::NONE,
}
}
}
impl<'ui, Label, Data, Format> Slider<'ui, Label, Data, Format>
where
Label: AsRef<str>,
Data: DataTypeKind,
Format: AsRef<str>,
{
#[inline]
pub fn range(mut self, min: Data, max: Data) -> Self {
self.min = min;
self.max = max;
self
}
#[inline]
pub fn display_format<Format2: AsRef<str>>(
self,
display_format: Format2,
) -> Slider<'ui, Label, Data, Format2> {
Slider {
ui: self.ui,
label: self.label,
min: self.min,
max: self.max,
display_format: Some(display_format),
flags: self.flags,
}
}
#[inline]
pub fn flags(mut self, flags: SliderFlags) -> Self {
self.flags = flags;
self
}
pub fn build(self, value: &mut Data) -> bool {
validate_slider_preconditions("Slider::build()", &self.min, &self.max, self.flags);
unsafe {
let (label, display_format) = self
.ui
.scratch_txt_with_opt(self.label, self.display_format);
sys::igSliderScalar(
label,
Data::KIND as i32,
value as *mut Data as *mut c_void,
&self.min as *const Data as *const c_void,
&self.max as *const Data as *const c_void,
display_format,
self.flags.bits(),
)
}
}
pub fn build_array(self, values: &mut [Data]) -> bool {
validate_slider_preconditions("Slider::build_array()", &self.min, &self.max, self.flags);
let count = component_count_i32("Slider::build_array()", values.len());
if self.flags.contains(SliderFlags::COLOR_MARKERS) {
assert!(
count <= 4,
"Slider::build_array() supports at most 4 components with COLOR_MARKERS"
);
}
unsafe {
let (label, display_format) = self
.ui
.scratch_txt_with_opt(self.label, self.display_format);
sys::igSliderScalarN(
label,
Data::KIND as i32,
values.as_mut_ptr() as *mut c_void,
count,
&self.min as *const Data as *const c_void,
&self.max as *const Data as *const c_void,
display_format,
self.flags.bits(),
)
}
}
}
#[derive(Clone, Debug)]
#[must_use]
pub struct VerticalSlider<Label, Data, Format = &'static str> {
label: Label,
size: [f32; 2],
min: Data,
max: Data,
display_format: Option<Format>,
flags: SliderFlags,
}
impl<Label, Data> VerticalSlider<Label, Data>
where
Label: AsRef<str>,
Data: DataTypeKind,
{
#[doc(alias = "VSliderScalar")]
pub fn new(label: Label, size: impl Into<[f32; 2]>, min: Data, max: Data) -> Self {
VerticalSlider {
label,
size: size.into(),
min,
max,
display_format: None,
flags: SliderFlags::NONE,
}
}
}
impl<Label, Data, Format> VerticalSlider<Label, Data, Format>
where
Label: AsRef<str>,
Data: DataTypeKind,
Format: AsRef<str>,
{
#[inline]
pub fn range(mut self, min: Data, max: Data) -> Self {
self.min = min;
self.max = max;
self
}
#[inline]
pub fn display_format<Format2: AsRef<str>>(
self,
display_format: Format2,
) -> VerticalSlider<Label, Data, Format2> {
VerticalSlider {
label: self.label,
size: self.size,
min: self.min,
max: self.max,
display_format: Some(display_format),
flags: self.flags,
}
}
#[inline]
pub fn flags(mut self, flags: SliderFlags) -> Self {
self.flags = flags;
self
}
pub fn build(self, ui: &Ui, value: &mut Data) -> bool {
validate_slider_preconditions("VerticalSlider::build()", &self.min, &self.max, self.flags);
unsafe {
let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format);
let size = sys::ImVec2::new(self.size[0], self.size[1]);
sys::igVSliderScalar(
label,
size,
Data::KIND as i32,
value as *mut Data as *mut c_void,
&self.min as *const Data as *const c_void,
&self.max as *const Data as *const c_void,
display_format,
self.flags.bits(),
)
}
}
}
#[derive(Copy, Clone, Debug)]
#[must_use]
pub struct AngleSlider<Label, Format = &'static str> {
label: Label,
min_degrees: f32,
max_degrees: f32,
display_format: Format,
flags: SliderFlags,
}
impl<Label> AngleSlider<Label>
where
Label: AsRef<str>,
{
#[doc(alias = "SliderAngle")]
pub fn new(label: Label) -> Self {
AngleSlider {
label,
min_degrees: -360.0,
max_degrees: 360.0,
display_format: "%.0f deg",
flags: SliderFlags::NONE,
}
}
}
impl<Label, Format> AngleSlider<Label, Format>
where
Label: AsRef<str>,
Format: AsRef<str>,
{
#[inline]
pub fn range_degrees(mut self, min_degrees: f32, max_degrees: f32) -> Self {
self.min_degrees = min_degrees;
self.max_degrees = max_degrees;
self
}
#[inline]
pub fn min_degrees(mut self, min_degrees: f32) -> Self {
self.min_degrees = min_degrees;
self
}
#[inline]
pub fn max_degrees(mut self, max_degrees: f32) -> Self {
self.max_degrees = max_degrees;
self
}
#[inline]
pub fn display_format<Format2: AsRef<str>>(
self,
display_format: Format2,
) -> AngleSlider<Label, Format2> {
AngleSlider {
label: self.label,
min_degrees: self.min_degrees,
max_degrees: self.max_degrees,
display_format,
flags: self.flags,
}
}
#[inline]
pub fn flags(mut self, flags: SliderFlags) -> Self {
self.flags = flags;
self
}
pub fn build(self, ui: &Ui, value_rad: &mut f32) -> bool {
validate_slider_flags("AngleSlider::build()", self.flags);
validate_slider_range("AngleSlider::build()", &self.min_degrees, &self.max_degrees);
unsafe {
let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format);
sys::igSliderAngle(
label,
value_rad as *mut _,
self.min_degrees,
self.max_degrees,
display_format,
self.flags.bits(),
)
}
}
}
bitflags::bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SliderFlags: i32 {
const NONE = 0;
const CLAMP_ON_INPUT = sys::ImGuiSliderFlags_ClampOnInput as i32;
const CLAMP_ZERO_RANGE = sys::ImGuiSliderFlags_ClampZeroRange as i32;
const NO_SPEED_TWEAKS = sys::ImGuiSliderFlags_NoSpeedTweaks as i32;
const ALWAYS_CLAMP = sys::ImGuiSliderFlags_AlwaysClamp as i32;
const LOGARITHMIC = sys::ImGuiSliderFlags_Logarithmic as i32;
const NO_ROUND_TO_FORMAT = sys::ImGuiSliderFlags_NoRoundToFormat as i32;
const NO_INPUT = sys::ImGuiSliderFlags_NoInput as i32;
const COLOR_MARKERS = sys::ImGuiSliderFlags_ColorMarkers as i32;
}
}
impl Ui {
pub fn slider<T: AsRef<str>, K: DataTypeKind>(
&self,
label: T,
min: K,
max: K,
value: &mut K,
) -> bool {
self.slider_config(label, min, max).build(value)
}
pub fn slider_config<T: AsRef<str>, K: DataTypeKind>(
&self,
label: T,
min: K,
max: K,
) -> Slider<'_, T, K> {
Slider {
ui: self,
label,
min,
max,
display_format: Option::<&'static str>::None,
flags: SliderFlags::NONE,
}
}
#[doc(alias = "SliderFloat")]
pub fn slider_f32(&self, label: impl AsRef<str>, value: &mut f32, min: f32, max: f32) -> bool {
self.slider_config(label, min, max).build(value)
}
#[doc(alias = "SliderInt")]
pub fn slider_i32(&self, label: impl AsRef<str>, value: &mut i32, min: i32, max: i32) -> bool {
self.slider_config(label, min, max).build(value)
}
#[doc(alias = "SliderFloat2")]
pub fn slider_float2(
&self,
label: impl AsRef<str>,
value: &mut [f32; 2],
min: f32,
max: f32,
) -> bool {
self.slider_config(label, min, max)
.build_array(value.as_mut_slice())
}
#[doc(alias = "SliderFloat3")]
pub fn slider_float3(
&self,
label: impl AsRef<str>,
value: &mut [f32; 3],
min: f32,
max: f32,
) -> bool {
self.slider_config(label, min, max)
.build_array(value.as_mut_slice())
}
#[doc(alias = "SliderFloat4")]
pub fn slider_float4(
&self,
label: impl AsRef<str>,
value: &mut [f32; 4],
min: f32,
max: f32,
) -> bool {
self.slider_config(label, min, max)
.build_array(value.as_mut_slice())
}
#[doc(alias = "SliderInt2")]
pub fn slider_int2(
&self,
label: impl AsRef<str>,
value: &mut [i32; 2],
min: i32,
max: i32,
) -> bool {
self.slider_config(label, min, max)
.build_array(value.as_mut_slice())
}
#[doc(alias = "SliderInt3")]
pub fn slider_int3(
&self,
label: impl AsRef<str>,
value: &mut [i32; 3],
min: i32,
max: i32,
) -> bool {
self.slider_config(label, min, max)
.build_array(value.as_mut_slice())
}
#[doc(alias = "SliderInt4")]
pub fn slider_int4(
&self,
label: impl AsRef<str>,
value: &mut [i32; 4],
min: i32,
max: i32,
) -> bool {
self.slider_config(label, min, max)
.build_array(value.as_mut_slice())
}
#[doc(alias = "VSliderFloat")]
pub fn v_slider_f32(
&self,
label: impl AsRef<str>,
size: impl Into<[f32; 2]>,
value: &mut f32,
min: f32,
max: f32,
) -> bool {
VerticalSlider::new(label, size, min, max).build(self, value)
}
#[doc(alias = "VSliderInt")]
pub fn v_slider_i32(
&self,
label: impl AsRef<str>,
size: impl Into<[f32; 2]>,
value: &mut i32,
min: i32,
max: i32,
) -> bool {
VerticalSlider::new(label, size, min, max).build(self, value)
}
#[doc(alias = "SliderAngle")]
pub fn slider_angle(&self, label: impl AsRef<str>, value_rad: &mut f32) -> bool {
AngleSlider::new(label).build(self, value_rad)
}
}