diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 8ca8cd6..e99d656 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -11,6 +11,7 @@ of closures - API for accessing the background drawlist - Tab bar / tab item API +- Redesigned drag slider API ### Changed diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index cb964b6..4f21ad2 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -493,16 +493,12 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.input_text(im_str!("input text"), &mut state.text) .build(); ui.input_int(im_str!("input int"), &mut state.i0).build(); - ui.drag_int(im_str!("drag int"), &mut state.i0).build(); + Drag::new(im_str!("drag int")).build(ui, &mut state.i0); ui.input_float(im_str!("input float"), &mut state.f0) .step(0.01) .step_fast(1.0) .build(); - ui.drag_float(im_str!("drag float"), &mut state.f0) - .speed(0.001) - .min(-1.0) - .max(1.0) - .build(); + Drag::new(im_str!("drag float")).range(-1.0..=1.0).speed(0.001).build(ui, &mut state.f0); ui.input_float3(im_str!("input float3"), &mut state.vec3f) .build(); ColorEdit::new(im_str!("color 1"), &mut state.col1).build(ui); diff --git a/src/drag.rs b/src/drag.rs deleted file mode 100644 index e8483d1..0000000 --- a/src/drag.rs +++ /dev/null @@ -1,348 +0,0 @@ -use std::marker::PhantomData; -use std::ptr; - -use super::{ImStr, Ui}; - -macro_rules! impl_display_format { - ($InputType:ident) => { - #[inline] - pub fn display_format(mut self, display_format: &'p ImStr) -> Self { - self.display_format = display_format; - self - } - }; -} - -macro_rules! impl_speed { - ($InputType:ident) => { - #[inline] - pub fn speed(mut self, value: f32) -> Self { - self.speed = value; - self - } - }; -} - -macro_rules! impl_min_max { - ($InputType:ident, $Value:ty) => { - #[inline] - pub fn min(mut self, value: $Value) -> Self { - self.min = value; - self - } - - #[inline] - pub fn max(mut self, value: $Value) -> Self { - self.max = value; - self - } - }; -} - -#[must_use] -pub struct DragFloat<'ui, 'p> { - label: &'p ImStr, - value: &'p mut f32, - speed: f32, - min: f32, - max: f32, - display_format: &'p ImStr, - _phantom: PhantomData<&'ui Ui<'ui>>, -} - -impl<'ui, 'p> DragFloat<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut f32) -> Self { - DragFloat { - label, - value, - speed: 1.0, - min: 0.0, - max: 0.0, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.3f\0") }, - _phantom: PhantomData, - } - } - - pub fn build(self) -> bool { - unsafe { - sys::igDragFloat( - self.label.as_ptr(), - self.value as *mut f32, - self.speed, - self.min, - self.max, - self.display_format.as_ptr(), - 0, - ) - } - } - - impl_display_format!(DragFloat); - impl_min_max!(DragFloat, f32); - impl_speed!(DragFloat); -} - -macro_rules! impl_drag_floatn { - ($DragFloatN:ident, $N:expr, $igDragFloatN:ident) => { - #[must_use] - pub struct $DragFloatN<'ui, 'p> { - label: &'p ImStr, - value: &'p mut [f32; $N], - speed: f32, - min: f32, - max: f32, - display_format: &'p ImStr, - _phantom: PhantomData<&'ui Ui<'ui>>, - } - - impl<'ui, 'p> $DragFloatN<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [f32; $N]) -> Self { - $DragFloatN { - label, - value, - speed: 1.0, - min: 0.0, - max: 0.0, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.3f\0") }, - _phantom: PhantomData, - } - } - - pub fn build(self) -> bool { - unsafe { - sys::$igDragFloatN( - self.label.as_ptr(), - self.value.as_mut_ptr(), - self.speed, - self.min, - self.max, - self.display_format.as_ptr(), - 0, - ) - } - } - - impl_display_format!(DragFloat); - impl_min_max!(DragFloat, f32); - impl_speed!(DragFloat); - } - }; -} - -impl_drag_floatn!(DragFloat2, 2, igDragFloat2); -impl_drag_floatn!(DragFloat3, 3, igDragFloat3); -impl_drag_floatn!(DragFloat4, 4, igDragFloat4); - -#[must_use] -pub struct DragFloatRange2<'ui, 'p> { - label: &'p ImStr, - current_min: &'p mut f32, - current_max: &'p mut f32, - speed: f32, - min: f32, - max: f32, - display_format: &'p ImStr, - display_format_max: Option<&'p ImStr>, - _phantom: PhantomData<&'ui Ui<'ui>>, -} - -impl<'ui, 'p> DragFloatRange2<'ui, 'p> { - pub fn new( - _: &Ui<'ui>, - label: &'p ImStr, - current_min: &'p mut f32, - current_max: &'p mut f32, - ) -> Self { - DragFloatRange2 { - label, - current_min, - current_max, - speed: 1.0, - min: 0.0, - max: 0.0, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.3f\0") }, - display_format_max: None, - _phantom: PhantomData, - } - } - - pub fn build(self) -> bool { - unsafe { - sys::igDragFloatRange2( - self.label.as_ptr(), - self.current_min as *mut f32, - self.current_max as *mut f32, - self.speed, - self.min, - self.max, - self.display_format.as_ptr(), - self.display_format_max.map_or(ptr::null(), |f| f.as_ptr()), - 0, - ) - } - } - - #[inline] - pub fn display_format_max(mut self, display_format: Option<&'p ImStr>) -> Self { - self.display_format_max = display_format; - self - } - - impl_display_format!(DragFloatRange2); - impl_min_max!(DragFloatRange2, f32); - impl_speed!(DragFloatRange2); -} - -#[must_use] -pub struct DragInt<'ui, 'p> { - label: &'p ImStr, - value: &'p mut i32, - speed: f32, - min: i32, - max: i32, - display_format: &'p ImStr, - _phantom: PhantomData<&'ui Ui<'ui>>, -} - -impl<'ui, 'p> DragInt<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut i32) -> Self { - DragInt { - label, - value, - speed: 1.0, - min: 0, - max: 0, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.0f\0") }, - _phantom: PhantomData, - } - } - - pub fn build(self) -> bool { - unsafe { - sys::igDragInt( - self.label.as_ptr(), - self.value as *mut i32, - self.speed, - self.min, - self.max, - self.display_format.as_ptr(), - 0, - ) - } - } - - impl_display_format!(DragInt); - impl_min_max!(DragInt, i32); - impl_speed!(DragInt); -} - -macro_rules! impl_drag_intn { - ($DragIntN:ident, $N:expr, $igDragIntN:ident) => { - #[must_use] - pub struct $DragIntN<'ui, 'p> { - label: &'p ImStr, - value: &'p mut [i32; $N], - speed: f32, - min: i32, - max: i32, - display_format: &'p ImStr, - _phantom: PhantomData<&'ui Ui<'ui>>, - } - - impl<'ui, 'p> $DragIntN<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [i32; $N]) -> Self { - $DragIntN { - label, - value, - speed: 1.0, - min: 0, - max: 0, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.0f\0") }, - _phantom: PhantomData, - } - } - - pub fn build(self) -> bool { - unsafe { - sys::$igDragIntN( - self.label.as_ptr(), - self.value.as_mut_ptr(), - self.speed, - self.min, - self.max, - self.display_format.as_ptr(), - 0, - ) - } - } - - impl_display_format!(DragInt); - impl_min_max!(DragInt, i32); - impl_speed!(DragInt); - } - }; -} - -impl_drag_intn!(DragInt2, 2, igDragInt2); -impl_drag_intn!(DragInt3, 3, igDragInt3); -impl_drag_intn!(DragInt4, 4, igDragInt4); - -#[must_use] -pub struct DragIntRange2<'ui, 'p> { - label: &'p ImStr, - current_min: &'p mut i32, - current_max: &'p mut i32, - speed: f32, - min: i32, - max: i32, - display_format: &'p ImStr, - display_format_max: Option<&'p ImStr>, - _phantom: PhantomData<&'ui Ui<'ui>>, -} - -impl<'ui, 'p> DragIntRange2<'ui, 'p> { - pub fn new( - _: &Ui<'ui>, - label: &'p ImStr, - current_min: &'p mut i32, - current_max: &'p mut i32, - ) -> Self { - DragIntRange2 { - label, - current_min, - current_max, - speed: 1.0, - min: 0, - max: 0, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.0f\0") }, - display_format_max: None, - _phantom: PhantomData, - } - } - - pub fn build(self) -> bool { - unsafe { - sys::igDragIntRange2( - self.label.as_ptr(), - self.current_min as *mut i32, - self.current_max as *mut i32, - self.speed, - self.min, - self.max, - self.display_format.as_ptr(), - self.display_format_max.map_or(ptr::null(), |f| f.as_ptr()), - 0, - ) - } - } - - #[inline] - pub fn display_format_max(mut self, display_format: Option<&'p ImStr>) -> Self { - self.display_format_max = display_format; - self - } - - impl_display_format!(DragIntRange2); - impl_min_max!(DragIntRange2, i32); - impl_speed!(DragIntRange2); -} diff --git a/src/internal.rs b/src/internal.rs index af39bd5..41c66b4 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,5 +1,6 @@ //! Internal raw utilities (don't use unless you know what you're doing!) +use std::ops::{RangeFrom, RangeInclusive, RangeToInclusive}; use std::slice; /// A generic version of the raw imgui-sys ImVector struct types @@ -149,3 +150,35 @@ unsafe impl DataTypeKind for f32 { unsafe impl DataTypeKind for f64 { const KIND: DataType = DataType::F64; } + +pub trait InclusiveRangeBounds { + fn start_bound(&self) -> Option<&T>; + fn end_bound(&self) -> Option<&T>; +} + +impl InclusiveRangeBounds for RangeFrom { + fn start_bound(&self) -> Option<&T> { + Some(&self.start) + } + fn end_bound(&self) -> Option<&T> { + None + } +} + +impl InclusiveRangeBounds for RangeInclusive { + fn start_bound(&self) -> Option<&T> { + Some(self.start()) + } + fn end_bound(&self) -> Option<&T> { + Some(self.end()) + } +} + +impl InclusiveRangeBounds for RangeToInclusive { + fn start_bound(&self) -> Option<&T> { + None + } + fn end_bound(&self) -> Option<&T> { + Some(&self.end) + } +} diff --git a/src/lib.rs b/src/lib.rs index 19a0489..1073ce1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,10 +11,6 @@ use std::thread; pub use self::clipboard::*; pub use self::context::*; -pub use self::drag::{ - DragFloat, DragFloat2, DragFloat3, DragFloat4, DragFloatRange2, DragInt, DragInt2, DragInt3, - DragInt4, DragIntRange2, -}; pub use self::fonts::atlas::*; pub use self::fonts::font::*; pub use self::fonts::glyph::*; @@ -38,6 +34,7 @@ pub use self::style::*; pub use self::utils::*; pub use self::widget::color_editors::*; pub use self::widget::combo_box::*; +pub use self::widget::drag::*; pub use self::widget::image::*; pub use self::widget::menu::*; pub use self::widget::progress_bar::*; @@ -53,7 +50,6 @@ use internal::RawCast; mod clipboard; mod columns; mod context; -mod drag; mod fonts; mod input; mod input_widget; @@ -272,62 +268,6 @@ impl<'ui> Ui<'ui> { } } -// Widgets: Drag -impl<'ui> Ui<'ui> { - pub fn drag_float<'p>(&self, label: &'p ImStr, value: &'p mut f32) -> DragFloat<'ui, 'p> { - DragFloat::new(self, label, value) - } - pub fn drag_float2<'p>( - &self, - label: &'p ImStr, - value: &'p mut [f32; 2], - ) -> DragFloat2<'ui, 'p> { - DragFloat2::new(self, label, value) - } - pub fn drag_float3<'p>( - &self, - label: &'p ImStr, - value: &'p mut [f32; 3], - ) -> DragFloat3<'ui, 'p> { - DragFloat3::new(self, label, value) - } - pub fn drag_float4<'p>( - &self, - label: &'p ImStr, - value: &'p mut [f32; 4], - ) -> DragFloat4<'ui, 'p> { - DragFloat4::new(self, label, value) - } - pub fn drag_float_range2<'p>( - &self, - label: &'p ImStr, - current_min: &'p mut f32, - current_max: &'p mut f32, - ) -> DragFloatRange2<'ui, 'p> { - DragFloatRange2::new(self, label, current_min, current_max) - } - pub fn drag_int<'p>(&self, label: &'p ImStr, value: &'p mut i32) -> DragInt<'ui, 'p> { - DragInt::new(self, label, value) - } - pub fn drag_int2<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 2]) -> DragInt2<'ui, 'p> { - DragInt2::new(self, label, value) - } - pub fn drag_int3<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 3]) -> DragInt3<'ui, 'p> { - DragInt3::new(self, label, value) - } - pub fn drag_int4<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 4]) -> DragInt4<'ui, 'p> { - DragInt4::new(self, label, value) - } - pub fn drag_int_range2<'p>( - &self, - label: &'p ImStr, - current_min: &'p mut i32, - current_max: &'p mut i32, - ) -> DragIntRange2<'ui, 'p> { - DragIntRange2::new(self, label, current_min, current_max) - } -} - /// Tracks a layout tooltip that must be ended by calling `.end()` #[must_use] pub struct TooltipToken { diff --git a/src/widget/drag.rs b/src/widget/drag.rs new file mode 100644 index 0000000..e36b0e5 --- /dev/null +++ b/src/widget/drag.rs @@ -0,0 +1,221 @@ +use std::os::raw::c_void; +use std::ptr; + +use crate::internal::{DataTypeKind, InclusiveRangeBounds}; +use crate::string::ImStr; +use crate::sys; +use crate::widget::slider::SliderFlags; +use crate::Ui; + +/// Builder for a drag slider widget. +#[derive(Copy, Clone, Debug)] +#[must_use] +pub struct Drag<'a, T: DataTypeKind> { + label: &'a ImStr, + speed: f32, + min: Option, + max: Option, + display_format: Option<&'a ImStr>, + flags: SliderFlags, +} + +impl<'a, T: DataTypeKind> Drag<'a, T> { + /// Constructs a new drag slider builder. + pub fn new(label: &ImStr) -> Drag { + Drag { + label, + speed: 1.0, + min: None, + max: None, + display_format: None, + flags: SliderFlags::empty(), + } + } + #[inline] + pub fn range>(mut self, range: R) -> Self { + self.min = range.start_bound().copied(); + self.max = range.end_bound().copied(); + self + } + /// Sets the value increment for a movement of one pixel. + /// + /// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1 + #[inline] + pub fn speed(mut self, speed: f32) -> Self { + self.speed = speed; + self + } + /// Sets the display format using *a C-style printf string* + #[inline] + pub fn display_format(mut self, display_format: &'a ImStr) -> Self { + self.display_format = Some(display_format); + self + } + /// Replaces all current settings with the given flags + #[inline] + pub fn flags(mut self, flags: SliderFlags) -> Self { + self.flags = flags; + self + } + /// Builds a drag slider that is bound to the given value. + /// + /// Returns true if the slider value was changed. + pub fn build(self, _: &Ui, value: &mut T) -> bool { + unsafe { + sys::igDragScalar( + self.label.as_ptr(), + T::KIND as i32, + value as *mut T as *mut c_void, + self.speed, + self.min + .as_ref() + .map(|min| min as *const T) + .unwrap_or(ptr::null()) as *const c_void, + self.max + .as_ref() + .map(|max| max as *const T) + .unwrap_or(ptr::null()) as *const c_void, + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.flags.bits() as i32, + ) + } + } + /// Builds a horizontal array of multiple drag sliders attached to the given slice. + /// + /// Returns true if any slider value was changed. + pub fn build_array(self, _: &Ui, values: &mut [T]) -> bool { + unsafe { + sys::igDragScalarN( + self.label.as_ptr(), + T::KIND as i32, + values.as_mut_ptr() as *mut c_void, + values.len() as i32, + self.speed, + self.min + .as_ref() + .map(|min| min as *const T) + .unwrap_or(ptr::null()) as *const c_void, + self.max + .as_ref() + .map(|max| max as *const T) + .unwrap_or(ptr::null()) as *const c_void, + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.flags.bits() as i32, + ) + } + } +} + +/// Builder for a drag slider widget. +#[derive(Copy, Clone, Debug)] +#[must_use] +pub struct DragRange<'a, T: DataTypeKind> { + label: &'a ImStr, + speed: f32, + min: Option, + max: Option, + display_format: Option<&'a ImStr>, + max_display_format: Option<&'a ImStr>, + flags: SliderFlags, +} + +impl<'a, T: DataTypeKind> DragRange<'a, T> { + /// Constructs a new drag slider builder. + pub fn new(label: &ImStr) -> DragRange { + DragRange { + label, + speed: 1.0, + min: None, + max: None, + display_format: None, + max_display_format: None, + flags: SliderFlags::empty(), + } + } + #[inline] + pub fn range>(mut self, range: R) -> Self { + self.min = range.start_bound().copied(); + self.max = range.end_bound().copied(); + self + } + /// Sets the value increment for a movement of one pixel. + /// + /// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1 + #[inline] + pub fn speed(mut self, speed: f32) -> Self { + self.speed = speed; + self + } + /// Sets the display format using *a C-style printf string* + #[inline] + pub fn display_format(mut self, display_format: &'a ImStr) -> Self { + self.display_format = Some(display_format); + self + } + /// Sets the display format for the max value using *a C-style printf string* + #[inline] + pub fn max_display_format(mut self, max_display_format: &'a ImStr) -> Self { + self.max_display_format = Some(max_display_format); + self + } + /// Replaces all current settings with the given flags + #[inline] + pub fn flags(mut self, flags: SliderFlags) -> Self { + self.flags = flags; + self + } +} + +impl<'a> DragRange<'a, f32> { + /// Builds a drag range slider that is bound to the given min/max values. + /// + /// Returns true if the slider value was changed. + pub fn build(self, _: &Ui, min: &mut f32, max: &mut f32) -> bool { + unsafe { + sys::igDragFloatRange2( + self.label.as_ptr(), + min as *mut f32, + max as *mut f32, + self.speed, + self.min.unwrap_or(0.0), + self.max.unwrap_or(0.0), + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.max_display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.flags.bits() as i32, + ) + } + } +} + +impl<'a> DragRange<'a, i32> { + /// Builds a drag range slider that is bound to the given min/max values. + /// + /// Returns true if the slider value was changed. + pub fn build(self, _: &Ui, min: &mut i32, max: &mut i32) -> bool { + unsafe { + sys::igDragIntRange2( + self.label.as_ptr(), + min as *mut i32, + max as *mut i32, + self.speed, + self.min.unwrap_or(0), + self.max.unwrap_or(0), + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.max_display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.flags.bits() as i32, + ) + } + } +} diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 3244388..064cdee 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -1,5 +1,6 @@ pub mod color_editors; pub mod combo_box; +pub mod drag; pub mod image; pub mod menu; pub mod misc;