@ -7,6 +7,7 @@ use crate::*;
// ----------------------------------------------------------------------------
type NumFormatter < 'a > = Box < dyn 'a + Fn ( f64 , RangeInclusive < usize > ) -> String > ;
type NumParser < 'a > = Box < dyn 'a + Fn ( & str ) -> Option < f64 > > ;
// ----------------------------------------------------------------------------
@ -82,6 +83,7 @@ pub struct Slider<'a> {
min_decimals : usize ,
max_decimals : Option < usize > ,
custom_formatter : Option < NumFormatter < 'a > > ,
custom_parser : Option < NumParser < 'a > > ,
}
impl < 'a > Slider < 'a > {
@ -126,6 +128,7 @@ impl<'a> Slider<'a> {
min_decimals : 0 ,
max_decimals : None ,
custom_formatter : None ,
custom_parser : None ,
}
}
@ -254,10 +257,34 @@ 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`]
///
/// ```
/// # 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)));
/// # let mut my_i32: i32 = 0;
/// ui.add(egui::Slider::new(&mut my_i32, 0..=((60 * 60 * 24) - 1))
/// .custom_formatter(|n, _| {
/// let n = n as i32;
/// let hours = n / (60 * 60);
/// let mins = (n / 60) % 60;
/// let secs = n % 60;
/// format!("{hours:02}:{mins:02}:{secs:02}")
/// })
/// .custom_parser(|s| {
/// let parts: Vec<&str> = s.split(':').collect();
/// if parts.len() == 3 {
/// parts[0].parse::<i32>().and_then(|h| {
/// parts[1].parse::<i32>().and_then(|m| {
/// parts[2].parse::<i32>().map(|s| {
/// ((h * 60 * 60) + (m * 60) + s) as f64
/// })
/// })
/// })
/// .ok()
/// } else {
/// None
/// }
/// }));
/// # });
/// ```
pub fn custom_formatter (
@ -268,6 +295,159 @@ impl<'a> Slider<'a> {
self
}
/// Set custom parser defining how the text input is parsed into a number.
///
/// 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`]
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// # let mut my_i32: i32 = 0;
/// ui.add(egui::Slider::new(&mut my_i32, 0..=((60 * 60 * 24) - 1))
/// .custom_formatter(|n, _| {
/// let n = n as i32;
/// let hours = n / (60 * 60);
/// let mins = (n / 60) % 60;
/// let secs = n % 60;
/// format!("{hours:02}:{mins:02}:{secs:02}")
/// })
/// .custom_parser(|s| {
/// let parts: Vec<&str> = s.split(':').collect();
/// if parts.len() == 3 {
/// parts[0].parse::<i32>().and_then(|h| {
/// parts[1].parse::<i32>().and_then(|m| {
/// parts[2].parse::<i32>().map(|s| {
/// ((h * 60 * 60) + (m * 60) + s) as f64
/// })
/// })
/// })
/// .ok()
/// } else {
/// None
/// }
/// }));
/// # });
/// ```
pub fn custom_parser ( mut self , parser : impl 'a + Fn ( & str ) -> Option < f64 > ) -> Self {
self . custom_parser = Some ( Box ::new ( parser ) ) ;
self
}
/// Set `custom_formatter` and `custom_parser` to display and parse numbers as binary integers. Floating point
/// numbers are *not* supported.
///
/// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be
/// prefixed with additional 0s to match `min_width`.
///
/// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise
/// they will be prefixed with a '-' sign.
///
/// # Panics
///
/// Panics if `min_width` is 0.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// # let mut my_i32: i32 = 0;
/// ui.add(egui::Slider::new(&mut my_i32, -100..=100).binary(64, false));
/// # });
/// ```
pub fn binary ( self , min_width : usize , twos_complement : bool ) -> Self {
assert ! (
min_width > 0 ,
"Slider::binary: `min_width` must be greater than 0"
) ;
if twos_complement {
self . custom_formatter ( move | n , _ | format ! ( "{:0>min_width$b}" , n as i64 ) )
} else {
self . custom_formatter ( move | n , _ | {
let sign = if n < 0.0 { "-" } else { "" } ;
format ! ( "{sign}{:0>min_width$b}" , n . abs ( ) as i64 )
} )
}
. custom_parser ( | s | i64 ::from_str_radix ( s , 2 ) . map ( | n | n as f64 ) . ok ( ) )
}
/// Set `custom_formatter` and `custom_parser` to display and parse numbers as octal integers. Floating point
/// numbers are *not* supported.
///
/// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be
/// prefixed with additional 0s to match `min_width`.
///
/// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise
/// they will be prefixed with a '-' sign.
///
/// # Panics
///
/// Panics if `min_width` is 0.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// # let mut my_i32: i32 = 0;
/// ui.add(egui::Slider::new(&mut my_i32, -100..=100).octal(22, false));
/// # });
/// ```
pub fn octal ( self , min_width : usize , twos_complement : bool ) -> Self {
assert ! (
min_width > 0 ,
"Slider::octal: `min_width` must be greater than 0"
) ;
if twos_complement {
self . custom_formatter ( move | n , _ | format ! ( "{:0>min_width$o}" , n as i64 ) )
} else {
self . custom_formatter ( move | n , _ | {
let sign = if n < 0.0 { "-" } else { "" } ;
format ! ( "{sign}{:0>min_width$o}" , n . abs ( ) as i64 )
} )
}
. custom_parser ( | s | i64 ::from_str_radix ( s , 8 ) . map ( | n | n as f64 ) . ok ( ) )
}
/// Set `custom_formatter` and `custom_parser` to display and parse numbers as hexadecimal integers. Floating point
/// numbers are *not* supported.
///
/// `min_width` specifies the minimum number of displayed digits; if the number is shorter than this, it will be
/// prefixed with additional 0s to match `min_width`.
///
/// If `twos_complement` is true, negative values will be displayed as the 2's complement representation. Otherwise
/// they will be prefixed with a '-' sign.
///
/// # Panics
///
/// Panics if `min_width` is 0.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// # let mut my_i32: i32 = 0;
/// ui.add(egui::Slider::new(&mut my_i32, -100..=100).hexadecimal(16, false, true));
/// # });
/// ```
pub fn hexadecimal ( self , min_width : usize , twos_complement : bool , upper : bool ) -> Self {
assert ! (
min_width > 0 ,
"Slider::hexadecimal: `min_width` must be greater than 0"
) ;
match ( twos_complement , upper ) {
( true , true ) = > {
self . custom_formatter ( move | n , _ | format ! ( "{:0>min_width$X}" , n as i64 ) )
}
( true , false ) = > {
self . custom_formatter ( move | n , _ | format ! ( "{:0>min_width$x}" , n as i64 ) )
}
( false , true ) = > self . custom_formatter ( move | n , _ | {
let sign = if n < 0.0 { "-" } else { "" } ;
format ! ( "{sign}{:0>min_width$X}" , n . abs ( ) as i64 )
} ) ,
( false , false ) = > self . custom_formatter ( move | n , _ | {
let sign = if n < 0.0 { "-" } else { "" } ;
format ! ( "{sign}{:0>min_width$x}" , n . abs ( ) as i64 )
} ) ,
}
. custom_parser ( | s | i64 ::from_str_radix ( s , 16 ) . map ( | n | n as f64 ) . ok ( ) )
}
/// 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.
@ -493,17 +673,20 @@ impl<'a> Slider<'a> {
} ;
let mut value = self . get_value ( ) ;
let response = ui . add ( {
let dv = DragValue ::new ( & mut value )
let mut 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 ( ) ) ;
match & self . custom_formatter {
Some ( fmt ) = > dv . custom_formatter ( fmt ) ,
None = > dv ,
if let Some ( fmt ) = & self . custom_formatter {
dv = dv . custom_formatter ( fmt ) ;
} ;
if let Some ( parser ) = & self . custom_parser {
dv = dv . custom_parser ( parser ) ;
}
dv
} ) ;
if value ! = self . get_value ( ) {
self . set_value ( value ) ;