From e8713bbb71a20bbfeb797f95279ca215e0a9cc42 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 2 Sep 2020 06:04:59 +0200 Subject: [PATCH] [widgets] DragValue can now be used with some integers --- egui/src/math.rs | 21 +++++++++++- egui/src/style.rs | 29 +++-------------- egui/src/widgets.rs | 65 ++++++++++++++++++++++++++++++++------ egui/src/widgets/slider.rs | 24 +++++++++++--- 4 files changed, 100 insertions(+), 39 deletions(-) diff --git a/egui/src/math.rs b/egui/src/math.rs index 4eda85a0..12113474 100644 --- a/egui/src/math.rs +++ b/egui/src/math.rs @@ -77,7 +77,15 @@ pub fn ease_in_ease_out(t: f32) -> f32 { pub const TAU: f32 = 2.0 * std::f32::consts::PI; /// Round a value to the given number of decimal places. -pub fn round_to_precision(value: f32, decimal_places: usize) -> f32 { +pub fn round_to_precision_f32(value: f32, decimal_places: usize) -> f32 { + // This is a stupid way of doing this, but stupid works. + format!("{:.*}", decimal_places, value) + .parse() + .unwrap_or_else(|_| value) +} + +/// Round a value to the given number of decimal places. +pub fn round_to_precision(value: f64, decimal_places: usize) -> f64 { // This is a stupid way of doing this, but stupid works. format!("{:.*}", decimal_places, value) .parse() @@ -113,6 +121,17 @@ pub fn almost_equal(a: f32, b: f32, epsilon: f32) -> bool { } } +#[test] +fn test_format() { + assert_eq!(format_with_minimum_precision(1_234_567.0, 0), "1234567"); + assert_eq!(format_with_minimum_precision(1_234_567.0, 1), "1234567.0"); + assert_eq!(format_with_minimum_precision(3.14, 2), "3.14"); + assert_eq!( + format_with_minimum_precision(std::f32::consts::PI, 2), + "3.1415927" + ); +} + #[test] fn test_almost_equal() { for &x in &[ diff --git a/egui/src/style.rs b/egui/src/style.rs index 22ba1d1a..1fff990c 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -429,29 +429,10 @@ fn ui_slider_vec2(ui: &mut Ui, value: &mut Vec2, range: std::ops::RangeInclusive // TODO: improve color picker fn ui_color(ui: &mut Ui, srgba: &mut Srgba, text: &str) { ui.horizontal_centered(|ui| { - // TODO: DragValue::u8 - // ui.label(format!("{} sRGBA: ", text)); - // ui.add(DragValue::u8(&mut srgba.r).speed(1)) - // .tooltip_text("r"); - // ui.add(DragValue::u8(&mut srgba.g).speed(1)) - // .tooltip_text("g"); - // ui.add(DragValue::u8(&mut srgba.b).speed(1)) - // .tooltip_text("b"); - // ui.add(DragValue::u8(&mut srgba.a).speed(1)) - // .tooltip_text("a"); - - ui.label(format!("{} RGBA: ", text)); - let mut rgba = Rgba::from(*srgba); - ui.add(DragValue::f32(&mut rgba.r).speed(0.003)) - .tooltip_text("r"); - ui.add(DragValue::f32(&mut rgba.g).speed(0.003)) - .tooltip_text("g"); - ui.add(DragValue::f32(&mut rgba.b).speed(0.003)) - .tooltip_text("b"); - ui.add(DragValue::f32(&mut rgba.a).speed(0.003)) - .tooltip_text("a"); - if rgba != Rgba::from(*srgba) { - *srgba = Srgba::from(rgba); - } + ui.label(format!("{} sRGBA: ", text)); + ui.add(DragValue::u8(&mut srgba.r)).tooltip_text("r"); + ui.add(DragValue::u8(&mut srgba.g)).tooltip_text("g"); + ui.add(DragValue::u8(&mut srgba.b)).tooltip_text("b"); + ui.add(DragValue::u8(&mut srgba.a)).tooltip_text("a"); }); } diff --git a/egui/src/widgets.rs b/egui/src/widgets.rs index 27d665db..f4d1a996 100644 --- a/egui/src/widgets.rs +++ b/egui/src/widgets.rs @@ -555,24 +555,69 @@ impl Widget for Separator { // ---------------------------------------------------------------------------- +/// Combined into one function (rather than two) to make it easier +/// for the borrow checker. +type GetSetValue<'a> = Box) -> f64>; + +fn get(value_function: &mut GetSetValue<'_>) -> f64 { + (value_function)(None) +} + +fn set(value_function: &mut GetSetValue<'_>, value: f64) { + (value_function)(Some(value)); +} + /// A floating point value that you can change by dragging the number. More compact than a slider. pub struct DragValue<'a> { - value: &'a mut f32, + value_function: GetSetValue<'a>, speed: f32, prefix: String, suffix: String, } impl<'a> DragValue<'a> { - pub fn f32(value: &'a mut f32) -> Self { - DragValue { - value, + fn from_get_set(value_function: impl 'a + FnMut(Option) -> f64) -> Self { + Self { + value_function: Box::new(value_function), speed: 1.0, prefix: Default::default(), suffix: Default::default(), } } + pub fn f32(value: &'a mut f32) -> Self { + Self { + ..Self::from_get_set(move |v: Option| { + if let Some(v) = v { + *value = v as f32 + } + *value as f64 + }) + } + } + + pub fn u8(value: &'a mut u8) -> Self { + Self { + ..Self::from_get_set(move |v: Option| { + if let Some(v) = v { + *value = v.round() as u8; + } + *value as f64 + }) + } + } + + pub fn i32(value: &'a mut i32) -> Self { + Self { + ..Self::from_get_set(move |v: Option| { + if let Some(v) = v { + *value = v.round() as i32; + } + *value as f64 + }) + } + } + /// How much the value changes when dragged one point (logical pixel). pub fn speed(mut self, speed: f32) -> Self { self.speed = speed; @@ -595,14 +640,15 @@ impl<'a> DragValue<'a> { impl<'a> Widget for DragValue<'a> { fn ui(self, ui: &mut Ui) -> Response { let Self { - value, + mut value_function, speed, prefix, suffix, } = self; + let value = get(&mut value_function); let aim_rad = ui.input().physical_pixel_size(); // ui.input().aim_radius(); // TODO let precision = (aim_rad / speed.abs()).log10().ceil().max(0.0) as usize; - let value_text = format_with_minimum_precision(*value, precision); + let value_text = format_with_minimum_precision(value as f32, precision); // TODO: full precision let kb_edit_id = ui.make_position_id().with("edit"); let is_kb_editing = ui.memory().has_kb_focus(kb_edit_id); @@ -621,7 +667,7 @@ impl<'a> Widget for DragValue<'a> { .text_style(TextStyle::Monospace), ); if let Ok(parsed_value) = value_text.parse() { - *value = parsed_value; + set(&mut value_function, parsed_value) } if ui.input().key_pressed(Key::Enter) { ui.memory().surrender_kb_focus(kb_edit_id); @@ -643,8 +689,9 @@ impl<'a> Widget for DragValue<'a> { let delta_points = mdelta.x - mdelta.y; // Increase to the right and up let delta_value = speed * delta_points; if delta_value != 0.0 { - *value += delta_value; - *value = round_to_precision(*value, precision); + let new_value = value + delta_value as f64; + let new_value = round_to_precision(new_value, precision); + set(&mut value_function, new_value); // TODO: To make use or `smart_aim` for `DragValue` we need to store some state somewhere, // otherwise we will just keep rounding to the same value while moving the mouse. } diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index c8d18011..4b99ad06 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -2,6 +2,7 @@ use std::ops::RangeInclusive; use crate::{paint::*, widgets::Label, *}; +// TODO: switch to f64 internally so we can handle integers larger than 2^24. /// Combined into one function (rather than two) to make it easier /// for the borrow checker. type SliderGetSet<'a> = Box) -> f32>; @@ -22,7 +23,7 @@ impl<'a> Slider<'a> { range: RangeInclusive, get_set_value: impl 'a + FnMut(Option) -> f32, ) -> Self { - Slider { + Self { get_set_value: Box::new(get_set_value), range, text: None, @@ -33,7 +34,7 @@ impl<'a> Slider<'a> { } pub fn f32(value: &'a mut f32, range: RangeInclusive) -> Self { - Slider { + Self { ..Self::from_get_set(range, move |v: Option| { if let Some(v) = v { *value = v @@ -43,9 +44,22 @@ impl<'a> Slider<'a> { } } + pub fn u8(value: &'a mut u8, range: RangeInclusive) -> Self { + let range = (*range.start() as f32)..=(*range.end() as f32); + Self { + precision: Some(0), + ..Self::from_get_set(range, move |v: Option| { + if let Some(v) = v { + *value = v.round() as u8 + } + *value as f32 + }) + } + } + pub fn i32(value: &'a mut i32, range: RangeInclusive) -> Self { let range = (*range.start() as f32)..=(*range.end() as f32); - Slider { + Self { precision: Some(0), ..Self::from_get_set(range, move |v: Option| { if let Some(v) = v { @@ -58,7 +72,7 @@ impl<'a> Slider<'a> { pub fn usize(value: &'a mut usize, range: RangeInclusive) -> Self { let range = (*range.start() as f32)..=(*range.end() as f32); - Slider { + Self { precision: Some(0), ..Self::from_get_set(range, move |v: Option| { if let Some(v) = v { @@ -94,7 +108,7 @@ impl<'a> Slider<'a> { fn set_value_f32(&mut self, mut value: f32) { if let Some(precision) = self.precision { - value = round_to_precision(value, precision); + value = round_to_precision_f32(value, precision); } (self.get_set_value)(Some(value)); }