Browse Source

Allow setting a layer as a sublayer of another (#4690)

When the layers are reordered at the end of the frame, the sublayers are
placed directly above their respective parents. This allows having Areas
inside Windows, e.g., for the pan-zoom container.

* Closes <https://github.com/emilk/egui/issues/4128>

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
pull/4704/head
YgorSouza 5 months ago
committed by GitHub
parent
commit
b1dc059ef3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      crates/egui/src/context.rs
  2. 44
      crates/egui/src/memory.rs
  3. 6
      crates/egui_demo_lib/src/demo/pan_zoom.rs

11
crates/egui/src/context.rs

@ -2374,6 +2374,17 @@ impl Context {
self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id));
}
/// Mark the `child` layer as a sublayer of `parent`.
///
/// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly
/// intended for adding a new [`Area`] inside a [`Window`].
///
/// This currently only supports one level of nesting. If `parent` is a sublayer of another
/// layer, the behavior is unspecified.
pub fn set_sublayer(&self, parent: LayerId, child: LayerId) {
self.memory_mut(|mem| mem.areas_mut().set_sublayer(parent, child));
}
/// Retrieve the [`LayerId`] of the top level windows.
pub fn top_layer_id(&self) -> Option<LayerId> {
self.memory(|mem| mem.areas().top_layer_id(Order::Middle))

44
crates/egui/src/memory.rs

@ -1,6 +1,6 @@
#![warn(missing_docs)] // Let's keep this file well-documented.` to memory.rs
use ahash::HashMap;
use ahash::{HashMap, HashSet};
use epaint::emath::TSTransform;
use crate::{
@ -951,6 +951,11 @@ pub struct Areas {
/// So if you close three windows and then reopen them all in one frame,
/// they will all be sent to the top, but keep their previous internal order.
wants_to_be_on_top: ahash::HashSet<LayerId>,
/// List of sublayers for each layer
///
/// When a layer has sublayers, they are moved directly above it in the ordering.
sublayers: ahash::HashMap<LayerId, HashSet<LayerId>>,
}
impl Areas {
@ -1042,20 +1047,38 @@ impl Areas {
}
}
/// Mark the `child` layer as a sublayer of `parent`.
///
/// Sublayers are moved directly above the parent layer at the end of the frame. This is mainly
/// intended for adding a new [Area](crate::Area) inside a [Window](crate::Window).
///
/// This currently only supports one level of nesting. If `parent` is a sublayer of another
/// layer, the behavior is unspecified.
pub fn set_sublayer(&mut self, parent: LayerId, child: LayerId) {
self.sublayers.entry(parent).or_default().insert(child);
}
pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {
self.order
.iter()
.filter(|layer| layer.order == order)
.filter(|layer| layer.order == order && !self.is_sublayer(layer))
.last()
.copied()
}
pub(crate) fn is_sublayer(&self, layer: &LayerId) -> bool {
self.sublayers
.iter()
.any(|(_, children)| children.contains(layer))
}
pub(crate) fn end_frame(&mut self) {
let Self {
visible_last_frame,
visible_current_frame,
order,
wants_to_be_on_top,
sublayers,
..
} = self;
@ -1063,6 +1086,23 @@ impl Areas {
visible_current_frame.clear();
order.sort_by_key(|layer| (layer.order, wants_to_be_on_top.contains(layer)));
wants_to_be_on_top.clear();
// For all layers with sublayers, put the sublayers directly after the parent layer:
let sublayers = std::mem::take(sublayers);
for (parent, children) in sublayers {
let mut moved_layers = vec![parent];
order.retain(|l| {
if children.contains(l) {
moved_layers.push(*l);
false
} else {
true
}
});
let Some(parent_pos) = order.iter().position(|l| l == &parent) else {
continue;
};
order.splice(parent_pos..=parent_pos, moved_layers);
}
}
}

6
crates/egui_demo_lib/src/demo/pan_zoom.rs

@ -110,11 +110,10 @@ impl crate::View for PanZoom {
.into_iter()
.enumerate()
{
let window_layer = ui.layer_id();
let id = egui::Area::new(id.with(("subarea", i)))
.default_pos(pos)
// Need to cover up the pan_zoom demo window,
// but may also cover over other windows.
.order(egui::Order::Foreground)
.order(egui::Order::Middle)
.show(ui.ctx(), |ui| {
ui.set_clip_rect(transform.inverse() * rect);
egui::Frame::default()
@ -130,6 +129,7 @@ impl crate::View for PanZoom {
.response
.layer_id;
ui.ctx().set_transform_layer(id, transform);
ui.ctx().set_sublayer(window_layer, id);
}
}
}

Loading…
Cancel
Save