use std::sync::Arc;
use std::{any::Any, iter::FusedIterator};
use crate::{Direction, Frame, Id, Rect};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UiKind {
Window,
CentralPanel,
LeftPanel,
RightPanel,
TopPanel,
BottomPanel,
Modal,
Frame,
ScrollArea,
Resize,
Menu,
Popup,
Tooltip,
Picker,
TableCell,
GenericArea,
Collapsible,
}
impl UiKind {
#[inline]
pub fn is_panel(&self) -> bool {
matches!(
self,
Self::CentralPanel
| Self::LeftPanel
| Self::RightPanel
| Self::TopPanel
| Self::BottomPanel
)
}
#[inline]
pub fn is_area(&self) -> bool {
match self {
Self::CentralPanel
| Self::LeftPanel
| Self::RightPanel
| Self::TopPanel
| Self::BottomPanel
| Self::Frame
| Self::ScrollArea
| Self::Resize
| Self::Collapsible
| Self::TableCell => false,
Self::Window
| Self::Menu
| Self::Modal
| Self::Popup
| Self::Tooltip
| Self::Picker
| Self::GenericArea => true,
}
}
}
#[derive(Clone, Default, Debug)]
pub struct UiStackInfo {
pub kind: Option<UiKind>,
pub frame: Frame,
pub tags: UiTags,
}
impl UiStackInfo {
#[inline]
pub fn new(kind: UiKind) -> Self {
Self {
kind: Some(kind),
..Default::default()
}
}
#[inline]
pub fn with_frame(mut self, frame: Frame) -> Self {
self.frame = frame;
self
}
#[inline]
pub fn with_tag(mut self, key: impl Into<String>) -> Self {
self.tags.insert(key, None);
self
}
#[inline]
pub fn with_tag_value(
mut self,
key: impl Into<String>,
value: impl Any + Send + Sync + 'static,
) -> Self {
self.tags.insert(key, Some(Arc::new(value)));
self
}
}
#[derive(Clone, Default, Debug)]
pub struct UiTags(pub ahash::HashMap<String, Option<Arc<dyn Any + Send + Sync + 'static>>>);
impl UiTags {
#[inline]
pub fn insert(
&mut self,
key: impl Into<String>,
value: Option<Arc<dyn Any + Send + Sync + 'static>>,
) {
self.0.insert(key.into(), value);
}
#[inline]
pub fn contains(&self, key: &str) -> bool {
self.0.contains_key(key)
}
#[inline]
pub fn get_any(&self, key: &str) -> Option<&Arc<dyn Any + Send + Sync + 'static>> {
self.0.get(key)?.as_ref()
}
pub fn get_downcast<T: Any + Send + Sync + 'static>(&self, key: &str) -> Option<&T> {
self.0.get(key)?.as_ref().and_then(|any| any.downcast_ref())
}
}
#[derive(Debug)]
pub struct UiStack {
pub id: Id,
pub info: UiStackInfo,
pub layout_direction: Direction,
pub min_rect: Rect,
pub max_rect: Rect,
pub parent: Option<Arc<UiStack>>,
}
impl UiStack {
#[inline]
pub fn kind(&self) -> Option<UiKind> {
self.info.kind
}
#[inline]
pub fn frame(&self) -> &Frame {
&self.info.frame
}
#[inline]
pub fn tags(&self) -> &UiTags {
&self.info.tags
}
#[inline]
pub fn is_panel_ui(&self) -> bool {
self.kind().is_some_and(|kind| kind.is_panel())
}
#[inline]
pub fn is_area_ui(&self) -> bool {
self.kind().is_some_and(|kind| kind.is_area())
}
#[inline]
pub fn is_root_ui(&self) -> bool {
self.parent.is_none()
}
#[inline]
pub fn has_visible_frame(&self) -> bool {
!self.info.frame.stroke.is_empty()
}
}
impl UiStack {
#[expect(clippy::iter_without_into_iter)]
pub fn iter(&self) -> UiStackIterator<'_> {
UiStackIterator { next: Some(self) }
}
pub fn contained_in(&self, kind: UiKind) -> bool {
self.iter().any(|frame| frame.kind() == Some(kind))
}
}
pub struct UiStackIterator<'a> {
next: Option<&'a UiStack>,
}
impl<'a> Iterator for UiStackIterator<'a> {
type Item = &'a UiStack;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let current = self.next;
self.next = current.and_then(|frame| frame.parent.as_deref());
current
}
}
impl FusedIterator for UiStackIterator<'_> {}