mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Add ability to customize the display of hover plot labels
This commit is contained in:
@@ -7,7 +7,7 @@ use epaint::Mesh;
|
|||||||
use crate::util::float_ord::FloatOrd;
|
use crate::util::float_ord::FloatOrd;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
use super::{PlotBounds, ScreenTransform};
|
use super::{CustomLabelFunc, PlotBounds, ScreenTransform};
|
||||||
use rect_elem::*;
|
use rect_elem::*;
|
||||||
use values::*;
|
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: &CustomLabelFunc,
|
||||||
|
) {
|
||||||
let points = match self.geometry() {
|
let points = match self.geometry() {
|
||||||
PlotGeometry::Points(points) => points,
|
PlotGeometry::Points(points) => points,
|
||||||
PlotGeometry::None => {
|
PlotGeometry::None => {
|
||||||
@@ -83,7 +89,7 @@ pub(super) trait PlotItem {
|
|||||||
let pointer = plot.transform.position_from_value(&value);
|
let pointer = plot.transform.position_from_value(&value);
|
||||||
shapes.push(Shape::circle_filled(pointer, 3.0, line_color));
|
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)
|
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<'_>,
|
||||||
|
_: &CustomLabelFunc,
|
||||||
|
) {
|
||||||
let bar = &self.bars[elem.index];
|
let bar = &self.bars[elem.index];
|
||||||
|
|
||||||
bar.add_shapes(plot.transform, true, shapes);
|
bar.add_shapes(plot.transform, true, shapes);
|
||||||
@@ -1501,7 +1513,13 @@ impl PlotItem for BoxPlot {
|
|||||||
find_closest_rect(&self.boxes, point, transform)
|
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<'_>,
|
||||||
|
_: &CustomLabelFunc,
|
||||||
|
) {
|
||||||
let box_plot = &self.boxes[elem.index];
|
let box_plot = &self.boxes[elem.index];
|
||||||
|
|
||||||
box_plot.add_shapes(plot.transform, true, shapes);
|
box_plot.add_shapes(plot.transform, true, shapes);
|
||||||
@@ -1619,6 +1637,7 @@ pub(super) fn rulers_at_value(
|
|||||||
name: &str,
|
name: &str,
|
||||||
plot: &PlotConfig<'_>,
|
plot: &PlotConfig<'_>,
|
||||||
shapes: &mut Vec<Shape>,
|
shapes: &mut Vec<Shape>,
|
||||||
|
custom_label_func: &CustomLabelFunc,
|
||||||
) {
|
) {
|
||||||
let line_color = rulers_color(plot.ui);
|
let line_color = rulers_color(plot.ui);
|
||||||
if plot.show_x {
|
if plot.show_x {
|
||||||
@@ -1638,7 +1657,9 @@ pub(super) fn rulers_at_value(
|
|||||||
let scale = plot.transform.dvalue_dpos();
|
let scale = plot.transform.dvalue_dpos();
|
||||||
let x_decimals = ((-scale[0].abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
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);
|
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!(
|
format!(
|
||||||
"{}x = {:.*}\ny = {:.*}",
|
"{}x = {:.*}\ny = {:.*}",
|
||||||
prefix, x_decimals, value.x, y_decimals, value.y
|
prefix, x_decimals, value.x, y_decimals, value.y
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ mod items;
|
|||||||
mod legend;
|
mod legend;
|
||||||
mod transform;
|
mod transform;
|
||||||
|
|
||||||
|
type CustomLabelFunc = Option<Box<dyn Fn(&str, &Value) -> String>>;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Information about the plot that has to persist between frames.
|
/// Information about the plot that has to persist between frames.
|
||||||
@@ -76,6 +78,7 @@ pub struct Plot {
|
|||||||
|
|
||||||
show_x: bool,
|
show_x: bool,
|
||||||
show_y: bool,
|
show_y: bool,
|
||||||
|
custom_label_func: CustomLabelFunc,
|
||||||
legend_config: Option<Legend>,
|
legend_config: Option<Legend>,
|
||||||
show_background: bool,
|
show_background: bool,
|
||||||
show_axes: [bool; 2],
|
show_axes: [bool; 2],
|
||||||
@@ -102,6 +105,7 @@ impl Plot {
|
|||||||
|
|
||||||
show_x: true,
|
show_x: true,
|
||||||
show_y: true,
|
show_y: true,
|
||||||
|
custom_label_func: None,
|
||||||
legend_config: None,
|
legend_config: None,
|
||||||
show_background: true,
|
show_background: true,
|
||||||
show_axes: [true; 2],
|
show_axes: [true; 2],
|
||||||
@@ -182,6 +186,32 @@ impl Plot {
|
|||||||
self
|
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.
|
/// Expand bounds to include the given x value.
|
||||||
/// For instance, to always show the y axis, call `plot.include_x(0.0)`.
|
/// 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 {
|
pub fn include_x(mut self, x: impl Into<f64>) -> Self {
|
||||||
@@ -235,6 +265,7 @@ impl Plot {
|
|||||||
view_aspect,
|
view_aspect,
|
||||||
mut show_x,
|
mut show_x,
|
||||||
mut show_y,
|
mut show_y,
|
||||||
|
custom_label_func,
|
||||||
legend_config,
|
legend_config,
|
||||||
show_background,
|
show_background,
|
||||||
show_axes,
|
show_axes,
|
||||||
@@ -406,6 +437,7 @@ impl Plot {
|
|||||||
items,
|
items,
|
||||||
show_x,
|
show_x,
|
||||||
show_y,
|
show_y,
|
||||||
|
custom_label_func,
|
||||||
show_axes,
|
show_axes,
|
||||||
transform: transform.clone(),
|
transform: transform.clone(),
|
||||||
};
|
};
|
||||||
@@ -613,6 +645,7 @@ struct PreparedPlot {
|
|||||||
items: Vec<Box<dyn PlotItem>>,
|
items: Vec<Box<dyn PlotItem>>,
|
||||||
show_x: bool,
|
show_x: bool,
|
||||||
show_y: bool,
|
show_y: bool,
|
||||||
|
custom_label_func: CustomLabelFunc,
|
||||||
show_axes: [bool; 2],
|
show_axes: [bool; 2],
|
||||||
transform: ScreenTransform,
|
transform: ScreenTransform,
|
||||||
}
|
}
|
||||||
@@ -731,6 +764,7 @@ impl PreparedPlot {
|
|||||||
transform,
|
transform,
|
||||||
show_x,
|
show_x,
|
||||||
show_y,
|
show_y,
|
||||||
|
custom_label_func,
|
||||||
items,
|
items,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
@@ -760,10 +794,10 @@ impl PreparedPlot {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some((item, elem)) = closest {
|
if let Some((item, elem)) = closest {
|
||||||
item.on_hover(elem, shapes, &plot);
|
item.on_hover(elem, shapes, &plot, custom_label_func);
|
||||||
} else {
|
} else {
|
||||||
let value = transform.value_from_position(pointer);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user