Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions egui_plot/src/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub use crate::items::polygon::Polygon;
pub use crate::items::series::Line;
pub use crate::items::span::Span;
pub use crate::items::text::Text;
use crate::label::HoverPosition;
use crate::label::LabelFormatter;
use crate::rect_elem::RectElement;

Expand Down Expand Up @@ -198,7 +199,14 @@ pub trait PlotItem {
let pointer = plot.transform.position_from_point(&value);
shapes.push(Shape::circle_filled(pointer, 3.0, line_color));

rulers_and_tooltip_at_value(plot_area_response, value, self.name(), plot, cursors, label_formatter);
rulers_and_tooltip_at_value(
plot_area_response,
value,
Some((self.name(), elem.index)),
plot,
cursors,
label_formatter,
);
}
}

Expand Down Expand Up @@ -272,7 +280,7 @@ fn add_rulers_and_text(
pub(super) fn rulers_and_tooltip_at_value(
plot_area_response: &egui::Response,
value: PlotPoint,
name: &str,
nearest_point: Option<(&str, usize)>,
plot: &PlotConfig<'_>,
cursors: &mut Vec<Cursor>,
label_formatter: &Option<LabelFormatter<'_>>,
Expand All @@ -292,10 +300,18 @@ pub(super) fn rulers_and_tooltip_at_value(
return;
};

let text = custom_label(name, &value);
if text.is_empty() {
let hover_position = match nearest_point {
Some((name, index)) => HoverPosition::NearDataPoint {
plot_name: name,
position: value,
index,
},
None => HoverPosition::Elsewhere { position: value },
};

let Some(text) = custom_label(&hover_position) else {
return;
}
};

// We show the tooltip as soon as we're hovering the plot area:
let mut tooltip = egui::Tooltip::always_open(
Expand Down
38 changes: 30 additions & 8 deletions egui_plot/src/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,40 @@ pub fn format_number(number: f64, num_decimals: usize) -> String {
}
}

type LabelFormatterFn<'a> = dyn Fn(&str, &PlotPoint) -> String + 'a;
type LabelFormatterFn<'a> = dyn Fn(&HoverPosition<'_>) -> Option<String> + 'a;

/// Optional label formatter function for customizing hover labels.
pub type LabelFormatter<'a> = Box<LabelFormatterFn<'a>>;

/// Indicates the position of the cursor in a plot for hover purposes.
#[derive(Copy, Clone, PartialEq)]
pub enum HoverPosition<'a> {
NearDataPoint {
/// The name of the plot whose data point is nearest to the cursor
plot_name: &'a str,

/// The position of the nearest data point
position: PlotPoint,

/// The index of the nearest data point in its plot
index: usize,
},
Elsewhere {
/// The position in the plot over which the cursor hovers
position: PlotPoint,
},
}

/// Default label formatter that shows the x and y coordinates with 3 decimal
/// places.
pub fn default_label_formatter(name: &str, value: &PlotPoint) -> String {
let prefix = if name.is_empty() {
String::new()
} else {
format!("{name}\n")
};
format!("{}x = {:.3}\ny = {:.3}", prefix, value.x, value.y)
#[expect(clippy::unnecessary_wraps)]
pub fn default_label_formatter(pos: &HoverPosition<'_>) -> Option<String> {
Some(match pos {
HoverPosition::NearDataPoint {
plot_name,
position,
index: _,
} => format!("{}\nx = {:.3}\ny = {:.3}", plot_name, position.x, position.y),
HoverPosition::Elsewhere { position } => format!("x = {:.3}\ny = {:.3}", position.x, position.y),
})
}
1 change: 1 addition & 0 deletions egui_plot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub use crate::items::Polygon;
pub use crate::items::Span;
pub use crate::items::Text;
pub use crate::items::VLine;
pub use crate::label::HoverPosition;
pub use crate::label::LabelFormatter;
pub use crate::label::default_label_formatter;
pub use crate::label::format_number;
Expand Down
18 changes: 8 additions & 10 deletions egui_plot/src/plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use crate::items::PlotItem;
use crate::items::Span;
use crate::items::horizontal_line;
use crate::items::vertical_line;
use crate::label::HoverPosition;
use crate::label::LabelFormatter;
use crate::memory::PlotMemory;
use crate::overlays::CoordinatesFormatter;
Expand Down Expand Up @@ -396,9 +397,7 @@ impl<'a> Plot<'a> {
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// use egui_plot::Line;
/// use egui_plot::Plot;
/// use egui_plot::PlotPoints;
/// use egui_plot::{HoverPosition, Line, Plot, PlotPoints};
/// let sin: PlotPoints = (0..1000)
/// .map(|i| {
/// let x = i as f64 * 0.01;
Expand All @@ -408,18 +407,17 @@ impl<'a> Plot<'a> {
/// let line = Line::new("sin", sin);
/// Plot::new("my_plot")
/// .view_aspect(2.0)
/// .label_formatter(|name, value| {
/// if !name.is_empty() {
/// format!("{}: {:.*}%", name, 1, value.y)
/// } else {
/// "".to_owned()
/// .label_formatter(|pos| match pos {
/// HoverPosition::NearDataPoint { plot_name, position, .. } if !plot_name.is_empty() => {
/// Some(format!("{}: {:.*}%", plot_name, 1, position.y))
/// }
/// _ => None,
/// })
/// .show(ui, |plot_ui| plot_ui.line(line));
/// # });
/// ```
#[inline]
pub fn label_formatter(mut self, label_formatter: impl Fn(&str, &PlotPoint) -> String + 'a) -> Self {
pub fn label_formatter(mut self, label_formatter: impl Fn(&HoverPosition<'_>) -> Option<String> + 'a) -> Self {
self.label_formatter = Some(Box::new(label_formatter));
self
}
Expand Down Expand Up @@ -1601,7 +1599,7 @@ impl<'a> Plot<'a> {
items::rulers_and_tooltip_at_value(
&plot_ui.response,
value,
"",
None,
&plot,
&mut cursors,
&self.label_formatter,
Expand Down
16 changes: 12 additions & 4 deletions examples/custom_axes/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use eframe::egui::Response;
use egui_plot::AxisHints;
use egui_plot::GridInput;
use egui_plot::GridMark;
use egui_plot::HoverPosition;
use egui_plot::Line;
use egui_plot::Plot;
use egui_plot::PlotPoint;
use egui_plot::PlotPoints;

#[derive(Default)]
Expand Down Expand Up @@ -101,14 +101,22 @@ impl CustomAxesExample {
}
};

let label_fmt = |_s: &str, val: &PlotPoint| {
format!(
let label_fmt = |pos: &HoverPosition<'_>| {
let val = match pos {
HoverPosition::NearDataPoint {
plot_name: _,
position,
index: _,
}
| HoverPosition::Elsewhere { position } => position,
};
Some(format!(
"Day {d}, {h}:{m:02}\n{p:.2}%",
d = day(val.x),
h = hour(val.x),
m = minute(val.x),
p = percent(val.y)
)
))
};

let x_axes = vec![
Expand Down
Loading