Plot boxed zoom with secondary mouse button (#1188)
This commit is contained in:
parent
c6ac1827f6
commit
869d556335
4 changed files with 83 additions and 0 deletions
|
@ -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 🔧
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -432,6 +455,7 @@ impl Plot {
|
|||
mut hovered_entry,
|
||||
mut hidden_items,
|
||||
last_screen_transform,
|
||||
mut last_click_pos_for_zoom,
|
||||
..
|
||||
} = memory;
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.;
|
||||
|
|
|
@ -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") {
|
||||
|
|
Loading…
Reference in a new issue