egui/
layers.rs

1//! Handles paint layers, i.e. how things
2//! are sometimes painted behind or in front of other things.
3
4use crate::{Id, *};
5use epaint::{emath::TSTransform, ClippedShape, Shape};
6
7/// Different layer categories
8#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10pub enum Order {
11    /// Painted behind all floating windows
12    Background,
13
14    /// Special layer between panels and windows
15    PanelResizeLine,
16
17    /// Normal moveable windows that you reorder by click
18    Middle,
19
20    /// Popups, menus etc that should always be painted on top of windows
21    /// Foreground objects can also have tooltips
22    Foreground,
23
24    /// Things floating on top of everything else, like tooltips.
25    /// You cannot interact with these.
26    Tooltip,
27
28    /// Debug layer, always painted last / on top
29    Debug,
30}
31
32impl Order {
33    const COUNT: usize = 6;
34    const ALL: [Self; Self::COUNT] = [
35        Self::Background,
36        Self::PanelResizeLine,
37        Self::Middle,
38        Self::Foreground,
39        Self::Tooltip,
40        Self::Debug,
41    ];
42    pub const TOP: Self = Self::Debug;
43
44    #[inline(always)]
45    pub fn allow_interaction(&self) -> bool {
46        match self {
47            Self::Background
48            | Self::PanelResizeLine
49            | Self::Middle
50            | Self::Foreground
51            | Self::Tooltip
52            | Self::Debug => true,
53        }
54    }
55
56    /// Short and readable summary
57    pub fn short_debug_format(&self) -> &'static str {
58        match self {
59            Self::Background => "backg",
60            Self::PanelResizeLine => "panel",
61            Self::Middle => "middl",
62            Self::Foreground => "foreg",
63            Self::Tooltip => "toolt",
64            Self::Debug => "debug",
65        }
66    }
67}
68
69/// An identifier for a paint layer.
70/// Also acts as an identifier for [`Area`]:s.
71#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
72#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
73pub struct LayerId {
74    pub order: Order,
75    pub id: Id,
76}
77
78impl LayerId {
79    pub fn new(order: Order, id: Id) -> Self {
80        Self { order, id }
81    }
82
83    pub fn debug() -> Self {
84        Self {
85            order: Order::Debug,
86            id: Id::new("debug"),
87        }
88    }
89
90    pub fn background() -> Self {
91        Self {
92            order: Order::Background,
93            id: Id::new("background"),
94        }
95    }
96
97    #[inline(always)]
98    pub fn allow_interaction(&self) -> bool {
99        self.order.allow_interaction()
100    }
101
102    /// Short and readable summary
103    pub fn short_debug_format(&self) -> String {
104        format!(
105            "{} {}",
106            self.order.short_debug_format(),
107            self.id.short_debug_format()
108        )
109    }
110}
111
112/// A unique identifier of a specific [`Shape`] in a [`PaintList`].
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115pub struct ShapeIdx(pub usize);
116
117/// A list of [`Shape`]s paired with a clip rectangle.
118#[derive(Clone, Default)]
119pub struct PaintList(Vec<ClippedShape>);
120
121impl PaintList {
122    #[inline(always)]
123    pub fn is_empty(&self) -> bool {
124        self.0.is_empty()
125    }
126
127    /// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.
128    #[inline(always)]
129    pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
130        let idx = ShapeIdx(self.0.len());
131        self.0.push(ClippedShape { clip_rect, shape });
132        idx
133    }
134
135    pub fn extend<I: IntoIterator<Item = Shape>>(&mut self, clip_rect: Rect, shapes: I) {
136        self.0.extend(
137            shapes
138                .into_iter()
139                .map(|shape| ClippedShape { clip_rect, shape }),
140        );
141    }
142
143    /// Modify an existing [`Shape`].
144    ///
145    /// Sometimes you want to paint a frame behind some contents, but don't know how large the frame needs to be
146    /// until the contents have been added, and therefor also painted to the [`PaintList`].
147    ///
148    /// The solution is to allocate a [`Shape`] using `let idx = paint_list.add(cr, Shape::Noop);`
149    /// and then later setting it using `paint_list.set(idx, cr, frame);`.
150    #[inline(always)]
151    pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
152        self.0[idx.0] = ClippedShape { clip_rect, shape };
153    }
154
155    /// Set the given shape to be empty (a `Shape::Noop`).
156    #[inline(always)]
157    pub fn reset_shape(&mut self, idx: ShapeIdx) {
158        self.0[idx.0].shape = Shape::Noop;
159    }
160
161    /// Transform each [`Shape`] and clip rectangle by this much, in-place
162    pub fn transform(&mut self, transform: TSTransform) {
163        for ClippedShape { clip_rect, shape } in &mut self.0 {
164            *clip_rect = transform.mul_rect(*clip_rect);
165            shape.transform(transform);
166        }
167    }
168
169    /// Read-only access to all held shapes.
170    pub fn all_entries(&self) -> impl ExactSizeIterator<Item = &ClippedShape> {
171        self.0.iter()
172    }
173}
174
175/// This is where painted [`Shape`]s end up during a frame.
176#[derive(Clone, Default)]
177pub struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);
178
179impl GraphicLayers {
180    /// Get or insert the [`PaintList`] for the given [`LayerId`].
181    pub fn entry(&mut self, layer_id: LayerId) -> &mut PaintList {
182        self.0[layer_id.order as usize]
183            .entry(layer_id.id)
184            .or_default()
185    }
186
187    /// Get the [`PaintList`] for the given [`LayerId`].
188    pub fn get(&self, layer_id: LayerId) -> Option<&PaintList> {
189        self.0[layer_id.order as usize].get(&layer_id.id)
190    }
191
192    /// Get the [`PaintList`] for the given [`LayerId`].
193    pub fn get_mut(&mut self, layer_id: LayerId) -> Option<&mut PaintList> {
194        self.0[layer_id.order as usize].get_mut(&layer_id.id)
195    }
196
197    pub fn drain(
198        &mut self,
199        area_order: &[LayerId],
200        transforms: &ahash::HashMap<LayerId, TSTransform>,
201    ) -> Vec<ClippedShape> {
202        crate::profile_function!();
203
204        let mut all_shapes: Vec<_> = Default::default();
205
206        for &order in &Order::ALL {
207            let order_map = &mut self.0[order as usize];
208
209            // If a layer is empty at the start of the frame
210            // then nobody has added to it, and it is old and defunct.
211            // Free it to save memory:
212            order_map.retain(|_, list| !list.is_empty());
213
214            // First do the layers part of area_order:
215            for layer_id in area_order {
216                if layer_id.order == order {
217                    if let Some(list) = order_map.get_mut(&layer_id.id) {
218                        if let Some(transform) = transforms.get(layer_id) {
219                            for clipped_shape in &mut list.0 {
220                                clipped_shape.clip_rect = *transform * clipped_shape.clip_rect;
221                                clipped_shape.shape.transform(*transform);
222                            }
223                        }
224                        all_shapes.append(&mut list.0);
225                    }
226                }
227            }
228
229            // Also draw areas that are missing in `area_order`:
230            for (id, list) in order_map {
231                let layer_id = LayerId::new(order, *id);
232
233                if let Some(transform) = transforms.get(&layer_id) {
234                    for clipped_shape in &mut list.0 {
235                        clipped_shape.clip_rect = *transform * clipped_shape.clip_rect;
236                        clipped_shape.shape.transform(*transform);
237                    }
238                }
239
240                all_shapes.append(&mut list.0);
241            }
242        }
243
244        all_shapes
245    }
246}