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?)
This commit is contained in:
Emil Ernerfeldt 2024-12-12 18:29:13 +01:00 committed by GitHub
parent 99c1034cfc
commit de8ac88c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 75 additions and 56 deletions

View File

@ -93,8 +93,8 @@ pub fn show_tooltip_at_pointer<R>(
pointer_rect.min.x = pointer_pos.x; pointer_rect.min.x = pointer_pos.x;
// Transform global coords to layer coords: // Transform global coords to layer coords:
if let Some(transform) = ctx.memory(|m| m.layer_transforms.get(&parent_layer).copied()) { if let Some(from_global) = ctx.layer_transform_from_global(parent_layer) {
pointer_rect = transform.inverse() * pointer_rect; pointer_rect = from_global * pointer_rect;
} }
show_tooltip_at_dyn( show_tooltip_at_dyn(
@ -162,8 +162,8 @@ fn show_tooltip_at_dyn<'c, R>(
) -> R { ) -> R {
// Transform layer coords to global coords: // Transform layer coords to global coords:
let mut widget_rect = *widget_rect; let mut widget_rect = *widget_rect;
if let Some(transform) = ctx.memory(|m| m.layer_transforms.get(&parent_layer).copied()) { if let Some(to_global) = ctx.layer_transform_to_global(parent_layer) {
widget_rect = transform * widget_rect; widget_rect = to_global * widget_rect;
} }
remember_that_tooltip_was_shown(ctx); remember_that_tooltip_was_shown(ctx);
@ -404,11 +404,12 @@ pub fn popup_above_or_below_widget<R>(
AboveOrBelow::Above => (widget_response.rect.left_top(), Align2::LEFT_BOTTOM), AboveOrBelow::Above => (widget_response.rect.left_top(), Align2::LEFT_BOTTOM),
AboveOrBelow::Below => (widget_response.rect.left_bottom(), Align2::LEFT_TOP), AboveOrBelow::Below => (widget_response.rect.left_bottom(), Align2::LEFT_TOP),
}; };
if let Some(transform) = parent_ui
if let Some(to_global) = parent_ui
.ctx() .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()); let frame = Frame::popup(parent_ui.style());

View File

@ -518,7 +518,7 @@ impl ContextImpl {
crate::hit_test::hit_test( crate::hit_test::hit_test(
&viewport.prev_pass.widgets, &viewport.prev_pass.widgets,
&layers, &layers,
&self.memory.layer_transforms, &self.memory.to_global,
pos, pos,
interact_radius, interact_radius,
) )
@ -1329,11 +1329,11 @@ impl Context {
res.is_pointer_button_down_on || res.long_touched || clicked || res.drag_stopped; res.is_pointer_button_down_on || res.long_touched || clicked || res.drag_stopped;
if is_interacted_with { if is_interacted_with {
res.interact_pointer_pos = input.pointer.interact_pos(); res.interact_pointer_pos = input.pointer.interact_pos();
if let (Some(transform), Some(pos)) = ( if let (Some(to_global), Some(pos)) = (
memory.layer_transforms.get(&res.layer_id), memory.to_global.get(&res.layer_id),
&mut res.interact_pointer_pos, &mut res.interact_pointer_pos,
) { ) {
*pos = transform.inverse() * *pos; *pos = to_global.inverse() * *pos;
} }
} }
@ -2381,7 +2381,7 @@ impl ContextImpl {
let shapes = viewport let shapes = viewport
.graphics .graphics
.drain(self.memory.areas().order(), &self.memory.layer_transforms); .drain(self.memory.areas().order(), &self.memory.to_global);
let mut repaint_needed = false; let mut repaint_needed = false;
@ -2697,6 +2697,7 @@ impl Context {
/// Transform the graphics of the given layer. /// Transform the graphics of the given layer.
/// ///
/// This will also affect input. /// 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. /// 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) { pub fn set_transform_layer(&self, layer_id: LayerId, transform: TSTransform) {
self.memory_mut(|m| { self.memory_mut(|m| {
if transform == TSTransform::IDENTITY { if transform == TSTransform::IDENTITY {
m.layer_transforms.remove(&layer_id) m.to_global.remove(&layer_id)
} else { } 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<TSTransform> {
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<TSTransform> {
self.layer_transform_to_global(layer_id)
.map(|t| t.inverse())
}
/// Move all the graphics at the given layer. /// Move all the graphics at the given layer.
/// ///
/// Is used to implement drag-and-drop preview. /// Is used to implement drag-and-drop preview.
@ -2777,12 +2793,11 @@ impl Context {
/// ///
/// See also [`Response::contains_pointer`]. /// See also [`Response::contains_pointer`].
pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool { pub fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool {
let rect = let rect = if let Some(to_global) = self.layer_transform_to_global(layer_id) {
if let Some(transform) = self.memory(|m| m.layer_transforms.get(&layer_id).copied()) { to_global * rect
transform * rect } else {
} else { rect
rect };
};
if !rect.is_positive() { if !rect.is_positive() {
return false; return false;
} }

View File

@ -35,7 +35,7 @@ pub struct WidgetHits {
pub fn hit_test( pub fn hit_test(
widgets: &WidgetRects, widgets: &WidgetRects,
layer_order: &[LayerId], layer_order: &[LayerId],
layer_transforms: &HashMap<LayerId, TSTransform>, layer_to_global: &HashMap<LayerId, TSTransform>,
pos: Pos2, pos: Pos2,
search_radius: f32, search_radius: f32,
) -> WidgetHits { ) -> WidgetHits {
@ -44,9 +44,9 @@ pub fn hit_test(
let search_radius_sq = search_radius * search_radius; let search_radius_sq = search_radius * search_radius;
// Transform the position into the local coordinate space of each layer: // Transform the position into the local coordinate space of each layer:
let pos_in_layers: HashMap<LayerId, Pos2> = layer_transforms let pos_in_layers: HashMap<LayerId, Pos2> = layer_to_global
.iter() .iter()
.map(|(layer_id, t)| (*layer_id, t.inverse() * pos)) .map(|(layer_id, to_global)| (*layer_id, to_global.inverse() * pos))
.collect(); .collect();
let mut closest_dist_sq = f32::INFINITY; let mut closest_dist_sq = f32::INFINITY;

View File

@ -213,7 +213,7 @@ impl GraphicLayers {
pub fn drain( pub fn drain(
&mut self, &mut self,
area_order: &[LayerId], area_order: &[LayerId],
transforms: &ahash::HashMap<LayerId, TSTransform>, to_global: &ahash::HashMap<LayerId, TSTransform>,
) -> Vec<ClippedShape> { ) -> Vec<ClippedShape> {
crate::profile_function!(); crate::profile_function!();
@ -231,10 +231,10 @@ impl GraphicLayers {
for layer_id in area_order { for layer_id in area_order {
if layer_id.order == order { if layer_id.order == order {
if let Some(list) = order_map.get_mut(&layer_id.id) { 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 { for clipped_shape in &mut list.0 {
clipped_shape.clip_rect = *transform * clipped_shape.clip_rect; clipped_shape.clip_rect = *to_global * clipped_shape.clip_rect;
clipped_shape.shape.transform(*transform); clipped_shape.shape.transform(*to_global);
} }
} }
all_shapes.append(&mut list.0); all_shapes.append(&mut list.0);
@ -246,10 +246,10 @@ impl GraphicLayers {
for (id, list) in order_map { for (id, list) in order_map {
let layer_id = LayerId::new(order, *id); 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 { for clipped_shape in &mut list.0 {
clipped_shape.clip_rect = *transform * clipped_shape.clip_rect; clipped_shape.clip_rect = *to_global * clipped_shape.clip_rect;
clipped_shape.shape.transform(*transform); clipped_shape.shape.transform(*to_global);
} }
} }

View File

@ -95,8 +95,13 @@ pub struct Memory {
#[cfg_attr(feature = "persistence", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
everything_is_visible: bool, everything_is_visible: bool,
/// Transforms per layer /// Transforms per layer.
pub layer_transforms: HashMap<LayerId, TSTransform>, ///
/// 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<LayerId, TSTransform>,
// ------------------------------------------------- // -------------------------------------------------
// Per-viewport: // Per-viewport:
@ -120,7 +125,7 @@ impl Default for Memory {
focus: Default::default(), focus: Default::default(),
viewport_id: Default::default(), viewport_id: Default::default(),
areas: Default::default(), areas: Default::default(),
layer_transforms: Default::default(), to_global: Default::default(),
popup: Default::default(), popup: Default::default(),
everything_is_visible: Default::default(), everything_is_visible: Default::default(),
add_fonts: Default::default(), add_fonts: Default::default(),
@ -819,7 +824,7 @@ impl Memory {
/// Top-most layer at the given position. /// Top-most layer at the given position.
pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> { pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
self.areas() self.areas()
.layer_id_at(pos, &self.layer_transforms) .layer_id_at(pos, &self.to_global)
.and_then(|layer_id| { .and_then(|layer_id| {
if self.is_above_modal_layer(layer_id) { if self.is_above_modal_layer(layer_id) {
Some(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<TSTransform> {
self.to_global.get(&layer_id).copied()
}
/// An iterator over all layers. Back-to-front, top is last. /// An iterator over all layers. Back-to-front, top is last.
pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ { pub fn layer_ids(&self) -> impl ExactSizeIterator<Item = LayerId> + '_ {
self.areas().order().iter().copied() self.areas().order().iter().copied()
@ -1194,15 +1205,15 @@ impl Areas {
pub fn layer_id_at( pub fn layer_id_at(
&self, &self,
pos: Pos2, pos: Pos2,
layer_transforms: &HashMap<LayerId, TSTransform>, layer_to_global: &HashMap<LayerId, TSTransform>,
) -> Option<LayerId> { ) -> Option<LayerId> {
for layer in self.order.iter().rev() { for layer in self.order.iter().rev() {
if self.is_visible(layer) { if self.is_visible(layer) {
if let Some(state) = self.areas.get(&layer.id) { if let Some(state) = self.areas.get(&layer.id) {
let mut rect = state.rect(); let mut rect = state.rect();
if state.interactable { if state.interactable {
if let Some(transform) = layer_transforms.get(layer) { if let Some(to_global) = layer_to_global.get(layer) {
rect = *transform * rect; rect = *to_global * rect;
} }
if rect.contains(pos) { if rect.contains(pos) {

View File

@ -406,11 +406,8 @@ impl MenuRoot {
} }
} }
if let Some(transform) = button if let Some(to_global) = button.ctx.layer_transform_to_global(button.layer_id) {
.ctx pos = to_global * pos;
.memory(|m| m.layer_transforms.get(&button.layer_id).copied())
{
pos = transform * pos;
} }
return MenuResponse::Create(pos, id); return MenuResponse::Create(pos, id);

View File

@ -392,11 +392,8 @@ impl Response {
pub fn drag_delta(&self) -> Vec2 { pub fn drag_delta(&self) -> Vec2 {
if self.dragged() { if self.dragged() {
let mut delta = self.ctx.input(|i| i.pointer.delta()); let mut delta = self.ctx.input(|i| i.pointer.delta());
if let Some(scaling) = self if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) {
.ctx delta *= from_global.scaling;
.memory(|m| m.layer_transforms.get(&self.layer_id).map(|t| t.scaling))
{
delta /= scaling;
} }
delta delta
} else { } else {
@ -478,11 +475,8 @@ impl Response {
pub fn hover_pos(&self) -> Option<Pos2> { pub fn hover_pos(&self) -> Option<Pos2> {
if self.hovered() { if self.hovered() {
let mut pos = self.ctx.input(|i| i.pointer.hover_pos())?; let mut pos = self.ctx.input(|i| i.pointer.hover_pos())?;
if let Some(transform) = self if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) {
.ctx pos = from_global * pos;
.memory(|m| m.layer_transforms.get(&self.layer_id).copied())
{
pos = transform.inverse() * pos;
} }
Some(pos) Some(pos)
} else { } else {

View File

@ -766,14 +766,15 @@ impl<'t> TextEdit<'t> {
} }
// Set IME output (in screen coords) when text is editable and visible // Set IME output (in screen coords) when text is editable and visible
let transform = ui let to_global = ui
.memory(|m| m.layer_transforms.get(&ui.layer_id()).copied()) .ctx()
.layer_transform_to_global(ui.layer_id())
.unwrap_or_default(); .unwrap_or_default();
ui.ctx().output_mut(|o| { ui.ctx().output_mut(|o| {
o.ime = Some(crate::output::IMEOutput { o.ime = Some(crate::output::IMEOutput {
rect: transform * rect, rect: to_global * rect,
cursor_rect: transform * primary_cursor_rect, cursor_rect: to_global * primary_cursor_rect,
}); });
}); });
} }