Skip to content

Commit

Permalink
Plot boxed zoom with secondary mouse button (#1188)
Browse files Browse the repository at this point in the history
  • Loading branch information
nongiach authored Feb 2, 2022
1 parent c6ac182 commit 869d556
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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 ([#1188](/~https://github.com/emilk/egui/pull/1188)).
* Added linked axis support for plots via `plot::LinkedAxisGroup` ([#1184](/~https://github.com/emilk/egui/pull/1184)).

### Changed 🔧
Expand Down
77 changes: 77 additions & 0 deletions egui/src/widgets/plot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ struct PlotMemory {
hidden_items: AHashSet<String>,
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<Pos2>,
}

impl PlotMemory {
Expand Down Expand Up @@ -132,6 +134,8 @@ pub struct Plot {
allow_drag: bool,
min_auto_bounds: PlotBounds,
margin_fraction: Vec2,
allow_boxed_zoom: bool,
boxed_zoom_pointer_button: PointerButton,
linked_axes: Option<LinkedAxisGroup>,

min_size: Vec2,
Expand Down Expand Up @@ -161,6 +165,8 @@ impl Plot {
allow_drag: true,
min_auto_bounds: PlotBounds::NOTHING,
margin_fraction: Vec2::splat(0.05),
allow_boxed_zoom: true,
boxed_zoom_pointer_button: PointerButton::Secondary,
linked_axes: None,

min_size: Vec2::splat(64.0),
Expand Down Expand Up @@ -247,6 +253,20 @@ impl Plot {
self
}

/// 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
}

/// 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
}

/// 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;
Expand Down Expand Up @@ -357,6 +377,8 @@ impl Plot {
center_y_axis,
allow_zoom,
allow_drag,
allow_boxed_zoom,
boxed_zoom_pointer_button: boxed_zoom_pointer,
min_auto_bounds,
margin_fraction,
width,
Expand Down Expand Up @@ -414,6 +436,7 @@ impl Plot {
center_x_axis,
center_y_axis,
),
last_click_pos_for_zoom: None,
});

// If the min bounds changed, recalculate everything.
Expand All @@ -432,6 +455,7 @@ impl Plot {
mut hovered_entry,
mut hidden_items,
last_screen_transform,
mut last_click_pos_for_zoom,
..
} = memory;

Expand Down Expand Up @@ -530,6 +554,53 @@ 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() {
Expand Down Expand Up @@ -566,6 +637,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();
Expand All @@ -582,6 +658,7 @@ impl Plot {
hidden_items,
min_auto_bounds,
last_screen_transform: transform,
last_click_pos_for_zoom,
};
memory.store(ui.ctx(), plot_id);

Expand Down
4 changes: 4 additions & 0 deletions egui/src/widgets/plot/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.;
Expand Down
1 change: 1 addition & 0 deletions egui_demo_lib/src/apps/demo/plot_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,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") {
Expand Down

0 comments on commit 869d556

Please sign in to comment.