Merge branch 'master' into data_aspect
This commit is contained in:
commit
e3f3410e1a
7 changed files with 122 additions and 68 deletions
|
@ -175,6 +175,7 @@ These are the official egui integrations:
|
|||
* [`amethyst_egui`](https://github.com/jgraef/amethyst_egui) for [the Amethyst game engine](https://amethyst.rs/).
|
||||
* [`bevy_egui`](https://github.com/mvlabat/bevy_egui) for [the Bevy game engine](https://bevyengine.org/).
|
||||
* [`egui_glfw_gl`](https://github.com/cohaereo/egui_glfw_gl) for [GLFW](https://crates.io/crates/glfw).
|
||||
* [`egui-glutin-gl`](https://github.com/h3r2tic/egui-glutin-gl/) for [glutin](https://crates.io/crates/glutin).
|
||||
* [`egui_sdl2_gl`](https://crates.io/crates/egui_sdl2_gl) for [SDL2](https://crates.io/crates/sdl2).
|
||||
* [`egui_sdl2_platform`](https://github.com/ComLarsic/egui_sdl2_platform) for [SDL2](https://crates.io/crates/sdl2).
|
||||
* [`egui_vulkano`](https://github.com/derivator/egui_vulkano) for [Vulkano](https://github.com/vulkano-rs/vulkano).
|
||||
|
|
|
@ -270,6 +270,10 @@ pub struct NativeOptions {
|
|||
/// You should avoid having a [`egui::CentralPanel`], or make sure its frame is also transparent.
|
||||
pub transparent: bool,
|
||||
|
||||
/// On desktop: mouse clicks pass through the window, used for non-interactable overlays
|
||||
/// Generally you would use this in conjunction with always_on_top
|
||||
pub mouse_passthrough: bool,
|
||||
|
||||
/// Turn on vertical syncing, limiting the FPS to the display refresh rate.
|
||||
///
|
||||
/// The default is `true`.
|
||||
|
@ -389,6 +393,7 @@ impl Default for NativeOptions {
|
|||
max_window_size: None,
|
||||
resizable: true,
|
||||
transparent: false,
|
||||
mouse_passthrough: false,
|
||||
vsync: true,
|
||||
multisampling: 0,
|
||||
depth_buffer: 0,
|
||||
|
|
|
@ -383,6 +383,9 @@ mod glow_integration {
|
|||
integration.egui_ctx.set_visuals(theme.egui_visuals());
|
||||
|
||||
gl_window.window().set_ime_allowed(true);
|
||||
if self.native_options.mouse_passthrough {
|
||||
gl_window.window().set_cursor_hittest(false).unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let event_loop_proxy = self.repaint_proxy.clone();
|
||||
|
|
|
@ -244,6 +244,13 @@ impl Prepared {
|
|||
rect
|
||||
}
|
||||
|
||||
fn content_with_margin(&self) -> Rect {
|
||||
let mut rect = self.content_ui.min_rect();
|
||||
rect.min -= self.frame.inner_margin.left_top() + self.frame.outer_margin.left_top();
|
||||
rect.max += self.frame.inner_margin.right_bottom() + self.frame.outer_margin.right_bottom();
|
||||
rect
|
||||
}
|
||||
|
||||
pub fn end(self, ui: &mut Ui) -> Response {
|
||||
let paint_rect = self.paint_rect();
|
||||
|
||||
|
@ -258,6 +265,6 @@ impl Prepared {
|
|||
ui.painter().set(where_to_put_background, shape);
|
||||
}
|
||||
|
||||
ui.allocate_rect(paint_rect, Sense::hover())
|
||||
ui.allocate_rect(self.content_with_margin(), Sense::hover())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,25 +73,15 @@ 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(Clone)]
|
||||
struct AutoBounds {
|
||||
#[derive(Copy, Clone)]
|
||||
struct AxisBools {
|
||||
x: bool,
|
||||
y: bool,
|
||||
}
|
||||
|
||||
impl AutoBounds {
|
||||
fn from_bool(val: bool) -> Self {
|
||||
AutoBounds { x: val, y: val }
|
||||
}
|
||||
|
||||
fn any(&self) -> bool {
|
||||
self.x || self.y
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for AutoBounds {
|
||||
impl From<bool> for AxisBools {
|
||||
fn from(val: bool) -> Self {
|
||||
AutoBounds::from_bool(val)
|
||||
AxisBools { x: val, y: val }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,10 +89,11 @@ impl From<bool> for AutoBounds {
|
|||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Clone)]
|
||||
struct PlotMemory {
|
||||
auto_bounds: AutoBounds,
|
||||
/// Indicates if the user has modified the bounds, for example by moving or zooming,
|
||||
/// or if the bounds should be calculated based by included point or auto bounds.
|
||||
bounds_modified: AxisBools,
|
||||
hovered_entry: Option<String>,
|
||||
hidden_items: ahash::HashSet<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>,
|
||||
|
@ -268,6 +259,7 @@ pub struct Plot {
|
|||
allow_zoom: bool,
|
||||
allow_drag: bool,
|
||||
allow_scroll: bool,
|
||||
auto_bounds: AxisBools,
|
||||
min_auto_bounds: PlotBounds,
|
||||
margin_fraction: Vec2,
|
||||
allow_boxed_zoom: bool,
|
||||
|
@ -281,6 +273,8 @@ pub struct Plot {
|
|||
data_aspect: Option<f32>,
|
||||
view_aspect: Option<f32>,
|
||||
|
||||
reset: bool,
|
||||
|
||||
show_x: bool,
|
||||
show_y: bool,
|
||||
label_formatter: LabelFormatter,
|
||||
|
@ -303,6 +297,7 @@ impl Plot {
|
|||
allow_zoom: true,
|
||||
allow_drag: true,
|
||||
allow_scroll: true,
|
||||
auto_bounds: false.into(),
|
||||
min_auto_bounds: PlotBounds::NOTHING,
|
||||
margin_fraction: Vec2::splat(0.05),
|
||||
allow_boxed_zoom: true,
|
||||
|
@ -316,6 +311,8 @@ impl Plot {
|
|||
data_aspect: None,
|
||||
view_aspect: None,
|
||||
|
||||
reset: false,
|
||||
|
||||
show_x: true,
|
||||
show_y: true,
|
||||
label_formatter: None,
|
||||
|
@ -402,7 +399,7 @@ impl Plot {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set the side margin as a fraction of the plot size.
|
||||
/// Set the side margin as a fraction of the plot size. Only used for auto bounds.
|
||||
///
|
||||
/// For instance, a value of `0.1` will add 10% space on both sides.
|
||||
pub fn set_margin_fraction(mut self, margin_fraction: Vec2) -> Self {
|
||||
|
@ -556,6 +553,18 @@ impl Plot {
|
|||
self
|
||||
}
|
||||
|
||||
/// Expand bounds to fit all items across the x axis, including values given by `include_x`.
|
||||
pub fn auto_bounds_x(mut self) -> Self {
|
||||
self.auto_bounds.x = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Expand bounds to fit all items across the y axis, including values given by `include_y`.
|
||||
pub fn auto_bounds_y(mut self) -> Self {
|
||||
self.auto_bounds.y = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show a legend including all named items.
|
||||
pub fn legend(mut self, legend: Legend) -> Self {
|
||||
self.legend_config = Some(legend);
|
||||
|
@ -592,6 +601,12 @@ impl Plot {
|
|||
self
|
||||
}
|
||||
|
||||
/// Resets the plot.
|
||||
pub fn reset(mut self) -> Self {
|
||||
self.reset = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Interact with and add items to the plot and finally draw it.
|
||||
pub fn show<R>(self, ui: &mut Ui, build_fn: impl FnOnce(&mut PlotUi) -> R) -> InnerResponse<R> {
|
||||
self.show_dyn(ui, Box::new(build_fn))
|
||||
|
@ -611,6 +626,7 @@ impl Plot {
|
|||
allow_drag,
|
||||
allow_boxed_zoom,
|
||||
boxed_zoom_pointer_button: boxed_zoom_pointer,
|
||||
auto_bounds,
|
||||
min_auto_bounds,
|
||||
margin_fraction,
|
||||
width,
|
||||
|
@ -624,6 +640,7 @@ impl Plot {
|
|||
coordinates_formatter,
|
||||
axis_formatters,
|
||||
legend_config,
|
||||
reset,
|
||||
show_background,
|
||||
show_axes,
|
||||
linked_axes,
|
||||
|
@ -661,11 +678,19 @@ impl Plot {
|
|||
// Load or initialize the memory.
|
||||
let plot_id = ui.make_persistent_id(id_source);
|
||||
ui.ctx().check_for_id_clash(plot_id, rect, "Plot");
|
||||
let mut memory = PlotMemory::load(ui.ctx(), plot_id).unwrap_or_else(|| PlotMemory {
|
||||
auto_bounds: (!min_auto_bounds.is_valid()).into(),
|
||||
let memory = if reset {
|
||||
if let Some(axes) = linked_axes.as_ref() {
|
||||
axes.bounds.set(None);
|
||||
};
|
||||
|
||||
None
|
||||
} else {
|
||||
PlotMemory::load(ui.ctx(), plot_id)
|
||||
}
|
||||
.unwrap_or_else(|| PlotMemory {
|
||||
bounds_modified: false.into(),
|
||||
hovered_entry: None,
|
||||
hidden_items: Default::default(),
|
||||
min_auto_bounds,
|
||||
last_screen_transform: ScreenTransform::new(
|
||||
rect,
|
||||
min_auto_bounds,
|
||||
|
@ -675,24 +700,12 @@ impl Plot {
|
|||
last_click_pos_for_zoom: None,
|
||||
});
|
||||
|
||||
// If the min bounds changed, recalculate everything.
|
||||
if min_auto_bounds != memory.min_auto_bounds {
|
||||
memory = PlotMemory {
|
||||
auto_bounds: (!min_auto_bounds.is_valid()).into(),
|
||||
hovered_entry: None,
|
||||
min_auto_bounds,
|
||||
..memory
|
||||
};
|
||||
memory.clone().store(ui.ctx(), plot_id);
|
||||
}
|
||||
|
||||
let PlotMemory {
|
||||
mut auto_bounds,
|
||||
mut bounds_modified,
|
||||
mut hovered_entry,
|
||||
mut hidden_items,
|
||||
last_screen_transform,
|
||||
mut last_click_pos_for_zoom,
|
||||
..
|
||||
} = memory;
|
||||
|
||||
// Call the plot build function.
|
||||
|
@ -774,52 +787,51 @@ impl Plot {
|
|||
if let Some(linked_bounds) = axes.get() {
|
||||
if axes.link_x {
|
||||
bounds.set_x(&linked_bounds);
|
||||
// Turn off auto bounds to keep it from overriding what we just set.
|
||||
auto_bounds.x = false;
|
||||
// Mark the axis as modified to prevent it from being changed.
|
||||
bounds_modified.x = true;
|
||||
}
|
||||
if axes.link_y {
|
||||
bounds.set_y(&linked_bounds);
|
||||
// Turn off auto bounds to keep it from overriding what we just set.
|
||||
auto_bounds.y = false;
|
||||
// Mark the axis as modified to prevent it from being changed.
|
||||
bounds_modified.y = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Allow double clicking to reset to automatic bounds.
|
||||
// Allow double clicking to reset to the initial bounds.
|
||||
if response.double_clicked_by(PointerButton::Primary) {
|
||||
auto_bounds = true.into();
|
||||
bounds_modified = false.into();
|
||||
}
|
||||
|
||||
if !bounds.is_valid() {
|
||||
auto_bounds = true.into();
|
||||
// Reset bounds to initial bounds if we haven't been modified.
|
||||
if !bounds_modified.x {
|
||||
bounds.set_x(&min_auto_bounds);
|
||||
}
|
||||
if !bounds_modified.y {
|
||||
bounds.set_y(&min_auto_bounds);
|
||||
}
|
||||
|
||||
let auto_x = !bounds_modified.x && (!min_auto_bounds.is_valid_x() || auto_bounds.x);
|
||||
let auto_y = !bounds_modified.y && (!min_auto_bounds.is_valid_y() || auto_bounds.y);
|
||||
|
||||
// Set bounds automatically based on content.
|
||||
if auto_bounds.any() {
|
||||
if auto_bounds.x {
|
||||
bounds.set_x(&min_auto_bounds);
|
||||
}
|
||||
|
||||
if auto_bounds.y {
|
||||
bounds.set_y(&min_auto_bounds);
|
||||
}
|
||||
|
||||
if auto_x || auto_y {
|
||||
for item in &items {
|
||||
let item_bounds = item.bounds();
|
||||
|
||||
if auto_bounds.x {
|
||||
if auto_x {
|
||||
bounds.merge_x(&item_bounds);
|
||||
}
|
||||
if auto_bounds.y {
|
||||
if auto_y {
|
||||
bounds.merge_y(&item_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
if auto_bounds.x {
|
||||
if auto_x {
|
||||
bounds.add_relative_margin_x(margin_fraction);
|
||||
}
|
||||
|
||||
if auto_bounds.y {
|
||||
if auto_y {
|
||||
bounds.add_relative_margin_y(margin_fraction);
|
||||
}
|
||||
}
|
||||
|
@ -842,7 +854,7 @@ impl Plot {
|
|||
if allow_drag && response.dragged_by(PointerButton::Primary) {
|
||||
response = response.on_hover_cursor(CursorIcon::Grabbing);
|
||||
transform.translate_bounds(-response.drag_delta());
|
||||
auto_bounds = false.into();
|
||||
bounds_modified = true.into();
|
||||
}
|
||||
|
||||
// Zooming
|
||||
|
@ -889,7 +901,7 @@ impl Plot {
|
|||
};
|
||||
if new_bounds.is_valid() {
|
||||
transform.set_bounds(new_bounds);
|
||||
auto_bounds = false.into();
|
||||
bounds_modified = true.into();
|
||||
}
|
||||
// reset the boxed zoom state
|
||||
last_click_pos_for_zoom = None;
|
||||
|
@ -906,14 +918,14 @@ impl Plot {
|
|||
};
|
||||
if zoom_factor != Vec2::splat(1.0) {
|
||||
transform.zoom(zoom_factor, hover_pos);
|
||||
auto_bounds = false.into();
|
||||
bounds_modified = true.into();
|
||||
}
|
||||
}
|
||||
if allow_scroll {
|
||||
let scroll_delta = ui.input().scroll_delta;
|
||||
if scroll_delta != Vec2::ZERO {
|
||||
transform.translate_bounds(-scroll_delta);
|
||||
auto_bounds = false.into();
|
||||
bounds_modified = true.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -963,10 +975,9 @@ impl Plot {
|
|||
}
|
||||
|
||||
let memory = PlotMemory {
|
||||
auto_bounds,
|
||||
bounds_modified,
|
||||
hovered_entry,
|
||||
hidden_items,
|
||||
min_auto_bounds,
|
||||
last_screen_transform: transform,
|
||||
last_click_pos_for_zoom,
|
||||
};
|
||||
|
|
|
@ -40,10 +40,26 @@ impl PlotBounds {
|
|||
&& self.max[1].is_finite()
|
||||
}
|
||||
|
||||
pub fn is_finite_x(&self) -> bool {
|
||||
self.min[0].is_finite() && self.max[0].is_finite()
|
||||
}
|
||||
|
||||
pub fn is_finite_y(&self) -> bool {
|
||||
self.min[1].is_finite() && self.max[1].is_finite()
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.is_finite() && self.width() > 0.0 && self.height() > 0.0
|
||||
}
|
||||
|
||||
pub fn is_valid_x(&self) -> bool {
|
||||
self.is_finite_x() && self.width() > 0.0
|
||||
}
|
||||
|
||||
pub fn is_valid_y(&self) -> bool {
|
||||
self.is_finite_y() && self.height() > 0.0
|
||||
}
|
||||
|
||||
pub fn width(&self) -> f64 {
|
||||
self.max[0] - self.min[0]
|
||||
}
|
||||
|
@ -181,8 +197,11 @@ pub(crate) struct ScreenTransform {
|
|||
impl ScreenTransform {
|
||||
pub fn new(frame: Rect, mut bounds: PlotBounds, x_centered: bool, y_centered: bool) -> Self {
|
||||
// Make sure they are not empty.
|
||||
if !bounds.is_valid() {
|
||||
bounds = PlotBounds::new_symmetrical(1.0);
|
||||
if !bounds.is_valid_x() {
|
||||
bounds.set_x(&PlotBounds::new_symmetrical(1.0));
|
||||
}
|
||||
if !bounds.is_valid_y() {
|
||||
bounds.set_y(&PlotBounds::new_symmetrical(1.0));
|
||||
}
|
||||
|
||||
// Scale axes so that the origin is in the center.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use epaint::{emath::lerp, vec2, Pos2, Shape, Stroke};
|
||||
use epaint::{emath::lerp, vec2, Color32, Pos2, Shape, Stroke};
|
||||
|
||||
use crate::{Response, Sense, Ui, Widget};
|
||||
|
||||
|
@ -10,6 +10,7 @@ use crate::{Response, Sense, Ui, Widget};
|
|||
pub struct Spinner {
|
||||
/// Uses the style's `interact_size` if `None`.
|
||||
size: Option<f32>,
|
||||
color: Option<Color32>,
|
||||
}
|
||||
|
||||
impl Spinner {
|
||||
|
@ -24,6 +25,12 @@ impl Spinner {
|
|||
self.size = Some(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the spinner's color.
|
||||
pub fn color(mut self, color: impl Into<Color32>) -> Self {
|
||||
self.color = Some(color.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Spinner {
|
||||
|
@ -31,6 +38,9 @@ impl Widget for Spinner {
|
|||
let size = self
|
||||
.size
|
||||
.unwrap_or_else(|| ui.style().spacing.interact_size.y);
|
||||
let color = self
|
||||
.color
|
||||
.unwrap_or_else(|| ui.visuals().strong_text_color());
|
||||
let (rect, response) = ui.allocate_exact_size(vec2(size, size), Sense::hover());
|
||||
|
||||
if ui.is_rect_visible(rect) {
|
||||
|
@ -47,10 +57,8 @@ impl Widget for Spinner {
|
|||
rect.center() + radius * vec2(cos as f32, sin as f32)
|
||||
})
|
||||
.collect();
|
||||
ui.painter().add(Shape::line(
|
||||
points,
|
||||
Stroke::new(3.0, ui.visuals().strong_text_color()),
|
||||
));
|
||||
ui.painter()
|
||||
.add(Shape::line(points, Stroke::new(3.0, color)));
|
||||
}
|
||||
|
||||
response
|
||||
|
|
Loading…
Reference in a new issue