egui/
layers.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//! Handles paint layers, i.e. how things
//! are sometimes painted behind or in front of other things.

use crate::{ahash, epaint, Id, IdMap, Rect};
use epaint::{emath::TSTransform, ClippedShape, Shape};

/// Different layer categories
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Order {
    /// Painted behind all floating windows
    Background,

    /// Special layer between panels and windows
    PanelResizeLine,

    /// Normal moveable windows that you reorder by click
    Middle,

    /// Popups, menus etc that should always be painted on top of windows
    /// Foreground objects can also have tooltips
    Foreground,

    /// Things floating on top of everything else, like tooltips.
    /// You cannot interact with these.
    Tooltip,

    /// Debug layer, always painted last / on top
    Debug,
}

impl Order {
    const COUNT: usize = 6;
    const ALL: [Self; Self::COUNT] = [
        Self::Background,
        Self::PanelResizeLine,
        Self::Middle,
        Self::Foreground,
        Self::Tooltip,
        Self::Debug,
    ];
    pub const TOP: Self = Self::Debug;

    #[inline(always)]
    pub fn allow_interaction(&self) -> bool {
        match self {
            Self::Background
            | Self::PanelResizeLine
            | Self::Middle
            | Self::Foreground
            | Self::Tooltip
            | Self::Debug => true,
        }
    }

    /// Short and readable summary
    pub fn short_debug_format(&self) -> &'static str {
        match self {
            Self::Background => "backg",
            Self::PanelResizeLine => "panel",
            Self::Middle => "middl",
            Self::Foreground => "foreg",
            Self::Tooltip => "toolt",
            Self::Debug => "debug",
        }
    }
}

/// An identifier for a paint layer.
/// Also acts as an identifier for [`crate::Area`]:s.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct LayerId {
    pub order: Order,
    pub id: Id,
}

impl LayerId {
    pub fn new(order: Order, id: Id) -> Self {
        Self { order, id }
    }

    pub fn debug() -> Self {
        Self {
            order: Order::Debug,
            id: Id::new("debug"),
        }
    }

    pub fn background() -> Self {
        Self {
            order: Order::Background,
            id: Id::new("background"),
        }
    }

    #[inline(always)]
    pub fn allow_interaction(&self) -> bool {
        self.order.allow_interaction()
    }

    /// Short and readable summary
    pub fn short_debug_format(&self) -> String {
        format!(
            "{} {}",
            self.order.short_debug_format(),
            self.id.short_debug_format()
        )
    }
}

/// A unique identifier of a specific [`Shape`] in a [`PaintList`].

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShapeIdx(pub usize);

/// A list of [`Shape`]s paired with a clip rectangle.
#[derive(Clone, Default)]
pub struct PaintList(Vec<ClippedShape>);

