diff --git a/egui/src/widgets/drag_value.rs b/egui/src/widgets/drag_value.rs index 657d2653414..2951b25f90f 100644 --- a/egui/src/widgets/drag_value.rs +++ b/egui/src/widgets/drag_value.rs @@ -27,6 +27,10 @@ impl MonoState { // ---------------------------------------------------------------------------- +type NumFormatter<'a> = Box) -> String>; + +// ---------------------------------------------------------------------------- + /// Combined into one function (rather than two) to make it easier /// for the borrow checker. type GetSetValue<'a> = Box) -> f64>; @@ -56,6 +60,7 @@ pub struct DragValue<'a> { clamp_range: RangeInclusive, min_decimals: usize, max_decimals: Option, + custom_formatter: Option>, } 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` 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) -> 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 { - "0".to_owned() - } else { - emath::format_with_decimals_in_range(value, auto_decimals..=max_decimals) + 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(); diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index 467589d8fb4..54013224699 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -6,6 +6,10 @@ use crate::*; // ---------------------------------------------------------------------------- +type NumFormatter<'a> = Box) -> String>; + +// ---------------------------------------------------------------------------- + /// Combined into one function (rather than two) to make it easier /// for the borrow checker. type GetSetValue<'a> = Box) -> f64>; @@ -75,6 +79,7 @@ pub struct Slider<'a> { step: Option, min_decimals: usize, max_decimals: Option, + custom_formatter: Option>, } 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` 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) -> 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. @@ -468,15 +493,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); }