Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add layer transforms, interaction in layer #3906

Merged
merged 39 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
24143f4
add scaling layers, interaction with scaled items
Jan 27, 2024
0f6f82c
fix style
Tweoss Jan 27, 2024
141433d
add pan_zoom demo
Tweoss Jan 29, 2024
c001c5a
add pan_zoom demo
Tweoss Jan 29, 2024
085c593
fix warnings
Tweoss Jan 29, 2024
af79f81
Merge branch 'master' into transform-layer
Tweoss Jan 29, 2024
9fc5b71
Merge branch 'master' into transform-layer
Tweoss Jan 29, 2024
264726b
clean up
Tweoss Jan 29, 2024
17bb75e
port TSTransform to emath, make transform persist
Tweoss Feb 1, 2024
b2342dc
use smooth scrolling in demo
Tweoss Feb 1, 2024
ac2ae96
update docstring in layers.rs
Tweoss Feb 1, 2024
998a6e6
fix cranky, style
Tweoss Feb 1, 2024
b0d73d0
Update crates/egui/src/context.rs doc comment
Tweoss Feb 1, 2024
18347d3
add TSTransform doctests, switch transform order
Tweoss Feb 1, 2024
e593bbf
minor refactoring
Tweoss Feb 1, 2024
227bd6a
add invert method for TSTransform
Tweoss Feb 1, 2024
5902ffd
add back translate for mesh.
Tweoss Feb 1, 2024
635fc2f
Merge branch 'master' into transform-layer
Tweoss Feb 1, 2024
9090324
Update docstring crates/emath/src/ts_transform.rs
Tweoss Feb 10, 2024
6178cd8
simplify pan_zoom logic, multiply TSTransforms
Tweoss Feb 10, 2024
f1df40a
fix TSTransform doc comments
Tweoss Feb 10, 2024
05ba7c8
optimize arcs in transforming galley
Tweoss Feb 10, 2024
be325f0
scale thickness of strokes when transforming
Tweoss Feb 10, 2024
0b892d7
clarify paintcallback scaling comment
Tweoss Feb 10, 2024
2526e5b
fix cranky
Tweoss Feb 10, 2024
5b33246
Merge branch 'master' into transform-layer
Tweoss Feb 10, 2024
f70293a
Merge branch 'master' into transform-layer
Tweoss Feb 15, 2024
663a1b3
fix drag ratio
Tweoss Feb 16, 2024
09c4a50
apply transform to mouse input positions
Tweoss Feb 16, 2024
edafee5
add drag_value example
Tweoss Feb 16, 2024
7fd7e7c
fix formatting
Tweoss Feb 16, 2024
499492e
fix area: use response (not ctx) pointer delta
Tweoss Feb 16, 2024
3c27b3b
refactor hover_pos transform in response.rs
Tweoss Feb 16, 2024
bd46536
add label and link to pan_zoom demo
Tweoss Feb 16, 2024
b0afe90
add tracking inner contents to outer window
Tweoss Feb 16, 2024
c6ca843
fix text inner bounds
Tweoss Feb 17, 2024
f6851ab
add painter example
Tweoss Feb 17, 2024
f4ed104
fix pointer interaction for transforms in SidePanel
Tweoss Feb 17, 2024
b109093
fix topbottom panel pointer position
Tweoss Feb 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
input_state::*,
layers::GraphicLayers,
load::{Bytes, Loaders, SizedTexture},
memory::Options,
memory::{LayerTransform, Options},
os::OperatingSystem,
output::FullOutput,
util::IdTypeMap,
Expand Down Expand Up @@ -2071,9 +2071,14 @@ impl Context {
/// Move all the graphics at the given layer.
///
/// Can be used to implement drag-and-drop (see relevant demo).
pub fn translate_layer(&self, layer_id: LayerId, delta: Vec2) {
pub fn transform_layer(&self, layer_id: LayerId, delta: Vec2, scale: f32) {
if delta != Vec2::ZERO {
self.graphics_mut(|g| g.entry(layer_id).translate(delta));
let transform = LayerTransform {
translation: delta,
scale,
};
self.graphics_mut(|g| g.entry(layer_id).transform(&transform));
self.memory_mut(|m| m.layer_transforms_mut().insert(layer_id, transform));
}
}

Expand Down Expand Up @@ -2102,6 +2107,10 @@ impl Context {
///
/// See also [`Response::contains_pointer`].
pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
let transform = self
.memory(|m| m.layer_transforms().get(&layer_id).cloned())
.unwrap_or_default();
let rect = transform.apply(rect);
if !rect.is_positive() {
return false;
}
Expand Down Expand Up @@ -2142,6 +2151,12 @@ impl Context {
let mut blocking_widget = None;

self.write(|ctx| {
let transform = ctx
.memory
.layer_transforms()
.get(&layer_id)
.cloned()
.unwrap_or_default();
let viewport = ctx.viewport();

// We add all widgets here, even non-interactive ones,
Expand All @@ -2165,7 +2180,7 @@ impl Context {
// which means there are no widgets covering us.
break;
}
if !blocking.rect.contains(pointer_pos) {
if !transform.apply(blocking.rect).contains(pointer_pos) {
continue;
}
if sense.interactive() && !blocking.sense.interactive() {
Expand Down
8 changes: 4 additions & 4 deletions crates/egui/src/layers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Handles paint layers, i.e. how things
//! are sometimes painted behind or in front of other things.

use crate::{Id, *};
use crate::{memory::LayerTransform, Id, *};
use epaint::{ClippedShape, Shape};

/// Different layer categories
Expand Down Expand Up @@ -159,10 +159,10 @@ impl PaintList {
}

/// Translate each [`Shape`] and clip rectangle by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
pub fn transform(&mut self, transform: &LayerTransform) {
for ClippedShape { clip_rect, shape } in &mut self.0 {
*clip_rect = clip_rect.translate(delta);
shape.translate(delta);
*clip_rect = transform.apply(*clip_rect);
shape.transform(transform.translation, transform.scale);
}
}

Expand Down
54 changes: 52 additions & 2 deletions crates/egui/src/memory.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![warn(missing_docs)] // Let's keep this file well-documented.` to memory.rs

use ahash::HashMap;

use crate::{
area, vec2,
window::{self, WindowInteraction},
Expand Down Expand Up @@ -88,6 +90,7 @@ pub struct Memory {
// -------------------------------------------------
// Per-viewport:
areas: ViewportIdMap<Areas>,
layer_transforms: HashMap<LayerId, LayerTransform>,

#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) interactions: ViewportIdMap<Interaction>,
Expand All @@ -107,6 +110,7 @@ impl Default for Memory {
viewport_id: Default::default(),
window_interactions: Default::default(),
areas: Default::default(),
layer_transforms: Default::default(),
popup: Default::default(),
everything_is_visible: Default::default(),
};
Expand Down Expand Up @@ -151,6 +155,32 @@ impl FocusDirection {
}
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
pub struct LayerTransform {
pub translation: Vec2,
pub scale: f32,
}

impl Default for LayerTransform {
fn default() -> Self {
Self {
translation: Vec2::default(),
scale: 1.0,
}
}
}

impl LayerTransform {
pub(crate) fn apply(&self, rect: Rect) -> Rect {
Rect::from_center_size(
(rect.center() + self.translation) * self.scale,
rect.size() * self.scale,
)
}
}

// ----------------------------------------------------------------------------

/// Some global options that you can read and write.
Expand Down Expand Up @@ -605,9 +635,20 @@ impl Memory {
self.areas.entry(self.viewport_id).or_default()
}

/// Access layer transformations.
pub fn layer_transforms(&self) -> &HashMap<LayerId, LayerTransform> {
&self.layer_transforms
}

/// Access layer transformations.
pub fn layer_transforms_mut(&mut self) -> &mut HashMap<LayerId, LayerTransform> {
&mut self.layer_transforms
}

/// Top-most layer at the given position.
pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> {
self.areas().layer_id_at(pos, resize_interact_radius_side)
self.areas()
.layer_id_at(pos, resize_interact_radius_side, &self.layer_transforms)
}

/// An iterator over all layers. Back-to-front. Top is last.
Expand Down Expand Up @@ -883,7 +924,12 @@ impl Areas {
}

/// Top-most layer at the given position.
pub fn layer_id_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<LayerId> {
pub fn layer_id_at(
&self,
pos: Pos2,
resize_interact_radius_side: f32,
layer_transforms: &HashMap<LayerId, LayerTransform>,
) -> Option<LayerId> {
for layer in self.order.iter().rev() {
if self.is_visible(layer) {
if let Some(state) = self.areas.get(&layer.id) {
Expand All @@ -894,6 +940,10 @@ impl Areas {
rect = rect.expand(resize_interact_radius_side);
}

if let Some(transform) = layer_transforms.get(layer) {
rect = transform.apply(rect);
}

if rect.contains(pos) {
return Some(*layer);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2153,7 +2153,7 @@ impl Ui {

if let Some(pointer_pos) = self.ctx().pointer_interact_pos() {
let delta = pointer_pos - response.rect.center();
self.ctx().translate_layer(layer_id, delta);
self.ctx().transform_layer(layer_id, delta, 1.0);
}

InnerResponse::new(inner, response)
Expand Down
1 change: 1 addition & 0 deletions crates/egui_demo_lib/src/demo/demo_app_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ impl Default for Demos {
Box::<super::MiscDemoWindow>::default(),
Box::<super::multi_touch::MultiTouch>::default(),
Box::<super::painting::Painting>::default(),
Box::<super::pan_zoom::PanZoom>::default(),
Box::<super::panels::Panels>::default(),
Box::<super::plot_demo::PlotDemo>::default(),
Box::<super::scrolling::Scrolling>::default(),
Expand Down
1 change: 1 addition & 0 deletions crates/egui_demo_lib/src/demo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod misc_demo_window;
pub mod multi_touch;
pub mod paint_bezier;
pub mod painting;
pub mod pan_zoom;
pub mod panels;
pub mod password;
pub mod plot_demo;
Expand Down
106 changes: 106 additions & 0 deletions crates/egui_demo_lib/src/demo/pan_zoom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#[derive(Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct PanZoom {
pan: egui::Vec2,
zoom: f32,
}

impl Eq for PanZoom {}

impl super::Demo for PanZoom {
fn name(&self) -> &'static str {
"🗖 Pan Zoom"
}

fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
use super::View as _;
let window = egui::Window::new("Pan Zoom")
.default_width(200.0)
.default_height(200.0)
.vscroll(false)
.open(open);
window.show(ctx, |ui| self.ui(ui));
}
}

impl super::View for PanZoom {
fn ui(&mut self, ui: &mut egui::Ui) {
// On initialization, zoom is 0
if self.zoom == 0.0 {
self.zoom = 1.0;
}

let (id, rect) = ui.allocate_space(ui.available_size());
let response = ui.interact(rect, id, egui::Sense::click_and_drag());
// Uncomment to allow dragging the background as well.
// self.pan += response.drag_delta() / self.zoom;

// Plot-like reset
if response.double_clicked() {
self.zoom = 1.0;
self.pan = egui::Vec2::ZERO;
}

if let Some(pointer) = ui.ctx().input(|i| i.pointer.hover_pos()) {
// Ignore if some other widget is covering this container.
if response.rect.contains(pointer) {
let original_zoom = self.zoom;
self.zoom *= ui.ctx().input(|i| i.zoom_delta());
let delta = pointer / self.zoom - pointer / original_zoom;
self.pan += delta;

// Keep mouse centered.
self.pan += ui.ctx().input(|i| i.raw_scroll_delta) / self.zoom;
}
}

let current_size = ui.min_rect();
[
(
current_size.left_top() + egui::Vec2::new(10.0, 10.0),
"top left!",
),
(
current_size.left_bottom() + egui::Vec2::new(10.0, -10.0),
"bottom left?",
),
(
current_size.right_bottom() + egui::Vec2::new(-10.0, -10.0),
"right bottom :D",
),
(
current_size.right_top() + egui::Vec2::new(-10.0, 10.0),
"right top ):",
),
]
.iter()
.map(|(pos, msg)| {
egui::Area::new(*msg)
.default_pos(*pos)
// Need to cover up the pan_zoom demo window,
// but may also cover over other windows.
.order(egui::Order::Foreground)
.show(ui.ctx(), |ui| {
let rect = egui::Rect::from_min_max(
(rect.min / self.zoom) - self.pan,
(rect.max / self.zoom) - self.pan,
);
ui.set_clip_rect(rect);
egui::Frame::default()
.rounding(egui::Rounding::same(4.0))
.inner_margin(egui::Margin::same(8.0))
.stroke(ui.ctx().style().visuals.window_stroke)
.fill(ui.style().visuals.panel_fill)
.show(ui, |ui| {
ui.style_mut().wrap = Some(false);
ui.button(*msg).clicked();
});
})
.response
.layer_id
})
.for_each(|id| {
ui.ctx().transform_layer(id, self.pan, self.zoom);
});
}
}
3 changes: 2 additions & 1 deletion crates/epaint/src/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,10 @@ impl Mesh {
}

/// Translate location by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
pub fn transform(&mut self, delta: Vec2, scale: f32) {
for v in &mut self.vertices {
v.pos += delta;
v.pos = (v.pos.to_vec2() * scale).to_pos2();
}
}

Expand Down
Loading
Loading