Redesign sliders

This commit is contained in:
Joonas Javanainen 2019-07-13 14:51:00 +03:00
parent 95662e575a
commit 664efd91a7
No known key found for this signature in database
GPG Key ID: D39CCA5CB19B9179
7 changed files with 256 additions and 293 deletions

View File

@ -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

View File

@ -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));
}

View File

@ -69,3 +69,57 @@ pub unsafe trait RawCast<T>: 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;
}

View File

@ -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> {

View File

@ -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);

View File

@ -4,4 +4,5 @@ pub mod image;
pub mod misc;
pub mod progress_bar;
pub mod selectable;
pub mod slider;
pub mod text;

193
src/widget/slider.rs Normal file
View File

@ -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<T>) -> Slider<T> {
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<T>) -> VerticalSlider<T> {
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(),
)
}
}
}