diff options
author | Emil Ernerfeldt <emil.ernerfeldt@gmail.com> | 2024-06-30 17:17:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-30 17:17:15 +0200 |
commit | dc1f032846876667a9d45a248dbb0b97fa7e6b93 (patch) | |
tree | 2698989c543ecc00713cb7c128ab009d7ef93d7d | |
parent | e297a1d10730f2f3410d4353daa588241dd62a80 (diff) |
Add `Style::number_formatter` as the default used by `DragValue` (#4740)
This allows users to customize how numbers are converted into strings in
a `DragValue`, as a global default.
This can be used (for instance) to show thousands separators, or use `,`
as a decimal separator.
-rw-r--r-- | crates/egui/src/style.rs | 61 | ||||
-rw-r--r-- | crates/egui/src/widgets/drag_value.rs | 16 | ||||
-rw-r--r-- | crates/egui/src/widgets/slider.rs | 6 |
3 files changed, 76 insertions, 7 deletions
diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index cd39cacb..dc366cee 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -2,7 +2,7 @@ #![allow(clippy::if_same_then_else)] -use std::collections::BTreeMap; +use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc}; use epaint::{Rounding, Shadow, Stroke}; @@ -11,6 +11,51 @@ use crate::{ RichText, WidgetText, }; +/// How to format numbers in e.g. a [`crate::DragValue`]. +#[derive(Clone)] +pub struct NumberFormatter( + Arc<dyn 'static + Sync + Send + Fn(f64, RangeInclusive<usize>) -> String>, +); + +impl NumberFormatter { + /// The first argument is the number to be formatted. + /// The second argument is the range of the number of decimals to show. + /// + /// See [`Self::format`] for the meaning of the `decimals` argument. + #[inline] + pub fn new( + formatter: impl 'static + Sync + Send + Fn(f64, RangeInclusive<usize>) -> String, + ) -> Self { + Self(Arc::new(formatter)) + } + + /// Format the given number with the given number of decimals. + /// + /// Decimals are counted after the decimal point. + /// + /// The minimum number of decimals is usually automatically calculated + /// from the sensitivity of the [`crate::DragValue`] and will usually be respected (e.g. include trailing zeroes), + /// but if the given value requires more decimals to represent accurately, + /// more decimals will be shown, up to the given max. + #[inline] + pub fn format(&self, value: f64, decimals: RangeInclusive<usize>) -> String { + (self.0)(value, decimals) + } +} + +impl std::fmt::Debug for NumberFormatter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("NumberFormatter") + } +} + +impl PartialEq for NumberFormatter { + #[inline] + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + // ---------------------------------------------------------------------------- /// Alias for a [`FontId`] (font of a certain size). @@ -182,6 +227,12 @@ pub struct Style { /// The style to use for [`DragValue`] text. pub drag_value_text_style: TextStyle, + /// How to format numbers as strings, e.g. in a [`crate::DragValue`]. + /// + /// You can override this to e.g. add thousands separators. + #[cfg_attr(feature = "serde", serde(skip))] + pub number_formatter: NumberFormatter, + /// If set, labels, buttons, etc. will use this to determine whether to wrap the text at the /// right edge of the [`Ui`] they are in. By default, this is `None`. /// @@ -231,6 +282,12 @@ pub struct Style { pub always_scroll_the_only_direction: bool, } +#[test] +fn style_impl_send_sync() { + fn assert_send_sync<T: Send + Sync>() {} + assert_send_sync::<Style>(); +} + impl Style { // TODO(emilk): rename style.interact() to maybe… `style.interactive` ? /// Use this style for interactive things. @@ -1060,6 +1117,7 @@ impl Default for Style { override_text_style: None, text_styles: default_text_styles(), drag_value_text_style: TextStyle::Button, + number_formatter: NumberFormatter(Arc::new(emath::format_with_decimals_in_range)), wrap: None, wrap_mode: None, spacing: Spacing::default(), @@ -1355,6 +1413,7 @@ impl Style { override_text_style, text_styles, drag_value_text_style, + number_formatter: _, // can't change callbacks in the UI wrap: _, wrap_mode: _, spacing, diff --git a/crates/egui/src/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index 1c237543..614bd77e 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -176,6 +176,8 @@ impl<'a> DragValue<'a> { /// 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. /// + /// The default formatter is [`Style::number_formatter`]. + /// /// See also: [`DragValue::custom_parser`] /// /// ``` @@ -481,7 +483,10 @@ impl<'a> Widget for DragValue<'a> { let value_text = match custom_formatter { Some(custom_formatter) => custom_formatter(value, auto_decimals..=max_decimals), - None => emath::format_with_decimals_in_range(value, auto_decimals..=max_decimals), + None => ui + .style() + .number_formatter + .format(value, auto_decimals..=max_decimals), }; let text_style = ui.style().drag_value_text_style.clone(); @@ -676,8 +681,11 @@ fn parse(custom_parser: &Option<NumParser<'_>>, value_text: &str) -> Option<f64> } } -fn default_parser(value_text: &str) -> Option<f64> { - let value_text: String = value_text +/// The default egui parser of numbers. +/// +/// It ignored whitespaces anywhere in the input, and treats the special minus character (U+2212) as a normal minus. +fn default_parser(text: &str) -> Option<f64> { + let text: String = text .chars() // Ignore whitespace (trailing, leading, and thousands separators): .filter(|c| !c.is_whitespace()) @@ -685,7 +693,7 @@ fn default_parser(value_text: &str) -> Option<f64> { .map(|c| if c == '−' { '-' } else { c }) .collect(); - value_text.parse().ok() + text.parse().ok() } fn clamp_value_to_range(x: f64, range: RangeInclusive<f64>) -> f64 { diff --git a/crates/egui/src/widgets/slider.rs b/crates/egui/src/widgets/slider.rs index b055c546..f84cc2a1 100644 --- a/crates/egui/src/widgets/slider.rs +++ b/crates/egui/src/widgets/slider.rs @@ -318,7 +318,9 @@ impl<'a> Slider<'a> { /// 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. /// - /// See also: [`DragValue::custom_parser`] + /// The default formatter is [`Style::number_formatter`]. + /// + /// See also: [`Slider::custom_parser`] /// /// ``` /// # egui::__run_test_ui(|ui| { @@ -361,7 +363,7 @@ impl<'a> Slider<'a> { /// A custom parser takes an `&str` to parse into a number and returns `Some` if it was successfully parsed /// or `None` otherwise. /// - /// See also: [`DragValue::custom_formatter`] + /// See also: [`Slider::custom_formatter`] /// /// ``` /// # egui::__run_test_ui(|ui| { |