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