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