Add methods for custom number formatting in DragValue and Slider (#1851)

This commit is contained in:
Adam Gąsior 2022-07-25 21:38:24 +01:00 committed by GitHub
parent c02fbfe973
commit 36cdae98df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 8 deletions

View file

@ -27,6 +27,10 @@ impl MonoState {
// ----------------------------------------------------------------------------
type NumFormatter<'a> = Box<dyn 'a + Fn(f64, RangeInclusive<usize>) -> String>;
// ----------------------------------------------------------------------------
/// Combined into one function (rather than two) to make it easier
/// for the borrow checker.
type GetSetValue<'a> = Box<dyn 'a + FnMut(Option<f64>) -> f64>;
@ -56,6 +60,7 @@ pub struct DragValue<'a> {
clamp_range: RangeInclusive<f64>,
min_decimals: usize,
max_decimals: Option<usize>,
custom_formatter: Option<NumFormatter<'a>>,
}
impl<'a> DragValue<'a> {
@ -85,6 +90,7 @@ impl<'a> DragValue<'a> {
clamp_range: f64::NEG_INFINITY..=f64::INFINITY,
min_decimals: 0,
max_decimals: None,
custom_formatter: None,
}
}
@ -145,6 +151,25 @@ impl<'a> DragValue<'a> {
self.max_decimals = Some(num_decimals);
self
}
/// Set custom formatter defining how numbers are converted into text.
///
/// A custom formatter takes a `f64` for the numeric value and a `RangeInclusive<usize>` representing
/// the decimal range i.e. minimum and maximum number of decimal places shown.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// # let mut my_i64: i64 = 0;
/// ui.add(egui::DragValue::new(&mut my_i64).custom_formatter(|n, _| format!("{:X}", n as i64)));
/// # });
/// ```
pub fn custom_formatter(
mut self,
formatter: impl 'a + Fn(f64, RangeInclusive<usize>) -> String,
) -> Self {
self.custom_formatter = Some(Box::new(formatter));
self
}
}
impl<'a> Widget for DragValue<'a> {
@ -157,6 +182,7 @@ impl<'a> Widget for DragValue<'a> {
suffix,
min_decimals,
max_decimals,
custom_formatter,
} = self;
let shift = ui.input().modifiers.shift_only();
@ -174,10 +200,15 @@ impl<'a> Widget for DragValue<'a> {
let max_decimals = max_decimals.unwrap_or(auto_decimals + 2);
let auto_decimals = auto_decimals.clamp(min_decimals, max_decimals);
let value_text = if value == 0.0 {
let value_text = match custom_formatter {
Some(custom_formatter) => custom_formatter(value, auto_decimals..=max_decimals),
None => {
if value == 0.0 {
"0".to_owned()
} else {
emath::format_with_decimals_in_range(value, auto_decimals..=max_decimals)
}
}
};
let kb_edit_id = ui.next_auto_id();

View file

@ -6,6 +6,10 @@ use crate::*;
// ----------------------------------------------------------------------------
type NumFormatter<'a> = Box<dyn 'a + Fn(f64, RangeInclusive<usize>) -> String>;
// ----------------------------------------------------------------------------
/// Combined into one function (rather than two) to make it easier
/// for the borrow checker.
type GetSetValue<'a> = Box<dyn 'a + FnMut(Option<f64>) -> f64>;
@ -75,6 +79,7 @@ pub struct Slider<'a> {
step: Option<f64>,
min_decimals: usize,
max_decimals: Option<usize>,
custom_formatter: Option<NumFormatter<'a>>,
}
impl<'a> Slider<'a> {
@ -118,6 +123,7 @@ impl<'a> Slider<'a> {
step: None,
min_decimals: 0,
max_decimals: None,
custom_formatter: None,
}
}
@ -241,6 +247,25 @@ impl<'a> Slider<'a> {
self
}
/// Set custom formatter defining how numbers are converted into text.
///
/// A custom formatter takes a `f64` for the numeric value and a `RangeInclusive<usize>` representing
/// the decimal range i.e. minimum and maximum number of decimal places shown.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// # let mut my_i64: i64 = 0;
/// ui.add(egui::Slider::new(&mut my_i64, 0..=100).custom_formatter(|n, _| format!("{:X}", n as i64)));
/// # });
/// ```
pub fn custom_formatter(
mut self,
formatter: impl 'a + Fn(f64, RangeInclusive<usize>) -> String,
) -> Self {
self.custom_formatter = Some(Box::new(formatter));
self
}
/// Helper: equivalent to `self.precision(0).smallest_positive(1.0)`.
/// If you use one of the integer constructors (e.g. `Slider::i32`) this is called for you,
/// but if you want to have a slider for picking integer values in an `Slider::f64`, use this.
@ -465,15 +490,19 @@ impl<'a> Slider<'a> {
_ => self.current_gradient(&position_range),
};
let mut value = self.get_value();
let response = ui.add(
DragValue::new(&mut value)
let response = ui.add({
let dv = DragValue::new(&mut value)
.speed(speed)
.clamp_range(self.clamp_range())
.min_decimals(self.min_decimals)
.max_decimals_opt(self.max_decimals)
.suffix(self.suffix.clone())
.prefix(self.prefix.clone()),
);
.prefix(self.prefix.clone());
match &self.custom_formatter {
Some(fmt) => dv.custom_formatter(fmt),
None => dv,
}
});
if value != self.get_value() {
self.set_value(value);
}