diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index decc8d1..c0eac05 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -17,6 +17,8 @@ - Redesigned image / image button API - Redesigned combo box API - Redesigned selectable API +- Redesigned slider API. Generic scalar sliders support all main data types and replace + previous individual sliders (int, int2, int3, int4, etc...) - Updated layout API - Renderer errors implement std::error::Error - Glium renderer re-exports imgui and glium diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 51395ea..487a107 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -406,9 +406,9 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { )); ui.spacing(); - ui.slider_float(im_str!("Wrap width"), &mut state.wrap_width, -20.0, 600.0) + Slider::new(im_str!("Wrap width"), -20.0 ..= 600.0) .display_format(im_str!("%.0f")) - .build(); + .build(ui, &mut state.wrap_width); ui.text(im_str!("Test paragraph 1:")); // TODO @@ -749,8 +749,8 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { ui.text(format!("Scrolling Text {}", i)); } }); - ui.slider_float(im_str!("Value"), &mut state.f, 0.0, 1.0) - .build(); + Slider::new(im_str!("Value"), 0.0..=1.0).build(ui, &mut state.f); + ui.input_float(im_str!("Input"), &mut state.f) .step(0.1) .build(); @@ -782,8 +782,7 @@ fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &m Note that you probably don't want to query the window size to output your content because that would create a feedback loop.", ); - ui.slider_int(im_str!("Number of lines"), &mut state.lines, 1, 20) - .build(); + Slider::new(im_str!("Number of lines"), 1..=20).build(ui, &mut state.lines); for i in 0..state.lines { ui.text(format!("{:2$}This is line {}", "", i, i as usize * 4)); } diff --git a/src/internal.rs b/src/internal.rs index dc2f483..5c8bd29 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -69,3 +69,57 @@ pub unsafe trait RawCast: Sized { &mut *(self as *mut _ as *mut T) } } + +/// A primary data type +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum DataType { + I8 = sys::ImGuiDataType_S8, + U8 = sys::ImGuiDataType_U8, + I16 = sys::ImGuiDataType_S16, + U16 = sys::ImGuiDataType_U16, + I32 = sys::ImGuiDataType_S32, + U32 = sys::ImGuiDataType_U32, + I64 = sys::ImGuiDataType_S64, + U64 = sys::ImGuiDataType_U64, + F32 = sys::ImGuiDataType_Float, + F64 = sys::ImGuiDataType_Double, +} + +/// Primitive type marker. +/// +/// If this trait is implemented for a type, it is assumed to have *exactly* the same +/// representation in memory as the primitive value described by the associated `KIND` constant. +pub unsafe trait DataTypeKind: Copy { + const KIND: DataType; +} +unsafe impl DataTypeKind for i8 { + const KIND: DataType = DataType::I8; +} +unsafe impl DataTypeKind for u8 { + const KIND: DataType = DataType::U8; +} +unsafe impl DataTypeKind for i16 { + const KIND: DataType = DataType::I16; +} +unsafe impl DataTypeKind for u16 { + const KIND: DataType = DataType::U16; +} +unsafe impl DataTypeKind for i32 { + const KIND: DataType = DataType::I32; +} +unsafe impl DataTypeKind for u32 { + const KIND: DataType = DataType::U32; +} +unsafe impl DataTypeKind for i64 { + const KIND: DataType = DataType::I64; +} +unsafe impl DataTypeKind for u64 { + const KIND: DataType = DataType::U64; +} +unsafe impl DataTypeKind for f32 { + const KIND: DataType = DataType::F32; +} +unsafe impl DataTypeKind for f64 { + const KIND: DataType = DataType::F64; +} diff --git a/src/lib.rs b/src/lib.rs index a023519..96ce08c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,10 +33,6 @@ pub use self::plotlines::PlotLines; pub use self::popup_modal::PopupModal; pub use self::render::draw_data::*; pub use self::render::renderer::*; -pub use self::sliders::{ - SliderFloat, SliderFloat2, SliderFloat3, SliderFloat4, SliderInt, SliderInt2, SliderInt3, - SliderInt4, -}; pub use self::stacks::*; pub use self::string::*; pub use self::style::*; @@ -47,6 +43,7 @@ pub use self::widget::combo_box::*; pub use self::widget::image::*; pub use self::widget::progress_bar::*; pub use self::widget::selectable::*; +pub use self::widget::slider::*; pub use self::window::child_window::*; pub use self::window::*; pub use self::window_draw_list::{ChannelsSplit, ImColor, WindowDrawList}; @@ -68,7 +65,6 @@ mod plothistogram; mod plotlines; mod popup_modal; mod render; -mod sliders; mod stacks; mod string; mod style; @@ -339,82 +335,6 @@ impl<'ui> Ui<'ui> { } } -// Widgets: Sliders -impl<'ui> Ui<'ui> { - pub fn slider_float<'p>( - &self, - label: &'p ImStr, - value: &'p mut f32, - min: f32, - max: f32, - ) -> SliderFloat<'ui, 'p> { - SliderFloat::new(self, label, value, min, max) - } - pub fn slider_float2<'p>( - &self, - label: &'p ImStr, - value: &'p mut [f32; 2], - min: f32, - max: f32, - ) -> SliderFloat2<'ui, 'p> { - SliderFloat2::new(self, label, value, min, max) - } - pub fn slider_float3<'p>( - &self, - label: &'p ImStr, - value: &'p mut [f32; 3], - min: f32, - max: f32, - ) -> SliderFloat3<'ui, 'p> { - SliderFloat3::new(self, label, value, min, max) - } - pub fn slider_float4<'p>( - &self, - label: &'p ImStr, - value: &'p mut [f32; 4], - min: f32, - max: f32, - ) -> SliderFloat4<'ui, 'p> { - SliderFloat4::new(self, label, value, min, max) - } - pub fn slider_int<'p>( - &self, - label: &'p ImStr, - value: &'p mut i32, - min: i32, - max: i32, - ) -> SliderInt<'ui, 'p> { - SliderInt::new(self, label, value, min, max) - } - pub fn slider_int2<'p>( - &self, - label: &'p ImStr, - value: &'p mut [i32; 2], - min: i32, - max: i32, - ) -> SliderInt2<'ui, 'p> { - SliderInt2::new(self, label, value, min, max) - } - pub fn slider_int3<'p>( - &self, - label: &'p ImStr, - value: &'p mut [i32; 3], - min: i32, - max: i32, - ) -> SliderInt3<'ui, 'p> { - SliderInt3::new(self, label, value, min, max) - } - pub fn slider_int4<'p>( - &self, - label: &'p ImStr, - value: &'p mut [i32; 4], - min: i32, - max: i32, - ) -> SliderInt4<'ui, 'p> { - SliderInt4::new(self, label, value, min, max) - } -} - // Widgets: Trees impl<'ui> Ui<'ui> { pub fn tree_node<'p>(&self, id: &'p ImStr) -> TreeNode<'ui, 'p> { diff --git a/src/sliders.rs b/src/sliders.rs deleted file mode 100644 index 620c427..0000000 --- a/src/sliders.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::marker::PhantomData; -use sys; - -use super::{ImStr, Ui}; - -// TODO: Consider using Range, even though it is half-open - -#[must_use] -pub struct SliderInt<'ui, 'p> { - label: &'p ImStr, - value: &'p mut i32, - min: i32, - max: i32, - display_format: &'p ImStr, - _phantom: PhantomData<&'ui Ui<'ui>>, -} - -impl<'ui, 'p> SliderInt<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut i32, min: i32, max: i32) -> Self { - SliderInt { - label, - value, - min, - max, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.0f\0") }, - _phantom: PhantomData, - } - } - #[inline] - pub fn display_format(mut self, display_format: &'p ImStr) -> Self { - self.display_format = display_format; - self - } - pub fn build(self) -> bool { - unsafe { - sys::igSliderInt( - self.label.as_ptr(), - self.value, - self.min, - self.max, - self.display_format.as_ptr(), - ) - } - } -} - -macro_rules! impl_slider_intn { - ($SliderIntN:ident, $N:expr, $igSliderIntN:ident) => { - #[must_use] - pub struct $SliderIntN<'ui, 'p> { - label: &'p ImStr, - value: &'p mut [i32; $N], - min: i32, - max: i32, - display_format: &'p ImStr, - _phantom: PhantomData<&'ui Ui<'ui>>, - } - - impl<'ui, 'p> $SliderIntN<'ui, 'p> { - pub fn new( - _: &Ui<'ui>, - label: &'p ImStr, - value: &'p mut [i32; $N], - min: i32, - max: i32, - ) -> Self { - $SliderIntN { - label, - value, - min, - max, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.0f\0") }, - _phantom: PhantomData, - } - } - #[inline] - pub fn display_format(mut self, display_format: &'p ImStr) -> Self { - self.display_format = display_format; - self - } - pub fn build(self) -> bool { - unsafe { - sys::$igSliderIntN( - self.label.as_ptr(), - self.value.as_mut_ptr(), - self.min, - self.max, - self.display_format.as_ptr(), - ) - } - } - } - }; -} - -impl_slider_intn!(SliderInt2, 2, igSliderInt2); -impl_slider_intn!(SliderInt3, 3, igSliderInt3); -impl_slider_intn!(SliderInt4, 4, igSliderInt4); - -#[must_use] -pub struct SliderFloat<'ui, 'p> { - label: &'p ImStr, - value: &'p mut f32, - min: f32, - max: f32, - display_format: &'p ImStr, - power: f32, - _phantom: PhantomData<&'ui Ui<'ui>>, -} - -impl<'ui, 'p> SliderFloat<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut f32, min: f32, max: f32) -> Self { - SliderFloat { - label, - value, - min, - max, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.3f\0") }, - power: 1.0, - _phantom: PhantomData, - } - } - #[inline] - pub fn display_format(mut self, display_format: &'p ImStr) -> Self { - self.display_format = display_format; - self - } - #[inline] - pub fn power(mut self, power: f32) -> Self { - self.power = power; - self - } - pub fn build(self) -> bool { - unsafe { - sys::igSliderFloat( - self.label.as_ptr(), - self.value, - self.min, - self.max, - self.display_format.as_ptr(), - self.power, - ) - } - } -} - -macro_rules! impl_slider_floatn { - ($SliderFloatN:ident, $N:expr, $igSliderFloatN:ident) => { - #[must_use] - pub struct $SliderFloatN<'ui, 'p> { - label: &'p ImStr, - value: &'p mut [f32; $N], - min: f32, - max: f32, - display_format: &'p ImStr, - power: f32, - _phantom: PhantomData<&'ui Ui<'ui>>, - } - - impl<'ui, 'p> $SliderFloatN<'ui, 'p> { - pub fn new( - _: &Ui<'ui>, - label: &'p ImStr, - value: &'p mut [f32; $N], - min: f32, - max: f32, - ) -> Self { - $SliderFloatN { - label, - value, - min, - max, - display_format: unsafe { ImStr::from_utf8_with_nul_unchecked(b"%.3f\0") }, - power: 1.0, - _phantom: PhantomData, - } - } - #[inline] - pub fn display_format(mut self, display_format: &'p ImStr) -> Self { - self.display_format = display_format; - self - } - #[inline] - pub fn power(mut self, power: f32) -> Self { - self.power = power; - self - } - pub fn build(self) -> bool { - unsafe { - sys::$igSliderFloatN( - self.label.as_ptr(), - self.value.as_mut_ptr(), - self.min, - self.max, - self.display_format.as_ptr(), - self.power, - ) - } - } - } - }; -} - -impl_slider_floatn!(SliderFloat2, 2, igSliderFloat2); -impl_slider_floatn!(SliderFloat3, 3, igSliderFloat3); -impl_slider_floatn!(SliderFloat4, 4, igSliderFloat4); diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 57067ba..a10d0a2 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -4,4 +4,5 @@ pub mod image; pub mod misc; pub mod progress_bar; pub mod selectable; +pub mod slider; pub mod text; diff --git a/src/widget/slider.rs b/src/widget/slider.rs new file mode 100644 index 0000000..040a6f6 --- /dev/null +++ b/src/widget/slider.rs @@ -0,0 +1,193 @@ +use std::ops::RangeInclusive; +use std::os::raw::c_void; +use std::ptr; + +use crate::internal::DataTypeKind; +use crate::string::ImStr; +use crate::sys; +use crate::Ui; + +/// Builder for a slider widget. +#[derive(Copy, Clone, Debug)] +#[must_use] +pub struct Slider<'a, T: DataTypeKind> { + label: &'a ImStr, + min: T, + max: T, + display_format: Option<&'a ImStr>, + power: f32, +} + +impl<'a, T: DataTypeKind> Slider<'a, T> { + /// Constructs a new slider builder with the given range. + pub fn new(label: &ImStr, range: RangeInclusive) -> Slider { + Slider { + label, + min: *range.start(), + max: *range.end(), + display_format: None, + power: 1.0, + } + } + /// 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 power (exponent) of the slider values + #[inline] + pub fn power(mut self, power: f32) -> Self { + self.power = power; + self + } + /// Builds a 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::igSliderScalar( + self.label.as_ptr(), + T::KIND as i32, + value as *mut T as *mut c_void, + &self.min as *const T as *const c_void, + &self.max as *const T as *const c_void, + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.power, + ) + } + } + /// Builds a horizontal array of multiple 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::igSliderScalarN( + self.label.as_ptr(), + T::KIND as i32, + values.as_mut_ptr() as *mut c_void, + values.len() as i32, + &self.min as *const T as *const c_void, + &self.max as *const T as *const c_void, + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.power, + ) + } + } +} + +/// Builder for a vertical slider widget. +#[derive(Clone, Debug)] +#[must_use] +pub struct VerticalSlider<'a, T: DataTypeKind + Copy> { + label: &'a ImStr, + size: [f32; 2], + min: T, + max: T, + display_format: Option<&'a ImStr>, + power: f32, +} + +impl<'a, T: DataTypeKind> VerticalSlider<'a, T> { + /// Constructs a new vertical slider builder with the given size and range. + pub fn new(label: &ImStr, size: [f32; 2], range: RangeInclusive) -> VerticalSlider { + VerticalSlider { + label, + size, + min: *range.start(), + max: *range.end(), + display_format: None, + power: 1.0, + } + } + /// 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 power (exponent) of the slider values + #[inline] + pub fn power(mut self, power: f32) -> Self { + self.power = power; + self + } + /// Builds a vertical 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::igVSliderScalar( + self.label.as_ptr(), + self.size.into(), + T::KIND as i32, + value as *mut T as *mut c_void, + &self.min as *const T as *const c_void, + &self.max as *const T as *const c_void, + self.display_format + .map(ImStr::as_ptr) + .unwrap_or(ptr::null()), + self.power, + ) + } + } +} + +/// Builder for an angle slider widget. +#[derive(Copy, Clone, Debug)] +#[must_use] +pub struct AngleSlider<'a> { + label: &'a ImStr, + min_degrees: f32, + max_degrees: f32, + display_format: &'a ImStr, +} + +impl<'a> AngleSlider<'a> { + /// Constructs a new angle slider builder. + pub fn new(label: &ImStr) -> AngleSlider { + use crate::im_str; + AngleSlider { + label, + min_degrees: -360.0, + max_degrees: 360.0, + display_format: im_str!("%.0f deg"), + } + } + /// Sets the minimum value (in degrees) + #[inline] + pub fn min_degrees(mut self, min_degrees: f32) -> Self { + self.min_degrees = min_degrees; + self + } + /// Sets the maximum value (in degrees) + #[inline] + pub fn max_degrees(mut self, max_degrees: f32) -> Self { + self.max_degrees = max_degrees; + 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 = display_format; + self + } + /// Builds an angle slider that is bound to the given value (in radians). + /// + /// Returns true if the slider value was changed. + pub fn build(self, _: &Ui, value_rad: &mut f32) -> bool { + unsafe { + sys::igSliderAngle( + self.label.as_ptr(), + value_rad as *mut _, + self.min_degrees, + self.max_degrees, + self.display_format.as_ptr(), + ) + } + } +}