From ce59e43869e5bf4b29dc81c1d0556bdad125c92d Mon Sep 17 00:00:00 2001 From: Fabian Lippold Date: Wed, 15 May 2024 09:58:31 +0200 Subject: [PATCH] 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. --- crates/egui_plot/src/axis.rs | 18 +++---- crates/egui_plot/src/items/mod.rs | 8 +-- crates/egui_plot/src/lib.rs | 82 ++++++++++++++++--------------- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/crates/egui_plot/src/axis.rs b/crates/egui_plot/src/axis.rs index 3c2972e6d87..059349d6673 100644 --- a/crates/egui_plot/src/axis.rs +++ b/crates/egui_plot/src/axis.rs @@ -8,7 +8,7 @@ use egui::{ use super::{transform::PlotTransform, GridMark}; -pub(super) type AxisFormatterFn = dyn Fn(GridMark, usize, &RangeInclusive) -> String; +pub(super) type AxisFormatterFn<'a> = dyn Fn(GridMark, usize, &RangeInclusive) -> String + 'a; /// X or Y axis. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -98,9 +98,9 @@ impl From for VPlacement { /// /// Used to configure axis label and ticks. #[derive(Clone)] -pub struct AxisHints { +pub struct AxisHints<'a> { pub(super) label: WidgetText, - pub(super) formatter: Arc, + pub(super) formatter: Arc>, pub(super) digits: usize, pub(super) placement: Placement, 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. const LINE_HEIGHT: f32 = 12.0; -impl AxisHints { +impl<'a> AxisHints<'a> { /// Initializes a default axis configuration for the X axis. pub fn new_x() -> Self { Self::new(Axis::X) @@ -145,7 +145,7 @@ impl AxisHints { /// The second parameter of `formatter` is the currently shown range on this axis. pub fn formatter( mut self, - fmt: impl Fn(GridMark, usize, &RangeInclusive) -> String + 'static, + fmt: impl Fn(GridMark, usize, &RangeInclusive) -> String + 'a, ) -> Self { self.formatter = Arc::new(fmt); self @@ -230,9 +230,9 @@ impl AxisHints { } #[derive(Clone)] -pub(super) struct AxisWidget { +pub(super) struct AxisWidget<'a> { pub range: RangeInclusive, - pub hints: AxisHints, + pub hints: AxisHints<'a>, /// The region where we draw the axis labels. pub rect: Rect, @@ -240,9 +240,9 @@ pub(super) struct AxisWidget { pub steps: Arc>, } -impl AxisWidget { +impl<'a> AxisWidget<'a> { /// 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 { range: (0.0..=0.0), hints, diff --git a/crates/egui_plot/src/items/mod.rs b/crates/egui_plot/src/items/mod.rs index 56098b36699..0470d955965 100644 --- a/crates/egui_plot/src/items/mod.rs +++ b/crates/egui_plot/src/items/mod.rs @@ -81,7 +81,7 @@ pub trait PlotItem { shapes: &mut Vec, cursors: &mut Vec, plot: &PlotConfig<'_>, - label_formatter: &LabelFormatter, + label_formatter: &LabelFormatter<'_>, ) { let points = match self.geometry() { PlotGeometry::Points(points) => points, @@ -1735,7 +1735,7 @@ impl PlotItem for BarChart { shapes: &mut Vec, cursors: &mut Vec, plot: &PlotConfig<'_>, - _: &LabelFormatter, + _: &LabelFormatter<'_>, ) { let bar = &self.bars[elem.index]; @@ -1909,7 +1909,7 @@ impl PlotItem for BoxPlot { shapes: &mut Vec, cursors: &mut Vec, plot: &PlotConfig<'_>, - _: &LabelFormatter, + _: &LabelFormatter<'_>, ) { let box_plot = &self.boxes[elem.index]; @@ -2033,7 +2033,7 @@ pub(super) fn rulers_at_value( plot: &PlotConfig<'_>, shapes: &mut Vec, cursors: &mut Vec, - label_formatter: &LabelFormatter, + label_formatter: &LabelFormatter<'_>, ) { if plot.show_x { cursors.push(Cursor::Vertical { x: value.x }); diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index 6b9d622d6e3..164710bef26 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -37,22 +37,22 @@ use axis::AxisWidget; use items::{horizontal_line, rulers_color, vertical_line}; use legend::LegendWidget; -type LabelFormatterFn = dyn Fn(&str, &PlotPoint) -> String; -pub type LabelFormatter = Option>; +type LabelFormatterFn<'a> = dyn Fn(&str, &PlotPoint) -> String + 'a; +pub type LabelFormatter<'a> = Option>>; -type GridSpacerFn = dyn Fn(GridInput) -> Vec; -type GridSpacer = Box; +type GridSpacerFn<'a> = dyn Fn(GridInput) -> Vec + 'a; +type GridSpacer<'a> = Box>; -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`]. -pub struct CoordinatesFormatter { - function: Box, +pub struct CoordinatesFormatter<'a> { + function: Box>, } -impl CoordinatesFormatter { +impl<'a> CoordinatesFormatter<'a> { /// 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 { function: Box::new(function), } @@ -72,7 +72,7 @@ impl CoordinatesFormatter { } } -impl Default for CoordinatesFormatter { +impl Default for CoordinatesFormatter<'_> { fn default() -> Self { Self::with_decimals(3) } @@ -143,7 +143,7 @@ pub struct PlotResponse { /// 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: Option, @@ -170,24 +170,24 @@ pub struct Plot { show_x: bool, show_y: bool, - label_formatter: LabelFormatter, - coordinates_formatter: Option<(Corner, CoordinatesFormatter)>, - x_axes: Vec, // default x axes - y_axes: Vec, // default y axes + label_formatter: LabelFormatter<'a>, + coordinates_formatter: Option<(Corner, CoordinatesFormatter<'a>)>, + x_axes: Vec>, // default x axes + y_axes: Vec>, // default y axes legend_config: Option, show_background: bool, show_axes: Vec2b, show_grid: Vec2b, grid_spacing: Rangef, - grid_spacers: [GridSpacer; 2], + grid_spacers: [GridSpacer<'a>; 2], sharp_grid_lines: bool, clamp_grid: bool, sense: Sense, } -impl Plot { +impl<'a> Plot<'a> { /// Give a unique id for each plot within the same [`Ui`]. pub fn new(id_source: impl std::hash::Hash) -> Self { Self { @@ -405,7 +405,7 @@ impl Plot { /// ``` pub fn label_formatter( mut self, - label_formatter: impl Fn(&str, &PlotPoint) -> String + 'static, + label_formatter: impl Fn(&str, &PlotPoint) -> String + 'a, ) -> Self { self.label_formatter = Some(Box::new(label_formatter)); self @@ -415,7 +415,7 @@ impl Plot { pub fn coordinates_formatter( mut self, position: Corner, - formatter: CoordinatesFormatter, + formatter: CoordinatesFormatter<'a>, ) -> Self { self.coordinates_formatter = Some((position, formatter)); self @@ -452,7 +452,7 @@ impl Plot { /// /// There are helpers for common cases, see [`log_grid_spacer`] and [`uniform_grid_spacer`]. #[inline] - pub fn x_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec + 'static) -> Self { + pub fn x_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec + 'a) -> Self { self.grid_spacers[0] = Box::new(spacer); self } @@ -461,7 +461,7 @@ impl Plot { /// /// See [`Self::x_grid_spacer`] for explanation. #[inline] - pub fn y_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec + 'static) -> Self { + pub fn y_grid_spacer(mut self, spacer: impl Fn(GridInput) -> Vec + 'a) -> Self { self.grid_spacers[1] = Box::new(spacer); self } @@ -662,7 +662,7 @@ impl Plot { /// * currently shown range on this axis. pub fn x_axis_formatter( mut self, - fmt: impl Fn(GridMark, usize, &RangeInclusive) -> String + 'static, + fmt: impl Fn(GridMark, usize, &RangeInclusive) -> String + 'a, ) -> Self { if let Some(main) = self.x_axes.first_mut() { main.formatter = Arc::new(fmt); @@ -678,7 +678,7 @@ impl Plot { /// * currently shown range on this axis. pub fn y_axis_formatter( mut self, - fmt: impl Fn(GridMark, usize, &RangeInclusive) -> String + 'static, + fmt: impl Fn(GridMark, usize, &RangeInclusive) -> String + 'a, ) -> Self { if let Some(main) = self.y_axes.first_mut() { 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. #[inline] - pub fn custom_x_axes(mut self, hints: Vec) -> Self { + pub fn custom_x_axes(mut self, hints: Vec>) -> Self { self.x_axes = hints; self } @@ -712,17 +712,21 @@ impl Plot { /// /// More than one axis may be specified. The first specified axis is considered the main axis. #[inline] - pub fn custom_y_axes(mut self, hints: Vec) -> Self { + pub fn custom_y_axes(mut self, hints: Vec>) -> Self { self.y_axes = hints; self } /// Interact with and add items to the plot and finally draw it. - pub fn show(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi) -> R) -> PlotResponse { + pub fn show( + self, + ui: &mut Ui, + build_fn: impl FnOnce(&mut PlotUi) -> R + 'a, + ) -> PlotResponse { self.show_dyn(ui, Box::new(build_fn)) } - fn show_dyn<'a, R>( + fn show_dyn( self, ui: &mut Ui, build_fn: Box R + 'a>, @@ -1246,12 +1250,12 @@ impl Plot { } /// Returns the rect left after adding axes. -fn axis_widgets( +fn axis_widgets<'a>( mem: Option<&PlotMemory>, show_axes: Vec2b, complete_rect: Rect, - [x_axes, y_axes]: [&[AxisHints]; 2], -) -> ([Vec; 2], Rect) { + [x_axes, y_axes]: [&'a [AxisHints<'a>]; 2], +) -> ([Vec>; 2], Rect) { // Next we want to create this layout. // Indices are only examples. // @@ -1275,8 +1279,8 @@ fn axis_widgets( // + +--------------------+---+ // - let mut x_axis_widgets = Vec::::new(); - let mut y_axis_widgets = Vec::::new(); + let mut x_axis_widgets = Vec::>::new(); + let mut y_axis_widgets = Vec::>::new(); // Will shrink as we add more axes. 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. /// 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 step_sizes = move |input: GridInput| -> Vec { // 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. /// 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 { let bounds = input.bounds; 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>, show_x: bool, show_y: bool, - label_formatter: LabelFormatter, - coordinates_formatter: Option<(Corner, CoordinatesFormatter)>, + label_formatter: LabelFormatter<'a>, + coordinates_formatter: Option<(Corner, CoordinatesFormatter<'a>)>, // axis_formatters: [AxisFormatter; 2], transform: PlotTransform, show_grid: Vec2b, grid_spacing: Rangef, - grid_spacers: [GridSpacer; 2], + grid_spacers: [GridSpacer<'a>; 2], draw_cursor_x: bool, draw_cursor_y: bool, draw_cursors: Vec, @@ -1466,7 +1470,7 @@ struct PreparedPlot { clamp_grid: bool, } -impl PreparedPlot { +impl<'a> PreparedPlot<'a> { fn ui(self, ui: &mut Ui, response: &Response) -> (Vec, Option) { let mut axes_shapes = Vec::new();