From 4f17c3f61a45fec7cbcb8bd298cd32caa721ff73 Mon Sep 17 00:00:00 2001 From: Ivgeni Segal Date: Sun, 5 Dec 2021 22:17:23 -0800 Subject: [PATCH 1/4] Add ability to customize the display of hover plot labels --- egui/src/widgets/plot/items/mod.rs | 33 +++++++++++++++++++++----- egui/src/widgets/plot/mod.rs | 38 ++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index 9f935ca7ed8..a5b62e976f2 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -7,7 +7,7 @@ use epaint::Mesh; use crate::util::float_ord::FloatOrd; use crate::*; -use super::{PlotBounds, ScreenTransform}; +use super::{CustomLabelFunc, PlotBounds, ScreenTransform}; use rect_elem::*; use values::*; @@ -61,7 +61,13 @@ pub(super) trait PlotItem { } } - fn on_hover(&self, elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>) { + fn on_hover( + &self, + elem: ClosestElem, + shapes: &mut Vec, + plot: &PlotConfig<'_>, + custom_label_func: &CustomLabelFunc, + ) { let points = match self.geometry() { PlotGeometry::Points(points) => points, PlotGeometry::None => { @@ -83,7 +89,7 @@ pub(super) trait PlotItem { let pointer = plot.transform.position_from_value(&value); shapes.push(Shape::circle_filled(pointer, 3.0, line_color)); - rulers_at_value(pointer, value, self.name(), plot, shapes); + rulers_at_value(pointer, value, self.name(), plot, shapes, custom_label_func); } } @@ -1365,7 +1371,13 @@ impl PlotItem for BarChart { find_closest_rect(&self.bars, point, transform) } - fn on_hover(&self, elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>) { + fn on_hover( + &self, + elem: ClosestElem, + shapes: &mut Vec, + plot: &PlotConfig<'_>, + _: &CustomLabelFunc, + ) { let bar = &self.bars[elem.index]; bar.add_shapes(plot.transform, true, shapes); @@ -1501,7 +1513,13 @@ impl PlotItem for BoxPlot { find_closest_rect(&self.boxes, point, transform) } - fn on_hover(&self, elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>) { + fn on_hover( + &self, + elem: ClosestElem, + shapes: &mut Vec, + plot: &PlotConfig<'_>, + _: &CustomLabelFunc, + ) { let box_plot = &self.boxes[elem.index]; box_plot.add_shapes(plot.transform, true, shapes); @@ -1619,6 +1637,7 @@ pub(super) fn rulers_at_value( name: &str, plot: &PlotConfig<'_>, shapes: &mut Vec, + custom_label_func: &CustomLabelFunc, ) { let line_color = rulers_color(plot.ui); if plot.show_x { @@ -1638,7 +1657,9 @@ pub(super) fn rulers_at_value( let scale = plot.transform.dvalue_dpos(); let x_decimals = ((-scale[0].abs().log10()).ceil().at_least(0.0) as usize).at_most(6); let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).at_most(6); - if plot.show_x && plot.show_y { + if let Some(custom_label) = custom_label_func { + custom_label(name, &value) + } else if plot.show_x && plot.show_y { format!( "{}x = {:.*}\ny = {:.*}", prefix, x_decimals, value.x, y_decimals, value.y diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 768c9f18b8f..47eca2ea1f6 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -18,6 +18,8 @@ mod items; mod legend; mod transform; +type CustomLabelFunc = Option String>>; + // ---------------------------------------------------------------------------- /// Information about the plot that has to persist between frames. @@ -76,6 +78,7 @@ pub struct Plot { show_x: bool, show_y: bool, + custom_label_func: CustomLabelFunc, legend_config: Option, show_background: bool, show_axes: [bool; 2], @@ -102,6 +105,7 @@ impl Plot { show_x: true, show_y: true, + custom_label_func: None, legend_config: None, show_background: true, show_axes: [true; 2], @@ -182,6 +186,32 @@ impl Plot { self } + /// Provide a function to customize the on-hovel label for the x and y axis + /// + /// ``` + /// # egui::__run_test_ui(|ui| { + /// use egui::plot::{Line, Plot, Value, Values}; + /// let sin = (0..1000).map(|i| { + /// let x = i as f64 * 0.01; + /// Value::new(x, x.sin()) + /// }); + /// let line = Line::new(Values::from_values_iter(sin)); + /// Plot::new("my_plot").view_aspect(2.0) + /// .custom_label_func(Some(Box::new(|name, value| { + /// if !name.is_empty() { + /// format!("{}: {:.*}%", name, 1, value.y).to_string() + /// } else { + /// "".to_string() + /// } + /// }))) + /// .show(ui, |plot_ui| plot_ui.line(line)); + /// # }); + /// ``` + pub fn custom_label_func(mut self, custom_lebel_func: CustomLabelFunc) -> Self { + self.custom_label_func = custom_lebel_func; + self + } + /// Expand bounds to include the given x value. /// For instance, to always show the y axis, call `plot.include_x(0.0)`. pub fn include_x(mut self, x: impl Into) -> Self { @@ -235,6 +265,7 @@ impl Plot { view_aspect, mut show_x, mut show_y, + custom_label_func, legend_config, show_background, show_axes, @@ -406,6 +437,7 @@ impl Plot { items, show_x, show_y, + custom_label_func, show_axes, transform: transform.clone(), }; @@ -613,6 +645,7 @@ struct PreparedPlot { items: Vec>, show_x: bool, show_y: bool, + custom_label_func: CustomLabelFunc, show_axes: [bool; 2], transform: ScreenTransform, } @@ -731,6 +764,7 @@ impl PreparedPlot { transform, show_x, show_y, + custom_label_func, items, .. } = self; @@ -760,10 +794,10 @@ impl PreparedPlot { }; if let Some((item, elem)) = closest { - item.on_hover(elem, shapes, &plot); + item.on_hover(elem, shapes, &plot, custom_label_func); } else { let value = transform.value_from_position(pointer); - items::rulers_at_value(pointer, value, "", &plot, shapes); + items::rulers_at_value(pointer, value, "", &plot, shapes, custom_label_func); } } } From ea959d15e7e72b918e247306aa469ecb2757ac60 Mon Sep 17 00:00:00 2001 From: Ivgeni Segal Date: Mon, 6 Dec 2021 22:17:40 -0800 Subject: [PATCH 2/4] Ergonomic enhancement to plot hover label function --- egui/src/widgets/plot/items/mod.rs | 10 +++++----- egui/src/widgets/plot/mod.rs | 18 +++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index a5b62e976f2..0e703788e53 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -7,7 +7,7 @@ use epaint::Mesh; use crate::util::float_ord::FloatOrd; use crate::*; -use super::{CustomLabelFunc, PlotBounds, ScreenTransform}; +use super::{CustomLabelFuncRef, PlotBounds, ScreenTransform}; use rect_elem::*; use values::*; @@ -66,7 +66,7 @@ pub(super) trait PlotItem { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - custom_label_func: &CustomLabelFunc, + custom_label_func: &CustomLabelFuncRef, ) { let points = match self.geometry() { PlotGeometry::Points(points) => points, @@ -1376,7 +1376,7 @@ impl PlotItem for BarChart { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - _: &CustomLabelFunc, + _: &CustomLabelFuncRef, ) { let bar = &self.bars[elem.index]; @@ -1518,7 +1518,7 @@ impl PlotItem for BoxPlot { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - _: &CustomLabelFunc, + _: &CustomLabelFuncRef, ) { let box_plot = &self.boxes[elem.index]; @@ -1637,7 +1637,7 @@ pub(super) fn rulers_at_value( name: &str, plot: &PlotConfig<'_>, shapes: &mut Vec, - custom_label_func: &CustomLabelFunc, + custom_label_func: &CustomLabelFuncRef, ) { let line_color = rulers_color(plot.ui); if plot.show_x { diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 47eca2ea1f6..9e69e496ea6 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -18,7 +18,8 @@ mod items; mod legend; mod transform; -type CustomLabelFunc = Option String>>; +type CustomLabelFunc = dyn Fn(&str, &Value) -> String; +type CustomLabelFuncRef = Option>; // ---------------------------------------------------------------------------- @@ -78,7 +79,7 @@ pub struct Plot { show_x: bool, show_y: bool, - custom_label_func: CustomLabelFunc, + custom_label_func: CustomLabelFuncRef, legend_config: Option, show_background: bool, show_axes: [bool; 2], @@ -197,18 +198,21 @@ impl Plot { /// }); /// let line = Line::new(Values::from_values_iter(sin)); /// Plot::new("my_plot").view_aspect(2.0) - /// .custom_label_func(Some(Box::new(|name, value| { + /// .custom_label_func(|name, value| { /// if !name.is_empty() { /// format!("{}: {:.*}%", name, 1, value.y).to_string() /// } else { /// "".to_string() /// } - /// }))) + /// }) /// .show(ui, |plot_ui| plot_ui.line(line)); /// # }); /// ``` - pub fn custom_label_func(mut self, custom_lebel_func: CustomLabelFunc) -> Self { - self.custom_label_func = custom_lebel_func; + pub fn custom_label_func String>( + mut self, + custom_lebel_func: F, + ) -> Self { + self.custom_label_func = Some(Box::new(custom_lebel_func)); self } @@ -645,7 +649,7 @@ struct PreparedPlot { items: Vec>, show_x: bool, show_y: bool, - custom_label_func: CustomLabelFunc, + custom_label_func: CustomLabelFuncRef, show_axes: [bool; 2], transform: ScreenTransform, } From 296caebb74b7ee993fbff97187791180d16708af Mon Sep 17 00:00:00 2001 From: Ivgeni Segal Date: Fri, 10 Dec 2021 19:10:53 -0800 Subject: [PATCH 3/4] Use Option instead of empty string for custom hover label name arg --- egui/src/widgets/plot/items/mod.rs | 1 + egui/src/widgets/plot/mod.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index 0e703788e53..ea4dee6ae95 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -1658,6 +1658,7 @@ pub(super) fn rulers_at_value( let x_decimals = ((-scale[0].abs().log10()).ceil().at_least(0.0) as usize).at_most(6); let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).at_most(6); if let Some(custom_label) = custom_label_func { + let name = (!name.is_empty()).then(|| name); custom_label(name, &value) } else if plot.show_x && plot.show_y { format!( diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 9e69e496ea6..061aa6d3082 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -18,7 +18,7 @@ mod items; mod legend; mod transform; -type CustomLabelFunc = dyn Fn(&str, &Value) -> String; +type CustomLabelFunc = dyn Fn(Option<&str>, &Value) -> String; type CustomLabelFuncRef = Option>; // ---------------------------------------------------------------------------- @@ -199,7 +199,7 @@ impl Plot { /// let line = Line::new(Values::from_values_iter(sin)); /// Plot::new("my_plot").view_aspect(2.0) /// .custom_label_func(|name, value| { - /// if !name.is_empty() { + /// if let Some(name) = name { /// format!("{}: {:.*}%", name, 1, value.y).to_string() /// } else { /// "".to_string() @@ -208,7 +208,7 @@ impl Plot { /// .show(ui, |plot_ui| plot_ui.line(line)); /// # }); /// ``` - pub fn custom_label_func String>( + pub fn custom_label_func, &Value) -> String>( mut self, custom_lebel_func: F, ) -> Self { From 39e9bb3ef5e2fcd7d531806f439e406a85d19ec3 Mon Sep 17 00:00:00 2001 From: Ivgeni Segal Date: Wed, 22 Dec 2021 15:57:47 -0800 Subject: [PATCH 4/4] Revert "Use Option instead of empty string for custom hover label name arg" This reverts commit 296caebb74b7ee993fbff97187791180d16708af. --- egui/src/widgets/plot/items/mod.rs | 1 - egui/src/widgets/plot/mod.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index ea4dee6ae95..0e703788e53 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -1658,7 +1658,6 @@ pub(super) fn rulers_at_value( let x_decimals = ((-scale[0].abs().log10()).ceil().at_least(0.0) as usize).at_most(6); let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).at_most(6); if let Some(custom_label) = custom_label_func { - let name = (!name.is_empty()).then(|| name); custom_label(name, &value) } else if plot.show_x && plot.show_y { format!( diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 061aa6d3082..9e69e496ea6 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -18,7 +18,7 @@ mod items; mod legend; mod transform; -type CustomLabelFunc = dyn Fn(Option<&str>, &Value) -> String; +type CustomLabelFunc = dyn Fn(&str, &Value) -> String; type CustomLabelFuncRef = Option>; // ---------------------------------------------------------------------------- @@ -199,7 +199,7 @@ impl Plot { /// let line = Line::new(Values::from_values_iter(sin)); /// Plot::new("my_plot").view_aspect(2.0) /// .custom_label_func(|name, value| { - /// if let Some(name) = name { + /// if !name.is_empty() { /// format!("{}: {:.*}%", name, 1, value.y).to_string() /// } else { /// "".to_string() @@ -208,7 +208,7 @@ impl Plot { /// .show(ui, |plot_ui| plot_ui.line(line)); /// # }); /// ``` - pub fn custom_label_func, &Value) -> String>( + pub fn custom_label_func String>( mut self, custom_lebel_func: F, ) -> Self {