From e70d66d21397febd49d9a1aeb6db250a0adc2f50 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Wed, 9 Feb 2022 16:43:18 +0100 Subject: [PATCH 1/8] show pointer coordinates and improve formatters --- egui/src/widgets/plot/items/mod.rs | 10 +- egui/src/widgets/plot/mod.rs | 118 ++++++++++++++++++----- egui/src/widgets/plot/transform.rs | 4 + egui_demo_lib/src/apps/demo/plot_demo.rs | 18 +++- 4 files changed, 118 insertions(+), 32 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index 983d29df095..b2d4f35e976 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -7,7 +7,7 @@ use epaint::Mesh; use crate::*; -use super::{CustomLabelFuncRef, PlotBounds, ScreenTransform}; +use super::{LabelFormatter, PlotBounds, ScreenTransform}; use rect_elem::*; use values::{ClosestElem, PlotGeometry}; @@ -66,7 +66,7 @@ pub(super) trait PlotItem { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - custom_label_func: &CustomLabelFuncRef, + custom_label_func: &LabelFormatter, ) { let points = match self.geometry() { PlotGeometry::Points(points) => points, @@ -1380,7 +1380,7 @@ impl PlotItem for BarChart { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - _: &CustomLabelFuncRef, + _: &LabelFormatter, ) { let bar = &self.bars[elem.index]; @@ -1522,7 +1522,7 @@ impl PlotItem for BoxPlot { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - _: &CustomLabelFuncRef, + _: &LabelFormatter, ) { let box_plot = &self.boxes[elem.index]; @@ -1643,7 +1643,7 @@ pub(super) fn rulers_at_value( name: &str, plot: &PlotConfig<'_>, shapes: &mut Vec, - custom_label_func: &CustomLabelFuncRef, + custom_label_func: &LabelFormatter, ) { let line_color = rulers_color(plot.ui); if plot.show_x { diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 647c1b4152a..ae63c4b91fa 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -1,6 +1,6 @@ //! Simple plotting library. -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, ops::RangeInclusive, rc::Rc}; use crate::*; use epaint::ahash::AHashSet; @@ -20,12 +20,43 @@ mod items; mod legend; mod transform; -type CustomLabelFunc = dyn Fn(&str, &Value) -> String; -type CustomLabelFuncRef = Option>; - -type AxisFormatterFn = dyn Fn(f64) -> String; +type LabelFormatterFn = dyn Fn(&str, &Value) -> String; +type LabelFormatter = Option>; +type AxisFormatterFn = dyn Fn(f64, &RangeInclusive) -> String; type AxisFormatter = Option>; +/// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`]. +pub struct CoordinatesFormatter { + function: Box String>, +} + +impl CoordinatesFormatter { + pub fn new(function: impl Fn(&Value) -> String + 'static) -> Self { + Self { + function: Box::new(function), + } + } + + /// Show a fixed number of decimal places. + pub fn with_precision(precision: usize) -> Self { + Self { + function: Box::new(move |value| { + format!("x: {:.p$}\ny: {:.p$}", value.x, value.y, p = precision) + }), + } + } + + fn format(&self, value: &Value) -> String { + (self.function)(value) + } +} + +impl Default for CoordinatesFormatter { + fn default() -> Self { + Self::with_precision(3) + } +} + // ---------------------------------------------------------------------------- /// Information about the plot that has to persist between frames. @@ -146,7 +177,8 @@ pub struct Plot { show_x: bool, show_y: bool, - custom_label_func: CustomLabelFuncRef, + label_formatter: LabelFormatter, + coordinates_formatter: Option<(Corner, CoordinatesFormatter)>, axis_formatters: [AxisFormatter; 2], legend_config: Option, show_background: bool, @@ -177,7 +209,8 @@ impl Plot { show_x: true, show_y: true, - custom_label_func: None, + label_formatter: None, + coordinates_formatter: None, axis_formatters: [None, None], // [None; 2] requires Copy legend_config: None, show_background: true, @@ -294,34 +327,50 @@ impl Plot { /// .show(ui, |plot_ui| plot_ui.line(line)); /// # }); /// ``` - pub fn custom_label_func( + pub fn label_formatter( + mut self, + label_formatter: impl Fn(&str, &Value) -> String + 'static, + ) -> Self { + self.label_formatter = Some(Box::new(label_formatter)); + self + } + + /// Show the pointer coordinates in the plot. + pub fn coordinates_formatter( mut self, - custom_label_func: impl Fn(&str, &Value) -> String + 'static, + position: Corner, + formatter: CoordinatesFormatter, ) -> Self { - self.custom_label_func = Some(Box::new(custom_label_func)); + self.coordinates_formatter = Some((position, formatter)); self } - /// Provide a function to customize the labels for the X axis. + /// Provide a function to customize the labels for the X axis based on the current value range. /// /// This is useful for custom input domains, e.g. date/time. /// /// If axis labels should not appear for certain values or beyond a certain zoom/resolution, /// the formatter function can return empty strings. This is also useful if your domain is /// discrete (e.g. only full days in a calendar). - pub fn x_axis_formatter(mut self, func: impl Fn(f64) -> String + 'static) -> Self { + pub fn x_axis_formatter( + mut self, + func: impl Fn(f64, &RangeInclusive) -> String + 'static, + ) -> Self { self.axis_formatters[0] = Some(Box::new(func)); self } - /// Provide a function to customize the labels for the Y axis. + /// Provide a function to customize the labels for the Y axis based on the current value range. /// /// This is useful for custom value representation, e.g. percentage or units. /// /// If axis labels should not appear for certain values or beyond a certain zoom/resolution, /// the formatter function can return empty strings. This is also useful if your Y values are /// discrete (e.g. only integers). - pub fn y_axis_formatter(mut self, func: impl Fn(f64) -> String + 'static) -> Self { + pub fn y_axis_formatter( + mut self, + func: impl Fn(f64, &RangeInclusive) -> String + 'static, + ) -> Self { self.axis_formatters[1] = Some(Box::new(func)); self } @@ -388,7 +437,8 @@ impl Plot { view_aspect, mut show_x, mut show_y, - custom_label_func, + label_formatter, + coordinates_formatter, axis_formatters, legend_config, show_background, @@ -630,7 +680,8 @@ impl Plot { items, show_x, show_y, - custom_label_func, + label_formatter, + coordinates_formatter, axis_formatters, show_axes, transform: transform.clone(), @@ -849,7 +900,8 @@ struct PreparedPlot { items: Vec>, show_x: bool, show_y: bool, - custom_label_func: CustomLabelFuncRef, + label_formatter: LabelFormatter, + coordinates_formatter: Option<(Corner, CoordinatesFormatter)>, axis_formatters: [AxisFormatter; 2], show_axes: [bool; 2], transform: ScreenTransform, @@ -877,7 +929,24 @@ impl PreparedPlot { self.hover(ui, pointer, &mut shapes); } - ui.painter().sub_region(*transform.frame()).extend(shapes); + let painter = ui.painter().sub_region(*transform.frame()); + painter.extend(shapes); + + if let Some((corner, formatter)) = self.coordinates_formatter.as_ref() { + if let Some(pointer) = response.hover_pos() { + let font_id = TextStyle::Monospace.resolve(ui.style()); + let coordinate = transform.value_from_position(pointer); + let text = formatter.format(&coordinate); + let padded_frame = transform.frame().shrink(4.0); + let (anchor, position) = match corner { + Corner::LeftTop => (Align2::LEFT_TOP, padded_frame.left_top()), + Corner::RightTop => (Align2::RIGHT_TOP, padded_frame.right_top()), + Corner::LeftBottom => (Align2::LEFT_BOTTOM, padded_frame.left_bottom()), + Corner::RightBottom => (Align2::RIGHT_BOTTOM, padded_frame.right_bottom()), + }; + painter.text(position, anchor, text, font_id, ui.visuals().text_color()); + } + } } fn paint_axis(&self, ui: &Ui, axis: usize, shapes: &mut Vec) { @@ -888,6 +957,11 @@ impl PreparedPlot { } = self; let bounds = transform.bounds(); + let axis_range = match axis { + 0 => bounds.range_x(), + 1 => bounds.range_y(), + _ => panic!("Axis {} does not exist.", axis), + }; let font_id = TextStyle::Body.resolve(ui.style()); @@ -947,7 +1021,7 @@ impl PreparedPlot { let color = color_from_alpha(ui, text_alpha); let text: String = if let Some(formatter) = axis_formatters[axis].as_deref() { - formatter(value_main) + formatter(value_main, &axis_range) } else { emath::round_to_decimals(value_main, 5).to_string() // hack }; @@ -982,7 +1056,7 @@ impl PreparedPlot { transform, show_x, show_y, - custom_label_func, + label_formatter, items, .. } = self; @@ -1012,10 +1086,10 @@ impl PreparedPlot { }; if let Some((item, elem)) = closest { - item.on_hover(elem, shapes, &plot, custom_label_func); + item.on_hover(elem, shapes, &plot, label_formatter); } else { let value = transform.value_from_position(pointer); - items::rulers_at_value(pointer, value, "", &plot, shapes, custom_label_func); + items::rulers_at_value(pointer, value, "", &plot, shapes, label_formatter); } } } diff --git a/egui/src/widgets/plot/transform.rs b/egui/src/widgets/plot/transform.rs index c208bc6e2c2..759a26d80ce 100644 --- a/egui/src/widgets/plot/transform.rs +++ b/egui/src/widgets/plot/transform.rs @@ -120,6 +120,10 @@ impl PlotBounds { self.min[0]..=self.max[0] } + pub(crate) fn range_y(&self) -> RangeInclusive { + self.min[1]..=self.max[1] + } + pub(crate) fn make_x_symmetrical(&mut self) { let x_abs = self.min[0].abs().max(self.max[0].abs()); self.min[0] = -x_abs; diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index fb0ab761c18..58003b47455 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -2,8 +2,9 @@ use std::f64::consts::TAU; use egui::*; use plot::{ - Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, Corner, HLine, Legend, Line, LineStyle, - MarkerShape, Plot, PlotImage, Points, Polygon, Text, VLine, Value, Values, + Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner, HLine, + Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, Points, Polygon, Text, VLine, Value, + Values, }; #[derive(PartialEq)] @@ -14,6 +15,7 @@ struct LineDemo { circle_center: Pos2, square: bool, proportional: bool, + coordinates: bool, line_style: LineStyle, } @@ -26,6 +28,7 @@ impl Default for LineDemo { circle_center: Pos2::new(0.0, 0.0), square: false, proportional: true, + coordinates: true, line_style: LineStyle::Solid, } } @@ -41,6 +44,7 @@ impl LineDemo { square, proportional, line_style, + coordinates, .. } = self; @@ -76,6 +80,8 @@ impl LineDemo { .on_hover_text("Always keep the viewport square."); ui.checkbox(proportional, "Proportional data axes") .on_hover_text("Tick are the same size on both axes."); + ui.checkbox(coordinates, "Show coordinates") + .on_hover_text("Can take a custom formatting function."); ComboBox::from_label("Line style") .selected_text(line_style.to_string()) @@ -151,6 +157,9 @@ impl Widget for &mut LineDemo { if self.proportional { plot = plot.data_aspect(1.0); } + if self.coordinates { + plot = plot.coordinates_formatter(Corner::LeftBottom, CoordinatesFormatter::default()); + } plot.show(ui, |plot_ui| { plot_ui.line(self.circle()); plot_ui.line(self.sin()); @@ -595,7 +604,7 @@ impl ChartsDemo { .name("Set 4") .stack_on(&[&chart1, &chart2, &chart3]); - let mut x_fmt: fn(f64) -> String = |val| { + let mut x_fmt: fn(f64, &std::ops::RangeInclusive) -> String = |val, _range| { if val >= 0.0 && val <= 4.0 && is_approx_integer(val) { // Only label full days from 0 to 4 format!("Day {}", val) @@ -605,7 +614,7 @@ impl ChartsDemo { } }; - let mut y_fmt: fn(f64) -> String = |val| { + let mut y_fmt: fn(f64, &std::ops::RangeInclusive) -> String = |val, _range| { let percent = 100.0 * val; if is_approx_integer(percent) && !is_approx_zero(percent) { @@ -753,7 +762,6 @@ impl super::View for PlotDemo { egui::reset_button(ui, self); ui.collapsing("Instructions", |ui| { ui.label("Pan by dragging, or scroll (+ shift = horizontal)."); - ui.label("Box zooming: Right click to zoom in and zoom out using a selection."); if cfg!(target_arch = "wasm32") { ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture."); } else if cfg!(target_os = "macos") { From 3e17949b3b4d0eb26167073a61f51a5f94738998 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Wed, 9 Feb 2022 17:02:25 +0100 Subject: [PATCH 2/8] rename variable and fix test --- egui/src/widgets/plot/items/mod.rs | 8 ++++---- egui/src/widgets/plot/mod.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/egui/src/widgets/plot/items/mod.rs b/egui/src/widgets/plot/items/mod.rs index b2d4f35e976..2a00a285d67 100644 --- a/egui/src/widgets/plot/items/mod.rs +++ b/egui/src/widgets/plot/items/mod.rs @@ -66,7 +66,7 @@ pub(super) trait PlotItem { elem: ClosestElem, shapes: &mut Vec, plot: &PlotConfig<'_>, - custom_label_func: &LabelFormatter, + label_formatter: &LabelFormatter, ) { let points = match self.geometry() { PlotGeometry::Points(points) => points, @@ -89,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, custom_label_func); + rulers_at_value(pointer, value, self.name(), plot, shapes, label_formatter); } } @@ -1643,7 +1643,7 @@ pub(super) fn rulers_at_value( name: &str, plot: &PlotConfig<'_>, shapes: &mut Vec, - custom_label_func: &LabelFormatter, + label_formatter: &LabelFormatter, ) { let line_color = rulers_color(plot.ui); if plot.show_x { @@ -1663,7 +1663,7 @@ 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 let Some(custom_label) = custom_label_func { + if let Some(custom_label) = label_formatter { custom_label(name, &value) } else if plot.show_x && plot.show_y { format!( diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index ae63c4b91fa..dd5db5b7a7a 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -317,7 +317,7 @@ impl Plot { /// }); /// let line = Line::new(Values::from_values_iter(sin)); /// Plot::new("my_plot").view_aspect(2.0) - /// .custom_label_func(|name, value| { + /// .label_formatter(|name, value| { /// if !name.is_empty() { /// format!("{}: {:.*}%", name, 1, value.y).to_string() /// } else { From c7d572f206f01af60d9ed7186389db0f7ed91201 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 10 Feb 2022 11:14:54 +0100 Subject: [PATCH 3/8] provide bounds and round to decimals --- egui/src/widgets/plot/mod.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index dd5db5b7a7a..64cf08a3ff5 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -27,11 +27,12 @@ type AxisFormatter = Option>; /// Specifies the coordinates formatting when passed to [`Plot::coordinates_formatter`]. pub struct CoordinatesFormatter { - function: Box String>, + function: Box String>, } impl CoordinatesFormatter { - pub fn new(function: impl Fn(&Value) -> String + 'static) -> Self { + /// Create a new formatter based on the pointer coordinate and the plot bounds. + pub fn new(function: impl Fn(&Value, &PlotBounds) -> String + 'static) -> Self { Self { function: Box::new(function), } @@ -40,14 +41,18 @@ impl CoordinatesFormatter { /// Show a fixed number of decimal places. pub fn with_precision(precision: usize) -> Self { Self { - function: Box::new(move |value| { - format!("x: {:.p$}\ny: {:.p$}", value.x, value.y, p = precision) + function: Box::new(move |value, _| { + format!( + "x: {}\ny: {}", + emath::round_to_decimals(value.x, precision).to_string(), + emath::round_to_decimals(value.y, precision).to_string(), + ) }), } } - fn format(&self, value: &Value) -> String { - (self.function)(value) + fn format(&self, value: &Value, bounds: &PlotBounds) -> String { + (self.function)(value, bounds) } } @@ -936,7 +941,7 @@ impl PreparedPlot { if let Some(pointer) = response.hover_pos() { let font_id = TextStyle::Monospace.resolve(ui.style()); let coordinate = transform.value_from_position(pointer); - let text = formatter.format(&coordinate); + let text = formatter.format(&coordinate, transform.bounds()); let padded_frame = transform.frame().shrink(4.0); let (anchor, position) = match corner { Corner::LeftTop => (Align2::LEFT_TOP, padded_frame.left_top()), From d03c242240ab608aa6999857c94eac0bbbf8f097 Mon Sep 17 00:00:00 2001 From: Sven Niederberger <73159570+s-nie@users.noreply.github.com> Date: Thu, 10 Feb 2022 15:49:24 +0100 Subject: [PATCH 4/8] Update egui/src/widgets/plot/mod.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 64cf08a3ff5..6f7465c53cc 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -39,7 +39,7 @@ impl CoordinatesFormatter { } /// Show a fixed number of decimal places. - pub fn with_precision(precision: usize) -> Self { + pub fn with_decimals(num_decimals: usize) -> Self { Self { function: Box::new(move |value, _| { format!( From ad8431f817d7cb8e90fc4ba533eb4a6a452d9e4b Mon Sep 17 00:00:00 2001 From: Sven Niederberger <73159570+s-nie@users.noreply.github.com> Date: Thu, 10 Feb 2022 15:49:30 +0100 Subject: [PATCH 5/8] Update egui/src/widgets/plot/mod.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 6f7465c53cc..719f745a9dc 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -350,7 +350,7 @@ impl Plot { self } - /// Provide a function to customize the labels for the X axis based on the current value range. + /// Provide a function to customize the labels for the X axis based on the current visible value range. /// /// This is useful for custom input domains, e.g. date/time. /// From f6b29153eb709847c1029f5cfd59aa2b1a55e9d4 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 10 Feb 2022 15:56:06 +0100 Subject: [PATCH 6/8] apply suggestions --- egui/src/widgets/plot/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 719f745a9dc..b6f080960a6 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -42,11 +42,7 @@ impl CoordinatesFormatter { pub fn with_decimals(num_decimals: usize) -> Self { Self { function: Box::new(move |value, _| { - format!( - "x: {}\ny: {}", - emath::round_to_decimals(value.x, precision).to_string(), - emath::round_to_decimals(value.y, precision).to_string(), - ) + format!("x: {:.d$}\ny: {:.d$}", value.x, value.y, d = num_decimals) }), } } @@ -58,7 +54,7 @@ impl CoordinatesFormatter { impl Default for CoordinatesFormatter { fn default() -> Self { - Self::with_precision(3) + Self::with_decimals(3) } } From b24e0f1e3eba060a6484679068f456b03321d889 Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Thu, 10 Feb 2022 16:11:51 +0100 Subject: [PATCH 7/8] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c38a1dc49d..e47b439f3c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Added linked axis support for plots via `plot::LinkedAxisGroup` ([#1184](https://github.com/emilk/egui/pull/1184)). * Added `Response::on_hover_text_at_pointer` as a convenience akin to `Response::on_hover_text` ([1179](https://github.com/emilk/egui/pull/1179)). * Added `ui.weak(text)`. +* Added plot pointer coordinates with `Plot::coordinates_formatter`. ([#1235](https://github.com/emilk/egui/pull/1235)) ### Changed 🔧 * ⚠️ `Context::input` and `Ui::input` now locks a mutex. This can lead to a dead-lock is used in an `if let` binding! @@ -43,6 +44,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Replaced Frame's `margin: Vec2` with `margin: Margin`, allowing for different margins on opposing sides ([#1219](https://github.com/emilk/egui/pull/1219)). * `Plot::highlight` now takes a `bool` argument ([#1159](https://github.com/emilk/egui/pull/1159)). * `ScrollArea::show` now returns a `ScrollAreaOutput`, so you might need to add `.inner` after the call to it ([#1166](https://github.com/emilk/egui/pull/1166)). +* Renamed `Plot::custom_label_func` to `Plot::label_formatter` ([#1235](https://github.com/emilk/egui/pull/1235)) ### Fixed 🐛 * Context menus now respects the theme ([#1043](https://github.com/emilk/egui/pull/1043)). From 8cc536346427ff9e484558c9b144d10c288f3e7d Mon Sep 17 00:00:00 2001 From: Sven Niederberger Date: Fri, 11 Feb 2022 08:13:10 +0100 Subject: [PATCH 8/8] bring back deleted line --- egui_demo_lib/src/apps/demo/plot_demo.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/egui_demo_lib/src/apps/demo/plot_demo.rs b/egui_demo_lib/src/apps/demo/plot_demo.rs index 58003b47455..bf845f094ec 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -762,6 +762,7 @@ impl super::View for PlotDemo { egui::reset_button(ui, self); ui.collapsing("Instructions", |ui| { ui.label("Pan by dragging, or scroll (+ shift = horizontal)."); + ui.label("Box zooming: Right click to zoom in and zoom out using a selection."); if cfg!(target_arch = "wasm32") { ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture."); } else if cfg!(target_os = "macos") {