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