Add ability to customize the display of hover plot labels (#934)
* Add ability to customize the display of hover plot labels * Ergonomic enhancement to plot hover label function * Use Option instead of empty string for custom hover label name arg * Revert "Use Option instead of empty string for custom hover label name arg" This reverts commit 296caebb74b7ee993fbff97187791180d16708af. Co-authored-by: Ivgeni Segal <ivgeni.segal@tovutiteam.com>
This commit is contained in:
parent
d05379902c
commit
26885c20d0
2 changed files with 67 additions and 8 deletions
|
@ -7,7 +7,7 @@ use epaint::Mesh;
|
|||
|
||||
use crate::*;
|
||||
|
||||
use super::{PlotBounds, ScreenTransform};
|
||||
use super::{CustomLabelFuncRef, PlotBounds, ScreenTransform};
|
||||
use rect_elem::*;
|
||||
use values::*;
|
||||
|
||||
|
@ -61,7 +61,13 @@ pub(super) trait PlotItem {
|
|||
}
|
||||
}
|
||||
|
||||
fn on_hover(&self, elem: ClosestElem, shapes: &mut Vec<Shape>, plot: &PlotConfig<'_>) {
|
||||
fn on_hover(
|
||||
&self,
|
||||
elem: ClosestElem,
|
||||
shapes: &mut Vec<Shape>,
|
||||
plot: &PlotConfig<'_>,
|
||||
custom_label_func: &CustomLabelFuncRef,
|
||||
) {
|
||||
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<Shape>, plot: &PlotConfig<'_>) {
|
||||
fn on_hover(
|
||||
&self,
|
||||
elem: ClosestElem,
|
||||
shapes: &mut Vec<Shape>,
|
||||
plot: &PlotConfig<'_>,
|
||||
_: &CustomLabelFuncRef,
|
||||
) {
|
||||
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<Shape>, plot: &PlotConfig<'_>) {
|
||||
fn on_hover(
|
||||
&self,
|
||||
elem: ClosestElem,
|
||||
shapes: &mut Vec<Shape>,
|
||||
plot: &PlotConfig<'_>,
|
||||
_: &CustomLabelFuncRef,
|
||||
) {
|
||||
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<Shape>,
|
||||
custom_label_func: &CustomLabelFuncRef,
|
||||
) {
|
||||
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
|
||||
|
|
|
@ -18,6 +18,9 @@ mod items;
|
|||
mod legend;
|
||||
mod transform;
|
||||
|
||||
type CustomLabelFunc = dyn Fn(&str, &Value) -> String;
|
||||
type CustomLabelFuncRef = Option<Box<CustomLabelFunc>>;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Information about the plot that has to persist between frames.
|
||||
|
@ -76,6 +79,7 @@ pub struct Plot {
|
|||
|
||||
show_x: bool,
|
||||
show_y: bool,
|
||||
custom_label_func: CustomLabelFuncRef,
|
||||
legend_config: Option<Legend>,
|
||||
show_background: bool,
|
||||
show_axes: [bool; 2],
|
||||
|
@ -102,6 +106,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 +187,35 @@ 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(|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<F: 'static + Fn(&str, &Value) -> String>(
|
||||
mut self,
|
||||
custom_lebel_func: F,
|
||||
) -> Self {
|
||||
self.custom_label_func = Some(Box::new(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<f64>) -> Self {
|
||||
|
@ -235,6 +269,7 @@ impl Plot {
|
|||
view_aspect,
|
||||
mut show_x,
|
||||
mut show_y,
|
||||
custom_label_func,
|
||||
legend_config,
|
||||
show_background,
|
||||
show_axes,
|
||||
|
@ -406,6 +441,7 @@ impl Plot {
|
|||
items,
|
||||
show_x,
|
||||
show_y,
|
||||
custom_label_func,
|
||||
show_axes,
|
||||
transform: transform.clone(),
|
||||
};
|
||||
|
@ -613,6 +649,7 @@ struct PreparedPlot {
|
|||
items: Vec<Box<dyn PlotItem>>,
|
||||
show_x: bool,
|
||||
show_y: bool,
|
||||
custom_label_func: CustomLabelFuncRef,
|
||||
show_axes: [bool; 2],
|
||||
transform: ScreenTransform,
|
||||
}
|
||||
|
@ -731,6 +768,7 @@ impl PreparedPlot {
|
|||
transform,
|
||||
show_x,
|
||||
show_y,
|
||||
custom_label_func,
|
||||
items,
|
||||
..
|
||||
} = self;
|
||||
|
@ -760,10 +798,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue