Introduce lifetime to `egui_plot::Plot` to replace `'static` fields (#4435)

* Closes https://github.com/emilk/egui/issues/4434

Shouldn't break anything, because when all arguments have a 'static
lifetime (required without this PR), the Plot is also 'static and can be
used like before.
This commit is contained in:
Fabian Lippold 2024-05-15 09:58:31 +02:00 committed by GitHub
parent c1eb3f884d
commit ce59e43869
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 56 additions and 52 deletions

View File

@ -8,7 +8,7 @@ use egui::{
use super::{transform::PlotTransform, GridMark}; use super::{transform::PlotTransform, GridMark};
pub(super) type AxisFormatterFn = dyn Fn(GridMark, usize, &RangeInclusive<f64>) -> String; pub(super) type AxisFormatterFn<'a> = dyn Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'a;
/// X or Y axis. /// X or Y axis.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -98,9 +98,9 @@ impl From<Placement> for VPlacement {
/// ///
/// Used to configure axis label and ticks. /// Used to configure axis label and ticks.
#[derive(Clone)] #[derive(Clone)]
pub struct AxisHints { pub struct AxisHints<'a> {
pub(super) label: WidgetText, pub(super) label: WidgetText,
pub(super) formatter: Arc<AxisFormatterFn>, pub(super) formatter: Arc<AxisFormatterFn<'a>>,
pub(super) digits: usize, pub(super) digits: usize,
pub(super) placement: Placement, pub(super) placement: Placement,
pub(super) label_spacing: Rangef, pub(super) label_spacing: Rangef,
@ -109,7 +109,7 @@ pub struct AxisHints {
// TODO(JohannesProgrammiert): this just a guess. It might cease to work if a user changes font size. // TODO(JohannesProgrammiert): this just a guess. It might cease to work if a user changes font size.
const LINE_HEIGHT: f32 = 12.0; const LINE_HEIGHT: f32 = 12.0;
impl AxisHints { impl<'a> AxisHints<'a> {
/// Initializes a default axis configuration for the X axis. /// Initializes a default axis configuration for the X axis.
pub fn new_x() -> Self { pub fn new_x() -> Self {
Self::new(Axis::X) Self::new(Axis::X)
@ -145,7 +145,7 @@ impl AxisHints {
/// The second parameter of `formatter` is the currently shown range on this axis. /// The second parameter of `formatter` is the currently shown range on this axis.
pub fn formatter( pub fn formatter(
mut self, mut self,
fmt: impl Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'static, fmt: impl Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'a,
) -> Self { ) -> Self {
self.formatter = Arc::new(fmt); self.formatter = Arc::new(fmt);
self self
@ -230,9 +230,9 @@ impl AxisHints {
} }
#[derive(Clone)] #[derive(Clone)]
pub(super) struct AxisWidget { pub(super) struct AxisWidget<'a> {
pub range: RangeInclusive<f64>, pub range: RangeInclusive<f64>,
pub hints: AxisHints, pub hints: AxisHints<'a>,
/// The region where we draw the axis labels. /// The region where we draw the axis labels.
pub rect: Rect, pub rect: Rect,
@ -240,9 +240,9 @@ pub(super) struct AxisWidget {
pub steps: Arc<Vec<GridMark>>, pub steps: Arc<Vec<GridMark>>,
} }
impl AxisWidget { impl<'a> AxisWidget<'a> {
/// if `rect` as width or height == 0, is will be automatically calculated from ticks and text. /// if `rect` as width or height == 0, is will be automatically calculated from ticks and text.
pub fn new(hints: AxisHints, rect: Rect) -> Self { pub fn new(hints: AxisHints<'a>, rect: Rect) -> Self {
Self { Self {
range: (0.0..=0.0), range: (0.0..=0.0),
hints, hints,

View File

@ -81,7 +81,7 @@ pub trait PlotItem {
shapes: &mut Vec<Shape>, shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>, cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>, plot: &PlotConfig<'_>,
label_formatter: &LabelFormatter, label_formatter: &LabelFormatter<'_>,
) { ) {
let points = match self.geometry() { let points = match self.geometry() {
PlotGeometry::Points(points) => points, PlotGeometry::Points(points) => points,
@ -1735,7 +1735,7 @@ impl PlotItem for BarChart {
shapes: &mut Vec<Shape>, shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>, cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>, plot: &PlotConfig<'_>,
_: &LabelFormatter, _: &LabelFormatter<'_>,
) { ) {
let bar = &self.bars[elem.index]; let bar = &self.bars[elem.index];
@ -1909,7 +1909,7 @@ impl PlotItem for BoxPlot {
shapes: &mut Vec<Shape>, shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>, cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>, plot: &PlotConfig<'_>,
_: &LabelFormatter, _: &LabelFormatter<'_>,
) { ) {
let box_plot = &self.boxes[elem.index]; let box_plot = &self.boxes[elem.index];
@ -2033,7 +2033,7 @@ pub(super) fn rulers_at_value(
plot: &PlotConfig<'_>, plot: &PlotConfig<'_>,
shapes: &mut Vec<Shape>, shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>, cursors: &mut Vec<Cursor>,
label_formatter: &LabelFormatter, label_formatter: &LabelFormatter<'_>,
) { ) {
if plot.show_x { if plot.show_x {
cursors.push(Cursor::Vertical { x: value.x }); cursors.push(Cursor::Vertical { x: value.x });

View File

@ -37,22 +37,22 @@ use axis::AxisWidget;
use items::{horizontal_line, rulers_color, vertical_line}; use items::{horizontal_line, rulers_color, vertical_line};
use legend::LegendWidget; use legend::LegendWidget;
type LabelFormatterFn = dyn Fn(&str, &PlotPoint) -> String; type LabelFormatterFn<'a> = dyn Fn(&str, &PlotPoint) -> String + 'a;
pub type LabelFormatter = Option<Box<LabelFormatterFn>>; pub type LabelFormatter<'a> = Option<Box<LabelFormatterFn<'a>>>;
type GridSpacerFn = dyn Fn(GridInput) -> Vec<GridMark>; type GridSpacerFn<'a> = dyn Fn(GridInput) -> Vec<GridMark> + 'a;
type GridSpacer = Box<GridSpacerFn>; type GridSpacer<'a> = Box<GridSpacerFn<'a>>;
type CoordinatesFormatterFn = dyn Fn(&PlotPoint, &PlotBounds) -> String; type CoordinatesFormatterFn<'a> = dyn Fn(&PlotPoint, &PlotBounds) -> String + 'a;
/// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`]. /// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`].
pub struct CoordinatesFormatter { pub struct CoordinatesFormatter<'a> {
function: Box<CoordinatesFormatterFn>, function: Box<CoordinatesFormatterFn<'a>>,
} }
impl CoordinatesFormatter { impl<'a> CoordinatesFormatter<'a> {
/// Create a new formatter based on the pointer coordinate and the plot bounds. /// Create a new formatter based on the pointer coordinate and the plot bounds.
pub fn new(function: impl Fn(&PlotPoint, &PlotBounds) -> String + 'static) -> Self { pub fn new(function: impl Fn(&PlotPoint, &PlotBounds) -> String + 'a) -> Self {
Self { Self {
function: Box::new(function), function: Box::new(function),
} }
@ -72,7 +72,7 @@ impl CoordinatesFormatter {
} }
} }
impl Default for CoordinatesFormatter { impl Default for CoordinatesFormatter<'_> {
fn default() -> Self { fn default() -> Self {
Self::with_decimals(3) Self::with_decimals(3)
} }
@ -143,7 +143,7 @@ pub struct PlotResponse<R> {
/// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line)); /// Plot::new("my_plot").view_aspect(2.0).show(ui, |plot_ui| plot_ui.line(line));
/// # }); /// # });
/// ``` /// ```
pub struct Plot { pub struct Plot<'a> {
id_source: Id, id_source: Id,
id: Option<Id>, id: Option<Id>,
@ -170,24 +170,24 @@ pub struct Plot {
show_x: bool, show_x: bool,
show_y: bool, show_y: bool,
label_formatter: LabelFormatter, label_formatter: LabelFormatter<'a>,
coordinates_formatter: Option<(Corner, CoordinatesFormatter)>, coordinates_formatter: Option<(Corner, CoordinatesFormatter<'a>)>,
x_axes: Vec<AxisHints>, // default x axes x_axes: Vec<AxisHints<'a>>, // default x axes
y_axes: Vec<AxisHints>, // default y axes y_axes: Vec<AxisHints<'a>>, // default y axes
legend_config: Option<Legend>, legend_config: Option<Legend>,
show_background: bool, show_background: bool,
show_axes: Vec2b, show_axes: Vec2b,
show_grid: Vec2b, show_grid: Vec2b,
grid_spacing: Rangef, grid_spacing: Rangef,
grid_spacers: [GridSpacer; 2], grid_spacers: [GridSpacer<'a>; 2],
sharp_grid_lines: bool, sharp_grid_lines: bool,
clamp_grid: bool, clamp_grid: bool,
sense: Sense, sense: Sense,
} }
impl Plot { impl<'a> Plot<'a> {
/// Give a unique id for each plot within the same [`Ui`]. /// Give a unique id for each plot within the same [`Ui`].
pub fn new(id_source: impl std::hash::Hash) -> Self { pub fn new(id_source: impl std::hash::Hash) -> Self {
Self { Self {
@ -405,7 +405,7 @@ impl Plot {
/// ``` /// ```
pub fn label_formatter( pub fn label_formatter(
mut self, mut self,
label_formatter: impl Fn(&str, &PlotPoint) -> String + 'static, label_formatter: impl Fn(&str, &PlotPoint) -> String + 'a,
) -> Self { ) -> Self {
self.label_formatter = Some(Box::new(label_formatter)); self.label_formatter = Some(Box::new(label_formatter));
self self
@ -415,7 +415,7 @@ impl Plot {
pub fn coordinates_formatter( pub fn coordinates_formatter(
mut self, mut self,
position: Corner, position: Corner,
formatter: CoordinatesFormatter, formatter: CoordinatesFormatter<'a>,
) -> Self { ) -> Self {
self.coordinates_formatter = Some((position, formatter)); self.coordinates_formatter = Some((position, formatter));
self self
@ -452,7 +452,7 @@ impl Plot {
/// ///
/// There are helpers for common cases, see [`log_grid_spacer`] and [`uniform_grid_spacer`]. /// There are helpers for common cases, see [`log_grid_spacer`] and [`uniform_grid_spacer`].
#[inline] #[inline]
pub fn x_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec<GridMark> + 'static) -> Self { pub fn x_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec<GridMark> + 'a) -> Self {
self.grid_spacers[0] = Box::new(spacer); self.grid_spacers[0] = Box::new(spacer);
self self
} }
@ -461,7 +461,7 @@ impl Plot {
/// ///
/// See [`Self::x_grid_spacer`] for explanation. /// See [`Self::x_grid_spacer`] for explanation.
#[inline] #[inline]
pub fn y_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec<GridMark> + 'static) -> Self { pub fn y_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec<GridMark> + 'a) -> Self {
self.grid_spacers[1] = Box::new(spacer); self.grid_spacers[1] = Box::new(spacer);
self self
} }
@ -662,7 +662,7 @@ impl Plot {
/// * currently shown range on this axis. /// * currently shown range on this axis.
pub fn x_axis_formatter( pub fn x_axis_formatter(
mut self, mut self,
fmt: impl Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'static, fmt: impl Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'a,
) -> Self { ) -> Self {
if let Some(main) = self.x_axes.first_mut() { if let Some(main) = self.x_axes.first_mut() {
main.formatter = Arc::new(fmt); main.formatter = Arc::new(fmt);
@ -678,7 +678,7 @@ impl Plot {
/// * currently shown range on this axis. /// * currently shown range on this axis.
pub fn y_axis_formatter( pub fn y_axis_formatter(
mut self, mut self,
fmt: impl Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'static, fmt: impl Fn(GridMark, usize, &RangeInclusive<f64>) -> String + 'a,
) -> Self { ) -> Self {
if let Some(main) = self.y_axes.first_mut() { if let Some(main) = self.y_axes.first_mut() {
main.formatter = Arc::new(fmt); main.formatter = Arc::new(fmt);
@ -703,7 +703,7 @@ impl Plot {
/// ///
/// More than one axis may be specified. The first specified axis is considered the main axis. /// More than one axis may be specified. The first specified axis is considered the main axis.
#[inline] #[inline]
pub fn custom_x_axes(mut self, hints: Vec<AxisHints>) -> Self { pub fn custom_x_axes(mut self, hints: Vec<AxisHints<'a>>) -> Self {
self.x_axes = hints; self.x_axes = hints;
self self
} }
@ -712,17 +712,21 @@ impl Plot {
/// ///
/// More than one axis may be specified. The first specified axis is considered the main axis. /// More than one axis may be specified. The first specified axis is considered the main axis.
#[inline] #[inline]
pub fn custom_y_axes(mut self, hints: Vec<AxisHints>) -> Self { pub fn custom_y_axes(mut self, hints: Vec<AxisHints<'a>>) -> Self {
self.y_axes = hints; self.y_axes = hints;
self self
} }
/// Interact with and add items to the plot and finally draw it. /// Interact with and add items to the plot and finally draw it.
pub fn show<R>(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi) -> R) -> PlotResponse<R> { pub fn show<R>(
self,
ui: &mut Ui,
build_fn: impl FnOnce(&mut PlotUi) -> R + 'a,
) -> PlotResponse<R> {
self.show_dyn(ui, Box::new(build_fn)) self.show_dyn(ui, Box::new(build_fn))
} }
fn show_dyn<'a, R>( fn show_dyn<R>(
self, self,
ui: &mut Ui, ui: &mut Ui,
build_fn: Box<dyn FnOnce(&mut PlotUi) -> R + 'a>, build_fn: Box<dyn FnOnce(&mut PlotUi) -> R + 'a>,
@ -1246,12 +1250,12 @@ impl Plot {
} }
/// Returns the rect left after adding axes. /// Returns the rect left after adding axes.
fn axis_widgets( fn axis_widgets<'a>(
mem: Option<&PlotMemory>, mem: Option<&PlotMemory>,
show_axes: Vec2b, show_axes: Vec2b,
complete_rect: Rect, complete_rect: Rect,
[x_axes, y_axes]: [&[AxisHints]; 2], [x_axes, y_axes]: [&'a [AxisHints<'a>]; 2],
) -> ([Vec<AxisWidget>; 2], Rect) { ) -> ([Vec<AxisWidget<'a>>; 2], Rect) {
// Next we want to create this layout. // Next we want to create this layout.
// Indices are only examples. // Indices are only examples.
// //
@ -1275,8 +1279,8 @@ fn axis_widgets(
// + +--------------------+---+ // + +--------------------+---+
// //
let mut x_axis_widgets = Vec::<AxisWidget>::new(); let mut x_axis_widgets = Vec::<AxisWidget<'_>>::new();
let mut y_axis_widgets = Vec::<AxisWidget>::new(); let mut y_axis_widgets = Vec::<AxisWidget<'_>>::new();
// Will shrink as we add more axes. // Will shrink as we add more axes.
let mut rect_left = complete_rect; let mut rect_left = complete_rect;
@ -1404,7 +1408,7 @@ pub struct GridMark {
/// ///
/// The logarithmic base, expressing how many times each grid unit is subdivided. /// The logarithmic base, expressing how many times each grid unit is subdivided.
/// 10 is a typical value, others are possible though. /// 10 is a typical value, others are possible though.
pub fn log_grid_spacer(log_base: i64) -> GridSpacer { pub fn log_grid_spacer(log_base: i64) -> GridSpacer<'static> {
let log_base = log_base as f64; let log_base = log_base as f64;
let step_sizes = move |input: GridInput| -> Vec<GridMark> { let step_sizes = move |input: GridInput| -> Vec<GridMark> {
// handle degenerate cases // handle degenerate cases
@ -1435,7 +1439,7 @@ pub fn log_grid_spacer(log_base: i64) -> GridSpacer {
/// ///
/// Why only 3 step sizes? Three is the number of different line thicknesses that egui typically uses in the grid. /// Why only 3 step sizes? Three is the number of different line thicknesses that egui typically uses in the grid.
/// Ideally, those 3 are not hardcoded values, but depend on the visible range (accessible through `GridInput`). /// Ideally, those 3 are not hardcoded values, but depend on the visible range (accessible through `GridInput`).
pub fn uniform_grid_spacer(spacer: impl Fn(GridInput) -> [f64; 3] + 'static) -> GridSpacer { pub fn uniform_grid_spacer<'a>(spacer: impl Fn(GridInput) -> [f64; 3] + 'a) -> GridSpacer<'a> {
let get_marks = move |input: GridInput| -> Vec<GridMark> { let get_marks = move |input: GridInput| -> Vec<GridMark> {
let bounds = input.bounds; let bounds = input.bounds;
let step_sizes = spacer(input); let step_sizes = spacer(input);
@ -1447,17 +1451,17 @@ pub fn uniform_grid_spacer(spacer: impl Fn(GridInput) -> [f64; 3] + 'static) ->
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
struct PreparedPlot { struct PreparedPlot<'a> {
items: Vec<Box<dyn PlotItem>>, items: Vec<Box<dyn PlotItem>>,
show_x: bool, show_x: bool,
show_y: bool, show_y: bool,
label_formatter: LabelFormatter, label_formatter: LabelFormatter<'a>,
coordinates_formatter: Option<(Corner, CoordinatesFormatter)>, coordinates_formatter: Option<(Corner, CoordinatesFormatter<'a>)>,
// axis_formatters: [AxisFormatter; 2], // axis_formatters: [AxisFormatter; 2],
transform: PlotTransform, transform: PlotTransform,
show_grid: Vec2b, show_grid: Vec2b,
grid_spacing: Rangef, grid_spacing: Rangef,
grid_spacers: [GridSpacer; 2], grid_spacers: [GridSpacer<'a>; 2],
draw_cursor_x: bool, draw_cursor_x: bool,
draw_cursor_y: bool, draw_cursor_y: bool,
draw_cursors: Vec<Cursor>, draw_cursors: Vec<Cursor>,
@ -1466,7 +1470,7 @@ struct PreparedPlot {
clamp_grid: bool, clamp_grid: bool,
} }
impl PreparedPlot { impl<'a> PreparedPlot<'a> {
fn ui(self, ui: &mut Ui, response: &Response) -> (Vec<Cursor>, Option<Id>) { fn ui(self, ui: &mut Ui, response: &Response) -> (Vec<Cursor>, Option<Id>) {
let mut axes_shapes = Vec::new(); let mut axes_shapes = Vec::new();