Plot widget - allow disabling zoom and drag for x and y separately (#2901)

* Set whether zooming allowed for x and y separately

* Set whether dragging allowed for x and y separately

* Add disclaimers about interaction with data_aspect

* Show zoom & drag behavior in plot demo/charts instead of context menu demo

* Code review suggestions - use AxisBools::any()

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>

* Simplify allow_drag and allow_zoom APIs to take in Into<AxisBools>

* Remove unnecessary using...

* Remove unrelated change

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Jackson Kruger 2023-04-18 10:58:19 -07:00 committed by GitHub
parent 902bcfe6aa
commit 438f6eafc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 28 deletions

View File

@ -7,7 +7,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
## Unreleased
* Add `char_limit` to `TextEdit` singleline mode to limit the amount of characters
* ⚠️ BREAKING: `Plot::link_axis` and `Plot::link_cursor` now take the name of the group ([#2410](https://github.com/emilk/egui/pull/2410)).
* Update `Plot::allow_zoom` and `Plot::allow_drag` to allow setting those values for X and Y axes independently ([#2901](https://github.com/emilk/egui/pull/2901)).
## 0.21.0 - 2023-02-08 - Deadlock fix and style customizability
* ⚠️ BREAKING: `egui::Context` now use closures for locking ([#2625](https://github.com/emilk/egui/pull/2625)):

View File

@ -72,15 +72,19 @@ impl Default for CoordinatesFormatter {
const MIN_LINE_SPACING_IN_POINTS: f64 = 6.0; // TODO(emilk): large enough for a wide label
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Copy, Clone)]
struct AxisBools {
x: bool,
y: bool,
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AxisBools {
pub x: bool,
pub y: bool,
}
impl AxisBools {
pub fn new(x: bool, y: bool) -> Self {
Self { x, y }
}
#[inline]
fn any(&self) -> bool {
pub fn any(&self) -> bool {
self.x || self.y
}
}
@ -165,8 +169,8 @@ pub struct Plot {
center_x_axis: bool,
center_y_axis: bool,
allow_zoom: bool,
allow_drag: bool,
allow_zoom: AxisBools,
allow_drag: AxisBools,
allow_scroll: bool,
allow_double_click_reset: bool,
allow_boxed_zoom: bool,
@ -207,8 +211,8 @@ impl Plot {
center_x_axis: false,
center_y_axis: false,
allow_zoom: true,
allow_drag: true,
allow_zoom: true.into(),
allow_drag: true.into(),
allow_scroll: true,
allow_double_click_reset: true,
allow_boxed_zoom: true,
@ -305,8 +309,13 @@ impl Plot {
}
/// Whether to allow zooming in the plot. Default: `true`.
pub fn allow_zoom(mut self, on: bool) -> Self {
self.allow_zoom = on;
///
/// Note: Allowing zoom in one axis but not the other may lead to unexpected results if used in combination with `data_aspect`.
pub fn allow_zoom<T>(mut self, on: T) -> Self
where
T: Into<AxisBools>,
{
self.allow_zoom = on.into();
self
}
@ -346,8 +355,11 @@ impl Plot {
}
/// 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;
pub fn allow_drag<T>(mut self, on: T) -> Self
where
T: Into<AxisBools>,
{
self.allow_drag = on.into();
self
}
@ -829,9 +841,16 @@ impl Plot {
}
// Dragging
if allow_drag && response.dragged_by(PointerButton::Primary) {
if allow_drag.any() && response.dragged_by(PointerButton::Primary) {
response = response.on_hover_cursor(CursorIcon::Grabbing);
transform.translate_bounds(-response.drag_delta());
let mut delta = -response.drag_delta();
if !allow_drag.x {
delta.x = 0.0;
}
if !allow_drag.y {
delta.y = 0.0;
}
transform.translate_bounds(delta);
bounds_modified = true.into();
}
@ -889,12 +908,18 @@ impl Plot {
let hover_pos = response.hover_pos();
if let Some(hover_pos) = hover_pos {
if allow_zoom {
let zoom_factor = if data_aspect.is_some() {
if allow_zoom.any() {
let mut zoom_factor = if data_aspect.is_some() {
Vec2::splat(ui.input(|i| i.zoom_delta()))
} else {
ui.input(|i| i.zoom_delta_2d())
};
if !allow_zoom.x {
zoom_factor.x = 1.0;
}
if !allow_zoom.y {
zoom_factor.y = 1.0;
}
if zoom_factor != Vec2::splat(1.0) {
transform.zoom(zoom_factor, hover_pos);
bounds_modified = true.into();

View File

@ -1,7 +1,7 @@
use std::f64::consts::TAU;
use std::ops::RangeInclusive;
use egui::plot::{GridInput, GridMark};
use egui::plot::{AxisBools, GridInput, GridMark};
use egui::*;
use plot::{
Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner, HLine,
@ -812,6 +812,8 @@ impl Default for Chart {
struct ChartsDemo {
chart: Chart,
vertical: bool,
allow_zoom: AxisBools,
allow_drag: AxisBools,
}
impl Default for ChartsDemo {
@ -819,22 +821,44 @@ impl Default for ChartsDemo {
Self {
vertical: true,
chart: Chart::default(),
allow_zoom: true.into(),
allow_drag: true.into(),
}
}
}
impl ChartsDemo {
fn ui(&mut self, ui: &mut Ui) -> Response {
ui.label("Type:");
ui.horizontal(|ui| {
ui.selectable_value(&mut self.chart, Chart::GaussBars, "Histogram");
ui.selectable_value(&mut self.chart, Chart::StackedBars, "Stacked Bar Chart");
ui.selectable_value(&mut self.chart, Chart::BoxPlot, "Box Plot");
});
ui.label("Orientation:");
ui.horizontal(|ui| {
ui.selectable_value(&mut self.vertical, true, "Vertical");
ui.selectable_value(&mut self.vertical, false, "Horizontal");
ui.vertical(|ui| {
ui.label("Type:");
ui.horizontal(|ui| {
ui.selectable_value(&mut self.chart, Chart::GaussBars, "Histogram");
ui.selectable_value(&mut self.chart, Chart::StackedBars, "Stacked Bar Chart");
ui.selectable_value(&mut self.chart, Chart::BoxPlot, "Box Plot");
});
ui.label("Orientation:");
ui.horizontal(|ui| {
ui.selectable_value(&mut self.vertical, true, "Vertical");
ui.selectable_value(&mut self.vertical, false, "Horizontal");
});
});
ui.vertical(|ui| {
ui.group(|ui| {
ui.add_enabled_ui(self.chart != Chart::StackedBars, |ui| {
ui.horizontal(|ui| {
ui.label("Allow zoom:");
ui.checkbox(&mut self.allow_zoom.x, "X");
ui.checkbox(&mut self.allow_zoom.y, "Y");
});
});
ui.horizontal(|ui| {
ui.label("Allow drag:");
ui.checkbox(&mut self.allow_drag.x, "X");
ui.checkbox(&mut self.allow_drag.y, "Y");
});
});
});
});
match self.chart {
Chart::GaussBars => self.bar_gauss(ui),
@ -867,6 +891,8 @@ impl ChartsDemo {
Plot::new("Normal Distribution Demo")
.legend(Legend::default())
.clamp_grid(true)
.allow_zoom(self.allow_zoom)
.allow_drag(self.allow_drag)
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
.response
}
@ -925,6 +951,7 @@ impl ChartsDemo {
Plot::new("Stacked Bar Chart Demo")
.legend(Legend::default())
.data_aspect(1.0)
.allow_drag(self.allow_drag)
.show(ui, |plot_ui| {
plot_ui.bar_chart(chart1);
plot_ui.bar_chart(chart2);
@ -968,6 +995,8 @@ impl ChartsDemo {
Plot::new("Box Plot Demo")
.legend(Legend::default())
.allow_zoom(self.allow_zoom)
.allow_drag(self.allow_drag)
.show(ui, |plot_ui| {
plot_ui.box_plot(box1);
plot_ui.box_plot(box2);