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> {}