From 467f7ab38a237b05535f58c980e5e73aee62bebd Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Sun, 1 Sep 2024 22:30:33 +0200 Subject: [PATCH] Add Ui::with_transform, making it possible to transform widgets without creating a new layer --- crates/egui/src/layers.rs | 14 +++++++++++++- crates/egui/src/ui.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/layers.rs b/crates/egui/src/layers.rs index 3a657ba013f..66bbd338387 100644 --- a/crates/egui/src/layers.rs +++ b/crates/egui/src/layers.rs @@ -124,10 +124,14 @@ impl PaintList { self.0.is_empty() } + pub fn next_idx(&self) -> ShapeIdx { + ShapeIdx(self.0.len()) + } + /// Returns the index of the new [`Shape`] that can be used with `PaintList::set`. #[inline(always)] pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx { - let idx = ShapeIdx(self.0.len()); + let idx = self.next_idx(); self.0.push(ClippedShape { clip_rect, shape }); idx } @@ -171,6 +175,14 @@ impl PaintList { } } + /// Transform each [`Shape`] and clip rectangle in range by this much, in-place + pub fn transform_range(&mut self, start: ShapeIdx, end: ShapeIdx, transform: TSTransform) { + for ClippedShape { clip_rect, shape } in &mut self.0[start.0..end.0] { + *clip_rect = transform.mul_rect(*clip_rect); + shape.transform(transform); + } + } + /// Read-only access to all held shapes. pub fn all_entries(&self) -> impl ExactSizeIterator { self.0.iter() diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index e502f11e148..064a937ab0f 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2685,6 +2685,33 @@ impl Ui { (InnerResponse { inner, response }, payload) } + + /// Create a new Scope and transform its contents via a [`TSTransform`]. + /// This only affects visuals, inputs will not be transformed. So this is mostly useful + /// to create visual effects on interactions, e.g. scaling a button on hover / click. + /// + /// Check out [`Context::set_transform_layer`] for a persistent transform that also affects + /// inputs. + pub fn with_transform( + &mut self, + transform: emath::TSTransform, + add_contents: impl FnOnce(&mut Self) -> R, + ) -> InnerResponse { + let start_idx = self.ctx().graphics(|gx| { + gx.get(self.layer_id()) + .map_or(crate::layers::ShapeIdx(0), |l| l.next_idx()) + }); + + let r = self.scope_dyn(UiBuilder::new(), Box::new(add_contents)); + + self.ctx().graphics_mut(|g| { + let list = g.entry(self.layer_id()); + let end_idx = list.next_idx(); + list.transform_range(start_idx, end_idx, transform); + }); + + r + } } /// # Menus