summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmil Ernerfeldt <emil.ernerfeldt@gmail.com>2024-06-30 17:17:15 +0200
committerGitHub <noreply@github.com>2024-06-30 17:17:15 +0200
commitdc1f032846876667a9d45a248dbb0b97fa7e6b93 (patch)
tree2698989c543ecc00713cb7c128ab009d7ef93d7d
parente297a1d10730f2f3410d4353daa588241dd62a80 (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.rs61
-rw-r--r--crates/egui/src/widgets/drag_value.rs16
-rw-r--r--crates/egui/src/widgets/slider.rs6
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| {