Skip to content

Commit

Permalink
Add methods for custom number formatting in DragValue and Slider (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Adanos020 authored Jul 25, 2022
1 parent c02fbfe commit 36cdae9
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 8 deletions.
39 changes: 35 additions & 4 deletions egui/src/widgets/drag_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -85,6 +90,7 @@ impl<'a> DragValue<'a> {
clamp_range: f64::NEG_INFINITY..=f64::INFINITY,
min_decimals: 0,
max_decimals: None,
custom_formatter: None,
}
}

Expand Down Expand Up @@ -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> {
Expand All @@ -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();
Expand All @@ -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();
Expand Down
37 changes: 33 additions & 4 deletions egui/src/widgets/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -118,6 +123,7 @@ impl<'a> Slider<'a> {
step: None,
min_decimals: 0,
max_decimals: None,
custom_formatter: None,
}
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 36cdae9

Please sign in to comment.