Skip to content

Commit

Permalink
Allow setting a layer as a sublayer of another (#4690)
Browse files Browse the repository at this point in the history
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 <#4128>

---------

Co-authored-by: Emil Ernerfeldt <[email protected]>
  • Loading branch information
YgorSouza and emilk authored Jun 23, 2024
1 parent 8cef6fc commit b1dc059
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
11 changes: 11 additions & 0 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
44 changes: 42 additions & 2 deletions crates/egui/src/memory.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1042,27 +1047,62 @@ 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;

std::mem::swap(visible_last_frame, visible_current_frame);
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);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/egui_demo_lib/src/demo/pan_zoom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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);
}
}
}

0 comments on commit b1dc059

Please sign in to comment.