egui/ui_stack.rs
1use std::sync::Arc;
2use std::{any::Any, iter::FusedIterator};
3
4use crate::{Direction, Frame, Id, Rect};
5
6/// What kind is this [`crate::Ui`]?
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum UiKind {
9 /// A [`crate::Window`].
10 Window,
11
12 /// A [`crate::CentralPanel`].
13 CentralPanel,
14
15 /// A left [`crate::SidePanel`].
16 LeftPanel,
17
18 /// A right [`crate::SidePanel`].
19 RightPanel,
20
21 /// A top [`crate::TopBottomPanel`].
22 TopPanel,
23
24 /// A bottom [`crate::TopBottomPanel`].
25 BottomPanel,
26
27 /// A [`crate::Frame`].
28 Frame,
29
30 /// A [`crate::ScrollArea`].
31 ScrollArea,
32
33 /// A [`crate::Resize`].
34 Resize,
35
36 /// The content of a regular menu.
37 Menu,
38
39 /// The content of a popup menu.
40 Popup,
41
42 /// A tooltip, as shown by e.g. [`crate::Response::on_hover_ui`].
43 Tooltip,
44
45 /// A picker, such as color picker.
46 Picker,
47
48 /// A table cell (from the `egui_extras` crate).
49 TableCell,
50
51 /// An [`crate::Area`] that is not of any other kind.
52 GenericArea,
53}
54
55impl UiKind {
56 /// Is this any kind of panel?
57 #[inline]
58 pub fn is_panel(&self) -> bool {
59 matches!(
60 self,
61 Self::CentralPanel
62 | Self::LeftPanel
63 | Self::RightPanel
64 | Self::TopPanel
65 | Self::BottomPanel
66 )
67 }
68
69 /// Is this any kind of [`crate::Area`]?
70 #[inline]
71 pub fn is_area(&self) -> bool {
72 match self {
73 Self::CentralPanel
74 | Self::LeftPanel
75 | Self::RightPanel
76 | Self::TopPanel
77 | Self::BottomPanel
78 | Self::Frame
79 | Self::ScrollArea
80 | Self::Resize
81 | Self::TableCell => false,
82
83 Self::Window
84 | Self::Menu
85 | Self::Popup
86 | Self::Tooltip
87 | Self::Picker
88 | Self::GenericArea => true,
89 }
90 }
91}
92
93// ----------------------------------------------------------------------------
94
95/// Information about a [`crate::Ui`] to be included in the corresponding [`UiStack`].
96#[derive(Clone, Default, Debug)]
97pub struct UiStackInfo {
98 pub kind: Option<UiKind>,
99 pub frame: Frame,
100 pub tags: UiTags,
101}
102
103impl UiStackInfo {
104 /// Create a new [`UiStackInfo`] with the given kind and an empty frame.
105 #[inline]
106 pub fn new(kind: UiKind) -> Self {
107 Self {
108 kind: Some(kind),
109 ..Default::default()
110 }
111 }
112
113 #[inline]
114 pub fn with_frame(mut self, frame: Frame) -> Self {
115 self.frame = frame;
116 self
117 }
118
119 /// Insert a tag with no value.
120 #[inline]
121 pub fn with_tag(mut self, key: impl Into<String>) -> Self {
122 self.tags.insert(key, None);
123 self
124 }
125
126 /// Insert a tag with some value.
127 #[inline]
128 pub fn with_tag_value(
129 mut self,
130 key: impl Into<String>,
131 value: impl Any + Send + Sync + 'static,
132 ) -> Self {
133 self.tags.insert(key, Some(Arc::new(value)));
134 self
135 }
136}
137
138// ----------------------------------------------------------------------------
139
140/// User-chosen tags.
141///
142/// You can use this in any way you want,
143/// i.e. to set some tag on a [`crate::Ui`] and then in your own widget check
144/// for the existence of this tag up the [`UiStack`].
145///
146/// Note that egui never sets any tags itself, so this is purely for user code.
147///
148/// All tagging is transient, and will only live as long as the parent [`crate::Ui`], i.e. within a single render frame.
149#[derive(Clone, Default, Debug)]
150pub struct UiTags(pub ahash::HashMap<String, Option<Arc<dyn Any + Send + Sync + 'static>>>);
151
152impl UiTags {
153 #[inline]
154 pub fn insert(
155 &mut self,
156 key: impl Into<String>,
157 value: Option<Arc<dyn Any + Send + Sync + 'static>>,
158 ) {
159 self.0.insert(key.into(), value);
160 }
161
162 #[inline]
163 pub fn contains(&self, key: &str) -> bool {
164 self.0.contains_key(key)
165 }
166
167 /// Get the value of a tag.
168 ///
169 /// Note that `None` is returned both if the key is set to the value `None`,
170 /// and if the key is not set at all.
171 #[inline]
172 pub fn get_any(&self, key: &str) -> Option<&Arc<dyn Any + Send + Sync + 'static>> {
173 self.0.get(key)?.as_ref()
174 }
175
176 /// Get the value of a tag.
177 ///
178 /// Note that `None` is returned both if the key is set to the value `None`,
179 /// and if the key is not set at all.
180 pub fn get_downcast<T: Any + Send + Sync + 'static>(&self, key: &str) -> Option<&T> {
181 self.0.get(key)?.as_ref().and_then(|any| any.downcast_ref())
182 }
183}
184
185// ----------------------------------------------------------------------------
186
187/// Information about a [`crate::Ui`] and its parents.
188///
189/// [`UiStack`] serves to keep track of the current hierarchy of [`crate::Ui`]s, such
190/// that nested widgets or user code may adapt to the surrounding context or obtain layout information
191/// from a [`crate::Ui`] that might be several steps higher in the hierarchy.
192///
193/// Note: since [`UiStack`] contains a reference to its parent, it is both a stack, and a node within
194/// that stack. Most of its methods are about the specific node, but some methods walk up the
195/// hierarchy to provide information about the entire stack.
196#[derive(Debug)]
197pub struct UiStack {
198 // stuff that `Ui::child_ui` can deal with directly
199 pub id: Id,
200 pub info: UiStackInfo,
201 pub layout_direction: Direction,
202 pub min_rect: Rect,
203 pub max_rect: Rect,
204 pub parent: Option<Arc<UiStack>>,
205}
206
207// these methods act on this specific node
208impl UiStack {
209 #[inline]
210 pub fn kind(&self) -> Option<UiKind> {
211 self.info.kind
212 }
213
214 #[inline]
215 pub fn frame(&self) -> &Frame {
216 &self.info.frame
217 }
218
219 /// User tags.
220 #[inline]
221 pub fn tags(&self) -> &UiTags {
222 &self.info.tags
223 }
224
225 /// Is this [`crate::Ui`] a panel?
226 #[inline]
227 pub fn is_panel_ui(&self) -> bool {
228 self.kind().map_or(false, |kind| kind.is_panel())
229 }
230
231 /// Is this a root [`crate::Ui`], i.e. created with [`crate::Ui::new()`]?
232 #[inline]
233 pub fn is_root_ui(&self) -> bool {
234 self.parent.is_none()
235 }
236
237 /// This this [`crate::Ui`] a [`crate::Frame`] with a visible stroke?
238 #[inline]
239 pub fn has_visible_frame(&self) -> bool {
240 !self.info.frame.stroke.is_empty()
241 }
242}
243
244// these methods act on the entire stack
245impl UiStack {
246 /// Return an iterator that walks the stack from this node to the root.
247 #[allow(clippy::iter_without_into_iter)]
248 pub fn iter(&self) -> UiStackIterator<'_> {
249 UiStackIterator { next: Some(self) }
250 }
251
252 /// Check if this node is or is contained in a [`crate::Ui`] of a specific kind.
253 pub fn contained_in(&self, kind: UiKind) -> bool {
254 self.iter().any(|frame| frame.kind() == Some(kind))
255 }
256}
257
258// ----------------------------------------------------------------------------
259
260/// Iterator that walks up a stack of `StackFrame`s.
261///
262/// See [`UiStack::iter`].
263pub struct UiStackIterator<'a> {
264 next: Option<&'a UiStack>,
265}
266
267impl<'a> Iterator for UiStackIterator<'a> {
268 type Item = &'a UiStack;
269
270 #[inline]
271 fn next(&mut self) -> Option<Self::Item> {
272 let current = self.next;
273 self.next = current.and_then(|frame| frame.parent.as_deref());
274 current
275 }
276}
277
278impl<'a> FusedIterator for UiStackIterator<'a> {}