impl PaintList {
    #[inline(always)]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn next_idx(&self) -> ShapeIdx {
        ShapeIdx(self.0.len())
    }

    /// Returns the index of the new [`Shape`] that can be used with `PaintList::set`.
    #[inline(always)]
    pub fn add(&mut self, clip_rect: Rect, shape: Shape) -> ShapeIdx {
        let idx = self.next_idx();
        self.0.push(ClippedShape { clip_rect, shape });
        idx
    }

    pub fn extend<I: IntoIterator<Item = Shape>>(&mut self, clip_rect: Rect, shapes: I) {
        self.0.extend(
            shapes
                .into_iter()
                .map(|shape| ClippedShape { clip_rect, shape }),
        );
    }

    /// Modify an existing [`Shape`].
    ///
    /// Sometimes you want to paint a frame behind some contents, but don't know how large the frame needs to be
    /// until the contents have been added, and therefor also painted to the [`PaintList`].
    ///
    /// The solution is to allocate a [`Shape`] using `let idx = paint_list.add(cr, Shape::Noop);`
    /// and then later setting it using `paint_list.set(idx, cr, frame);`.
    #[inline(always)]
    pub fn set(&mut self, idx: ShapeIdx, clip_rect: Rect, shape: Shape) {
        if self.0.len() <= idx.0 {
            #[cfg(feature = "log")]
            log::warn!("Index {} is out of bounds for PaintList", idx.0);
            return;
        }

        self.0[idx.0] = ClippedShape { clip_rect, shape };
    }

    /// Set the given shape to be empty (a `Shape::Noop`).
    #[inline(always)]
    pub fn reset_shape(&mut self, idx: ShapeIdx) {
        self.0[idx.0].shape = Shape::Noop;
    }

    /// Mutate the shape at the given index, if any.
    pub fn mutate_shape(&mut self, idx: ShapeIdx, f: impl FnOnce(&mut ClippedShape)) {
        self.0.get_mut(idx.0).map(f);
    }

    /// Transform each [`Shape`] and clip rectangle by this much, in-place
    pub fn transform(&mut self, transform: TSTransform) {
        for ClippedShape { clip_rect, shape } in &mut self.0 {
            *clip_rect = transform.mul_rect(*clip_rect);
            shape.transform(transform);
        }
    }

    /// Transform each [`Shape`] and clip rectangle in range by this much, in-place
    pub fn transform_range(&mut self, start: ShapeIdx, end: ShapeIdx, transform: TSTransform) {
        for ClippedShape { clip_rect, shape } in &mut self.0[start.0..end.0] {
            *clip_rect = transform.mul_rect(*clip_rect);
            shape.transform(transform);
        }
    }

    /// Read-only access to all held shapes.
    pub fn all_entries(&self) -> impl ExactSizeIterator<Item = &ClippedShape> {
        self.0.iter()
    }
}

/// This is where painted [`Shape`]s end up during a frame.
#[derive(Clone, Default)]
pub struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);

impl GraphicLayers {
    /// Get or insert the [`PaintList`] for the given [`LayerId`].
    pub fn entry(&mut self, layer_id: LayerId) -> &mut PaintList {
        self.0[layer_id.order as usize]
            .entry(layer_id.id)
            .or_default()
    }

    /// Get the [`PaintList`] for the given [`LayerId`].
    pub fn get(&self, layer_id: LayerId) -> Option<&PaintList> {
        self.0[layer_id.order as usize].get(&layer_id.id)
    }

    /// Get the [`PaintList`] for the given [`LayerId`].
    pub fn get_mut(&mut self, layer_id: LayerId) -> Option<&mut PaintList> {
        self.0[layer_id.order as usize].get_mut(&layer_id.id)
    }

    pub fn drain(
        &mut self,
        area_order: &[LayerId],
        transforms: &ahash::HashMap<LayerId, TSTransform>,
    ) -> Vec<ClippedShape> {
        crate::profile_function!();

        let mut all_shapes: Vec<_> = Default::default();

        for &order in &Order::ALL {
            let order_map = &mut self.0[order as usize];

            // If a layer is empty at the start of the frame
            // then nobody has added to it, and it is old and defunct.
            // Free it to save memory:
            order_map.retain(|_, list| !list.is_empty());

            // First do the layers part of area_order:
            for layer_id in area_order {
                if layer_id.order == order {
                    if let Some(list) = order_map.get_mut(&layer_id.id) {
                        if let Some(transform) = transforms.get(layer_id) {
                            for clipped_shape in &mut list.0 {
                                clipped_shape.clip_rect = *transform * clipped_shape.clip_rect;
                                clipped_shape.shape.transform(*transform);
                            }
                        }
                        all_shapes.append(&mut list.0);
                    }
                }
            }

            // Also draw areas that are missing in `area_order`:
            for (id, list) in order_map {
                let layer_id = LayerId::new(order, *id);

                if let Some(transform) = transforms.get(&layer_id) {
                    for clipped_shape in &mut list.0 {
                        clipped_shape.clip_rect = *transform * clipped_shape.clip_rect;
                        clipped_shape.shape.transform(*transform);
                    }
                }

                all_shapes.append(&mut list.0);
            }
        }

        all_shapes
    }
}