From 1923382b5c2506e1946246d7780e7d43fdc7d2b1 Mon Sep 17 00:00:00 2001 From: chaignc Date: Mon, 31 Jan 2022 18:33:58 +0100 Subject: [PATCH 1/7] add boxed zoom to plot --- egui/src/widgets/plot/mod.rs | 67 ++++++++++++++++++++++++++++++ egui/src/widgets/plot/transform.rs | 4 ++ 2 files changed, 71 insertions(+) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 9550d98f0aa..c1294ad22eb 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -35,6 +35,8 @@ struct PlotMemory { hidden_items: AHashSet, min_auto_bounds: PlotBounds, last_screen_transform: ScreenTransform, + /// Allows to remember the first click position when performing a boxed zoom + last_click_pos_for_zoom: Option, } impl PlotMemory { @@ -73,6 +75,8 @@ pub struct Plot { allow_drag: bool, min_auto_bounds: PlotBounds, margin_fraction: Vec2, + allow_boxed_zoom: bool, + boxed_zoom_pointer: PointerButton, min_size: Vec2, width: Option, @@ -101,6 +105,8 @@ impl Plot { allow_drag: true, min_auto_bounds: PlotBounds::NOTHING, margin_fraction: Vec2::splat(0.05), + allow_boxed_zoom: true, + boxed_zoom_pointer: PointerButton::Secondary, min_size: Vec2::splat(64.0), width: None, @@ -186,6 +192,18 @@ impl Plot { self } + /// Whether to allow zooming in the plot. Default: `true`. + pub fn allow_boxed_zoom(mut self, on: bool) -> Self { + self.allow_boxed_zoom = on; + self + } + + /// Config the pointer to use for boxed_zoom. Default: `Secondary` + pub fn boxed_zoom_pointer(mut self, boxed_zoom_pointer: PointerButton) -> Self { + self.boxed_zoom_pointer = boxed_zoom_pointer; + self + } + /// Whether to allow dragging in the plot to move the bounds. Default: `true`. pub fn allow_drag(mut self, on: bool) -> Self { self.allow_drag = on; @@ -289,6 +307,8 @@ impl Plot { center_y_axis, allow_zoom, allow_drag, + allow_boxed_zoom, + boxed_zoom_pointer, min_auto_bounds, margin_fraction, width, @@ -345,6 +365,7 @@ impl Plot { center_x_axis, center_y_axis, ), + last_click_pos_for_zoom: None }); // If the min bounds changed, recalculate everything. @@ -363,6 +384,7 @@ impl Plot { mut hovered_entry, mut hidden_items, last_screen_transform, + mut last_click_pos_for_zoom, .. } = memory; @@ -442,6 +464,45 @@ impl Plot { } // Zooming + let mut boxed_zoom_rect = None; + if allow_boxed_zoom { + // Save last click to allow boxed zooming + if response.drag_started() && response.dragged_by(boxed_zoom_pointer) { + // it would be best for egui that input has a memory of the last click pos because it's a common pattern + last_click_pos_for_zoom = response.hover_pos() + } + let box_start_pos = last_click_pos_for_zoom; + let box_end_pos = response.hover_pos(); + if let (Some(box_start_pos), Some(box_end_pos)) = (box_start_pos, box_end_pos) { + // while dragging prepare a Shape and draw it later on top of the plot + if response.dragged_by(boxed_zoom_pointer) { + response = response.on_hover_cursor(CursorIcon::ZoomIn); + let rect = epaint::Rect::from_two_pos(box_start_pos, box_end_pos); + boxed_zoom_rect = Some(( + epaint::RectShape::stroke(rect, 0.0, epaint::Stroke::new(4., Color32::DARK_BLUE)), // Outer stroke + epaint::RectShape::stroke(rect, 0.0, epaint::Stroke::new(2., Color32::WHITE)) // Inner stroke + )); + } + // when the click is release perform the zoom + if response.drag_released() { + let box_start_pos = transform.value_from_position(box_start_pos); + let box_end_pos = transform.value_from_position(box_end_pos); + let new_bounds = PlotBounds{ + min: [box_start_pos.x, box_end_pos.y], + max: [box_end_pos.x, box_start_pos.y] + }; + if new_bounds.is_valid() { + *transform.bounds_mut() = new_bounds; + auto_bounds = false; + } else { + auto_bounds = true; + } + // reset the boxed zoom state + last_click_pos_for_zoom = None; + } + } + } + if allow_zoom { if let Some(hover_pos) = response.hover_pos() { let zoom_factor = if data_aspect.is_some() { @@ -478,6 +539,11 @@ impl Plot { }; prepared.ui(ui, &response); + if let Some(boxed_zoom_rect) = boxed_zoom_rect { + ui.painter().sub_region(rect).add(boxed_zoom_rect.0); + ui.painter().sub_region(rect).add(boxed_zoom_rect.1); + } + if let Some(mut legend) = legend { ui.add(&mut legend); hidden_items = legend.get_hidden_items(); @@ -490,6 +556,7 @@ impl Plot { hidden_items, min_auto_bounds, last_screen_transform: transform, + last_click_pos_for_zoom }; memory.store(ui.ctx(), plot_id); diff --git a/egui/src/widgets/plot/transform.rs b/egui/src/widgets/plot/transform.rs index 53007950338..397a5b5c2fe 100644 --- a/egui/src/widgets/plot/transform.rs +++ b/egui/src/widgets/plot/transform.rs @@ -178,6 +178,10 @@ impl ScreenTransform { &self.bounds } + pub fn bounds_mut(&mut self) -> &mut PlotBounds { + &mut self.bounds + } + pub fn translate_bounds(&mut self, mut delta_pos: Vec2) { if self.x_centered { delta_pos.x = 0.; From 4d6c2ff85f52e7fba64412c40673bb03f188fabe Mon Sep 17 00:00:00 2001 From: chaignc Date: Mon, 31 Jan 2022 18:43:54 +0100 Subject: [PATCH 2/7] Add boxed_zoom to CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6454b4aa85..f954ce2a6cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Added `CollapsingHeader::icon` to override the default open/close icon using a custom function. ([1147](https://github.com/emilk/egui/pull/1147)). * Added `Plot::x_axis_formatter` and `Plot::y_axis_formatter` for custom axis labels ([#1130](https://github.com/emilk/egui/pull/1130)). * Added `ui.data()`, `ctx.data()`, `ctx.options()` and `ctx.tessellation_options()` ([#1175](https://github.com/emilk/egui/pull/1175)). +* Added `Plot::allow_boxed_zoom()`, `Plot::boxed_zoom_pointer()` for boxed zooming on plots. + ### Changed 🔧 * ⚠️ `Context::input` and `Ui::input` now locks a mutex. This can lead to a dead-lock is used in an `if let` binding! From 3377e80d67dcc093e9aeb822f155c0b75cd89699 Mon Sep 17 00:00:00 2001 From: nongiach Date: Mon, 31 Jan 2022 21:09:19 +0100 Subject: [PATCH 3/7] Update CHANGELOG.md add emlik update on changelog Co-authored-by: Emil Ernerfeldt --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f954ce2a6cf..0075c127e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w * Added `CollapsingHeader::icon` to override the default open/close icon using a custom function. ([1147](https://github.com/emilk/egui/pull/1147)). * Added `Plot::x_axis_formatter` and `Plot::y_axis_formatter` for custom axis labels ([#1130](https://github.com/emilk/egui/pull/1130)). * Added `ui.data()`, `ctx.data()`, `ctx.options()` and `ctx.tessellation_options()` ([#1175](https://github.com/emilk/egui/pull/1175)). -* Added `Plot::allow_boxed_zoom()`, `Plot::boxed_zoom_pointer()` for boxed zooming on plots. +* Added `Plot::allow_boxed_zoom()`, `Plot::boxed_zoom_pointer()` for boxed zooming on plots ([#1188](https://github.com/emilk/egui/pull/1188)). ### Changed 🔧 From 7aeddb24b97be034f39ccaf91e76ed2f1f02e6f1 Mon Sep 17 00:00:00 2001 From: nongiach Date: Mon, 31 Jan 2022 21:09:41 +0100 Subject: [PATCH 4/7] Update egui/src/widgets/plot/mod.rs add emlik update on comment in mod.rs Co-authored-by: Emil Ernerfeldt --- egui/src/widgets/plot/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index c1294ad22eb..2e9ee93f72e 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -192,7 +192,9 @@ impl Plot { self } - /// Whether to allow zooming in the plot. Default: `true`. + /// Whether to allow zooming in the plot by dragging out a box with the secondary mouse button. + /// + /// Default: `true`. pub fn allow_boxed_zoom(mut self, on: bool) -> Self { self.allow_boxed_zoom = on; self From acb28b92f034ff8f908015fcef7fdfc60c3aaa09 Mon Sep 17 00:00:00 2001 From: chaignc Date: Mon, 31 Jan 2022 21:19:53 +0100 Subject: [PATCH 5/7] rename boxed_zoom_button to boxed_zoom_pointer_button --- egui/src/widgets/plot/mod.rs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 2e9ee93f72e..5dd3683ff9b 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -76,7 +76,7 @@ pub struct Plot { min_auto_bounds: PlotBounds, margin_fraction: Vec2, allow_boxed_zoom: bool, - boxed_zoom_pointer: PointerButton, + boxed_zoom_pointer_button: PointerButton, min_size: Vec2, width: Option, @@ -106,7 +106,7 @@ impl Plot { min_auto_bounds: PlotBounds::NOTHING, margin_fraction: Vec2::splat(0.05), allow_boxed_zoom: true, - boxed_zoom_pointer: PointerButton::Secondary, + boxed_zoom_pointer_button: PointerButton::Secondary, min_size: Vec2::splat(64.0), width: None, @@ -201,8 +201,8 @@ impl Plot { } /// Config the pointer to use for boxed_zoom. Default: `Secondary` - pub fn boxed_zoom_pointer(mut self, boxed_zoom_pointer: PointerButton) -> Self { - self.boxed_zoom_pointer = boxed_zoom_pointer; + pub fn boxed_zoom_pointer_button(mut self, boxed_zoom_pointer_button: PointerButton) -> Self { + self.boxed_zoom_pointer_button = boxed_zoom_pointer_button; self } @@ -310,7 +310,7 @@ impl Plot { allow_zoom, allow_drag, allow_boxed_zoom, - boxed_zoom_pointer, + boxed_zoom_pointer_button: boxed_zoom_pointer, min_auto_bounds, margin_fraction, width, @@ -367,7 +367,7 @@ impl Plot { center_x_axis, center_y_axis, ), - last_click_pos_for_zoom: None + last_click_pos_for_zoom: None, }); // If the min bounds changed, recalculate everything. @@ -472,7 +472,7 @@ impl Plot { if response.drag_started() && response.dragged_by(boxed_zoom_pointer) { // it would be best for egui that input has a memory of the last click pos because it's a common pattern last_click_pos_for_zoom = response.hover_pos() - } + } let box_start_pos = last_click_pos_for_zoom; let box_end_pos = response.hover_pos(); if let (Some(box_start_pos), Some(box_end_pos)) = (box_start_pos, box_end_pos) { @@ -481,17 +481,25 @@ impl Plot { response = response.on_hover_cursor(CursorIcon::ZoomIn); let rect = epaint::Rect::from_two_pos(box_start_pos, box_end_pos); boxed_zoom_rect = Some(( - epaint::RectShape::stroke(rect, 0.0, epaint::Stroke::new(4., Color32::DARK_BLUE)), // Outer stroke - epaint::RectShape::stroke(rect, 0.0, epaint::Stroke::new(2., Color32::WHITE)) // Inner stroke + epaint::RectShape::stroke( + rect, + 0.0, + epaint::Stroke::new(4., Color32::DARK_BLUE), + ), // Outer stroke + epaint::RectShape::stroke( + rect, + 0.0, + epaint::Stroke::new(2., Color32::WHITE), + ), // Inner stroke )); } // when the click is release perform the zoom if response.drag_released() { let box_start_pos = transform.value_from_position(box_start_pos); let box_end_pos = transform.value_from_position(box_end_pos); - let new_bounds = PlotBounds{ + let new_bounds = PlotBounds { min: [box_start_pos.x, box_end_pos.y], - max: [box_end_pos.x, box_start_pos.y] + max: [box_end_pos.x, box_start_pos.y], }; if new_bounds.is_valid() { *transform.bounds_mut() = new_bounds; @@ -558,7 +566,7 @@ impl Plot { hidden_items, min_auto_bounds, last_screen_transform: transform, - last_click_pos_for_zoom + last_click_pos_for_zoom, }; memory.store(ui.ctx(), plot_id); From eba9f1c84e2c878a73a17d51ca316d1653f5b2ea Mon Sep 17 00:00:00 2001 From: chaignc Date: Mon, 31 Jan 2022 21:44:47 +0100 Subject: [PATCH 6/7] update lib demo with instruction for box zomming --- 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 931bc40a779..abdce54f7c9 100644 --- a/egui_demo_lib/src/apps/demo/plot_demo.rs +++ b/egui_demo_lib/src/apps/demo/plot_demo.rs @@ -679,6 +679,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") { From 541713e24852a93647d7177e77ef3634c17892aa Mon Sep 17 00:00:00 2001 From: chaignc Date: Mon, 31 Jan 2022 21:48:14 +0100 Subject: [PATCH 7/7] fix cargo clippy --- egui/src/widgets/plot/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/egui/src/widgets/plot/mod.rs b/egui/src/widgets/plot/mod.rs index 5dd3683ff9b..0e56674d80e 100644 --- a/egui/src/widgets/plot/mod.rs +++ b/egui/src/widgets/plot/mod.rs @@ -200,7 +200,7 @@ impl Plot { self } - /// Config the pointer to use for boxed_zoom. Default: `Secondary` + /// Config the button pointer to use for boxed zooming. Default: `Secondary` pub fn boxed_zoom_pointer_button(mut self, boxed_zoom_pointer_button: PointerButton) -> Self { self.boxed_zoom_pointer_button = boxed_zoom_pointer_button; self @@ -471,7 +471,7 @@ impl Plot { // Save last click to allow boxed zooming if response.drag_started() && response.dragged_by(boxed_zoom_pointer) { // it would be best for egui that input has a memory of the last click pos because it's a common pattern - last_click_pos_for_zoom = response.hover_pos() + last_click_pos_for_zoom = response.hover_pos(); } let box_start_pos = last_click_pos_for_zoom; let box_end_pos = response.hover_pos();