From de8ac88c0eecae42c473cc00296ecfbbea1ec521 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 12 Dec 2024 18:29:13 +0100 Subject: [PATCH] Add `Context::layer_transform_to_global` & `layer_transform_from_global` (#5465) This makes it easy to get the current transform of a layer, and uses consistent naming everywhere. `Memory::layer_transforms` is now called `Memory::to_global`, because the old name was ambiguous (transform into what direction?) --- crates/egui/src/containers/popup.rs | 15 +++---- crates/egui/src/context.rs | 41 +++++++++++++------- crates/egui/src/hit_test.rs | 6 +-- crates/egui/src/layers.rs | 14 +++---- crates/egui/src/memory/mod.rs | 25 ++++++++---- crates/egui/src/menu.rs | 7 +--- crates/egui/src/response.rs | 14 ++----- crates/egui/src/widgets/text_edit/builder.rs | 9 +++-- 8 files changed, 75 insertions(+), 56 deletions(-) diff --git a/crates/egui/src/containers/popup.rs b/crates/egui/src/containers/popup.rs index a90e615aca7..81bf84a2f4f 100644 --- a/crates/egui/src/containers/popup.rs +++ b/crates/egui/src/containers/popup.rs @@ -93,8 +93,8 @@ pub fn show_tooltip_at_pointer( pointer_rect.min.x = pointer_pos.x; // Transform global coords to layer coords: - if let Some(transform) = ctx.memory(|m| m.layer_transforms.get(&parent_layer).copied()) { - pointer_rect = transform.inverse() * pointer_rect; + if let Some(from_global) = ctx.layer_transform_from_global(parent_layer) { + pointer_rect = from_global * pointer_rect; } show_tooltip_at_dyn( @@ -162,8 +162,8 @@ fn show_tooltip_at_dyn<'c, R>( ) -> R { // Transform layer coords to global coords: let mut widget_rect = *widget_rect; - if let Some(transform) = ctx.memory(|m| m.layer_transforms.get(&parent_layer).copied()) { - widget_rect = transform * widget_rect; + if let Some(to_global) = ctx.layer_transform_to_global(parent_layer) { + widget_rect = to_global * widget_rect; } remember_that_tooltip_was_shown(ctx); @@ -404,11 +404,12 @@ pub fn popup_above_or_below_widget( AboveOrBelow::Above => (widget_response.rect.left_top(), Align2::LEFT_BOTTOM), AboveOrBelow::Below => (widget_response.rect.left_bottom(), Align2::LEFT_TOP), }; - if let Some(transform) = parent_ui + + if let Some(to_global) = parent_ui .ctx() - .memory(|m| m.layer_transforms.get(&parent_ui.layer_id()).copied()) + .layer_transform_to_global(parent_ui.layer_id()) { - pos = transform * pos; + pos = to_global * pos; } let frame = Frame::popup(parent_ui.style()); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index aa449849e32..9286f38025f 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -518,7 +518,7 @@ impl ContextImpl { crate::hit_test::hit_test( &viewport.prev_pass.widgets, &layers, - &self.memory.layer_transforms, + &self.memory.to_global, pos, interact_radius, ) @@ -1329,11 +1329,11 @@ impl Context { res.is_pointer_button_down_on || res.long_touched || clicked || res.drag_stopped; if is_interacted_with { res.interact_pointer_pos = input.pointer.interact_pos(); - if let (Some(transform), Some(pos)) = ( - memory.layer_transforms.get(&res.layer_id), + if let (Some(to_global), Some(pos)) = ( + memory.to_global.get(&res.layer_id), &mut res.interact_pointer_pos, ) { - *pos = transform.inverse() * *pos; + *pos = to_global.inverse() * *pos; } } @@ -2381,7 +2381,7 @@ impl ContextImpl { let shapes = viewport .graphics - .drain(self.memory.areas().order(), &self.memory.layer_transforms); + .drain(self.memory.areas().order(), &self.memory.to_global); let mut repaint_needed = false; @@ -2697,6 +2697,7 @@ impl Context { /// Transform the graphics of the given layer. /// /// This will also affect input. + /// The direction of the given transform is "into the global coordinate system". /// /// This is a sticky setting, remembered from one frame to the next. /// @@ -2706,13 +2707,28 @@ impl Context { pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) { self.memory_mut(|m| { if transform == TSTransform::IDENTITY { - m.layer_transforms.remove(&layer_id) + m.to_global.remove(&layer_id) } else { - m.layer_transforms.insert(layer_id, transform) + m.to_global.insert(layer_id, transform) } }); } + /// Return how to transform the graphics of the given layer into the global coordinate system. + /// + /// Set this with [`Self::layer_transform_to_global`]. + pub fn layer_transform_to_global(&self, layer_id: LayerId) -> Option { + self.memory(|m| m.to_global.get(&layer_id).copied()) + } + + /// Return how to transform the graphics of the global coordinate system into the local coordinate system of the given layer. + /// + /// This returns the inverse of [`Self::layer_transform_to_global`]. + pub fn layer_transform_from_global(&self, layer_id: LayerId) -> Option { + self.layer_transform_to_global(layer_id) + .map(|t| t.inverse()) + } + /// Move all the graphics at the given layer. /// /// Is used to implement drag-and-drop preview. @@ -2777,12 +2793,11 @@ impl Context { /// /// See also [`Response::contains_pointer`]. pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool { - let rect = - if let Some(transform) = self.memory(|m| m.layer_transforms.get(&layer_id).copied()) { - transform * rect - } else { - rect - }; + let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) { + to_global * rect + } else { + rect + }; if !rect.is_positive() { return false; } diff --git a/crates/egui/src/hit_test.rs b/crates/egui/src/hit_test.rs index 142645f24e6..fe0ce75f9c9 100644 --- a/crates/egui/src/hit_test.rs +++ b/crates/egui/src/hit_test.rs @@ -35,7 +35,7 @@ pub struct WidgetHits { pub fn hit_test( widgets: &WidgetRects, layer_order: &[LayerId], - layer_transforms: &HashMap, + layer_to_global: &HashMap, pos: Pos2, search_radius: f32, ) -> WidgetHits { @@ -44,9 +44,9 @@ pub fn hit_test( let search_radius_sq = search_radius * search_radius; // Transform the position into the local coordinate space of each layer: - let pos_in_layers: HashMap = layer_transforms + let pos_in_layers: HashMap = layer_to_global .iter() - .map(|(layer_id, t)| (*layer_id, t.inverse() * pos)) + .map(|(layer_id, to_global)| (*layer_id, to_global.inverse() * pos)) .collect(); let mut closest_dist_sq = f32::INFINITY; diff --git a/crates/egui/src/layers.rs b/crates/egui/src/layers.rs index 7966e12ad2a..595f67034f5 100644 --- a/crates/egui/src/layers.rs +++ b/crates/egui/src/layers.rs @@ -213,7 +213,7 @@ impl GraphicLayers { pub fn drain( &mut self, area_order: &[LayerId], - transforms: &ahash::HashMap, + to_global: &ahash::HashMap, ) -> Vec { crate::profile_function!(); @@ -231,10 +231,10 @@ impl GraphicLayers { for layer_id in area_order { if layer_id.order == order { if let Some(list) = order_map.get_mut(&layer_id.id) { - if let Some(transform) = transforms.get(layer_id) { + if let Some(to_global) = to_global.get(layer_id) { for clipped_shape in &mut list.0 { - clipped_shape.clip_rect = *transform * clipped_shape.clip_rect; - clipped_shape.shape.transform(*transform); + clipped_shape.clip_rect = *to_global * clipped_shape.clip_rect; + clipped_shape.shape.transform(*to_global); } } all_shapes.append(&mut list.0); @@ -246,10 +246,10 @@ impl GraphicLayers { for (id, list) in order_map { let layer_id = LayerId::new(order, *id); - if let Some(transform) = transforms.get(&layer_id) { + if let Some(to_global) = to_global.get(&layer_id) { for clipped_shape in &mut list.0 { - clipped_shape.clip_rect = *transform * clipped_shape.clip_rect; - clipped_shape.shape.transform(*transform); + clipped_shape.clip_rect = *to_global * clipped_shape.clip_rect; + clipped_shape.shape.transform(*to_global); } } diff --git a/crates/egui/src/memory/mod.rs b/crates/egui/src/memory/mod.rs index 5e94e6c0915..0a44df8cc57 100644 --- a/crates/egui/src/memory/mod.rs +++ b/crates/egui/src/memory/mod.rs @@ -95,8 +95,13 @@ pub struct Memory { #[cfg_attr(feature = "persistence", serde(skip))] everything_is_visible: bool, - /// Transforms per layer - pub layer_transforms: HashMap, + /// Transforms per layer. + /// + /// Instead of using this directly, use: + /// * [`crate::Context::set_transform_layer`] + /// * [`crate::Context::layer_transform_to_global`] + /// * [`crate::Context::layer_transform_from_global`] + pub to_global: HashMap, // ------------------------------------------------- // Per-viewport: @@ -120,7 +125,7 @@ impl Default for Memory { focus: Default::default(), viewport_id: Default::default(), areas: Default::default(), - layer_transforms: Default::default(), + to_global: Default::default(), popup: Default::default(), everything_is_visible: Default::default(), add_fonts: Default::default(), @@ -819,7 +824,7 @@ impl Memory { /// Top-most layer at the given position. pub fn layer_id_at(&self, pos: Pos2) -> Option { self.areas() - .layer_id_at(pos, &self.layer_transforms) + .layer_id_at(pos, &self.to_global) .and_then(|layer_id| { if self.is_above_modal_layer(layer_id) { Some(layer_id) @@ -829,6 +834,12 @@ impl Memory { }) } + /// The currently set transform of a layer. + #[deprecated = "Use `Context::layer_transform_to_global` instead"] + pub fn layer_transforms(&self, layer_id: LayerId) -> Option { + self.to_global.get(&layer_id).copied() + } + /// An iterator over all layers. Back-to-front, top is last. pub fn layer_ids(&self) -> impl ExactSizeIterator + '_ { self.areas().order().iter().copied() @@ -1194,15 +1205,15 @@ impl Areas { pub fn layer_id_at( &self, pos: Pos2, - layer_transforms: &HashMap, + layer_to_global: &HashMap, ) -> Option { for layer in self.order.iter().rev() { if self.is_visible(layer) { if let Some(state) = self.areas.get(&layer.id) { let mut rect = state.rect(); if state.interactable { - if let Some(transform) = layer_transforms.get(layer) { - rect = *transform * rect; + if let Some(to_global) = layer_to_global.get(layer) { + rect = *to_global * rect; } if rect.contains(pos) { diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 8faf8e77ceb..166e18da04b 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -406,11 +406,8 @@ impl MenuRoot { } } - if let Some(transform) = button - .ctx - .memory(|m| m.layer_transforms.get(&button.layer_id).copied()) - { - pos = transform * pos; + if let Some(to_global) = button.ctx.layer_transform_to_global(button.layer_id) { + pos = to_global * pos; } return MenuResponse::Create(pos, id); diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index f49383c70d1..18ddf793cc6 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -392,11 +392,8 @@ impl Response { pub fn drag_delta(&self) -> Vec2 { if self.dragged() { let mut delta = self.ctx.input(|i| i.pointer.delta()); - if let Some(scaling) = self - .ctx - .memory(|m| m.layer_transforms.get(&self.layer_id).map(|t| t.scaling)) - { - delta /= scaling; + if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) { + delta *= from_global.scaling; } delta } else { @@ -478,11 +475,8 @@ impl Response { pub fn hover_pos(&self) -> Option { if self.hovered() { let mut pos = self.ctx.input(|i| i.pointer.hover_pos())?; - if let Some(transform) = self - .ctx - .memory(|m| m.layer_transforms.get(&self.layer_id).copied()) - { - pos = transform.inverse() * pos; + if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) { + pos = from_global * pos; } Some(pos) } else { diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 012d8c8f0d5..587f498b0a2 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -766,14 +766,15 @@ impl<'t> TextEdit<'t> { } // Set IME output (in screen coords) when text is editable and visible - let transform = ui - .memory(|m| m.layer_transforms.get(&ui.layer_id()).copied()) + let to_global = ui + .ctx() + .layer_transform_to_global(ui.layer_id()) .unwrap_or_default(); ui.ctx().output_mut(|o| { o.ime = Some(crate::output::IMEOutput { - rect: transform * rect, - cursor_rect: transform * primary_cursor_rect, + rect: to_global * rect, + cursor_rect: to_global * primary_cursor_rect, }); }); }