use crate::core::ElementId;
use crate::event::EventManager;
use crate::geometry::{Point, Rect};
use crate::runtime::element::ElementTree;
use crate::view::node::ViewNode;
use std::cell::{Cell, RefCell};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LayerKind {
Content,
Popup,
Overlay,
Modal,
Tooltip,
System,
}
impl LayerKind {
pub fn z_base(&self) -> i32 {
match self {
LayerKind::Content => 0,
LayerKind::Popup => 1000,
LayerKind::Overlay => 2000,
LayerKind::Modal => 3000,
LayerKind::Tooltip => 4000,
LayerKind::System => 5000,
}
}
pub fn z_range(&self) -> std::ops::RangeInclusive<i32> {
self.z_base()..=(self.z_base() + 999)
}
pub fn dispatch_order() -> [LayerKind; 6] {
[
LayerKind::System,
LayerKind::Tooltip,
LayerKind::Modal,
LayerKind::Overlay,
LayerKind::Popup,
LayerKind::Content,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LayerHandle(pub u64);
#[derive(Debug, Clone, Copy)]
pub enum Anchor {
None,
Above { anchor: Rect, gap: f32 },
Below { anchor: Rect, gap: f32 },
LeftOf { anchor: Rect, gap: f32 },
RightOf { anchor: Rect, gap: f32 },
Fixed { x: f32, y: f32 },
ScreenCenter,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusPolicy {
Transparent,
Dismissable,
BlockBelow,
}
#[derive(Debug, Clone, Copy)]
pub struct LayerOptions {
pub visible: bool,
pub dismiss_on_outside: bool,
pub backdrop: Option<crate::geometry::Color>,
}
impl Default for LayerOptions {
fn default() -> Self {
Self {
visible: true,
dismiss_on_outside: false,
backdrop: None,
}
}
}
#[derive(Clone)]
pub struct LayerEntry {
pub handle: LayerHandle,
pub kind: LayerKind,
pub root_id: ElementId,
pub anchor: Anchor,
pub visible: Cell<bool>,
pub focus: FocusPolicy,
pub seq: i32,
pub dismiss_on_outside_click: bool,
pub backdrop: Option<crate::geometry::Color>,
}
impl LayerEntry {
pub fn z(&self) -> i32 {
self.kind.z_base() + self.seq
}
}
impl std::fmt::Debug for LayerEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LayerEntry")
.field("handle", &self.handle)
.field("kind", &self.kind)
.field("root_id", &self.root_id)
.field("anchor", &self.anchor)
.field("visible", &self.visible)
.field("focus", &self.focus)
.field("seq", &self.seq)
.field("dismiss_on_outside_click", &self.dismiss_on_outside_click)
.field("backdrop", &self.backdrop)
.finish()
}
}
pub struct LayerStack {
pub tree: ElementTree,
pub event_manager: RefCell<EventManager>,
entries: RefCell<Vec<LayerEntry>>,
kind_seq: RefCell<[i32; 6]>, next_handle: Cell<u64>,
content_handle: Cell<Option<LayerHandle>>,
default_overlay_handle: Cell<Option<LayerHandle>>,
default_modal_handle: Cell<Option<LayerHandle>>,
}
impl LayerStack {
pub fn new() -> Self {
Self {
tree: ElementTree::new(),
event_manager: RefCell::new(EventManager::new()),
entries: RefCell::new(Vec::new()),
kind_seq: RefCell::new([0; 6]),
next_handle: Cell::new(0),
content_handle: Cell::new(None),
default_overlay_handle: Cell::new(None),
default_modal_handle: Cell::new(None),
}
}
pub fn push(
&mut self,
kind: LayerKind,
view: ViewNode,
anchor: Anchor,
focus: FocusPolicy,
opts: LayerOptions,
) -> LayerHandle {
let id = self.tree.create_subtree_from_node(&view);
let handle = LayerHandle(self.next_handle.get());
self.next_handle.set(handle.0 + 1);
let seq = self.alloc_seq(kind);
let entry = LayerEntry {
handle,
kind,
root_id: id,
anchor,
visible: Cell::new(opts.visible),
focus,
seq,
dismiss_on_outside_click: opts.dismiss_on_outside,
backdrop: opts.backdrop,
};
self.entries.borrow_mut().push(entry);
handle
}
pub fn remove(&mut self, handle: LayerHandle) {
let mut es = self.entries.borrow_mut();
if let Some(pos) = es.iter().position(|e| e.handle == handle) {
let e = es.remove(pos);
self.tree.remove(e.root_id);
}
}
pub fn set_visible(&self, handle: LayerHandle, v: bool) {
if let Some(entry) = self.by_handle(handle) {
entry.visible.set(v);
}
}
pub fn update_view(&mut self, handle: LayerHandle, new_view: ViewNode) {
if let Some(entry) = self.by_handle(handle) {
self.tree.update_node(entry.root_id, &new_view);
}
}
pub fn reanchor(&self, handle: LayerHandle, new_anchor: Anchor) {
let mut es = self.entries.borrow_mut();
if let Some(pos) = es.iter().position(|e| e.handle == handle) {
es[pos].anchor = new_anchor;
}
}
pub fn by_handle(&self, handle: LayerHandle) -> Option<LayerEntry> {
self.entries
.borrow()
.iter()
.find(|e| e.handle == handle)
.cloned()
}
pub fn sorted_entries_for_render(&self) -> Vec<LayerEntry> {
let mut es = self.entries.borrow().clone();
es.sort_by_key(|e| e.z());
es
}
pub fn sorted_entries_for_hit(&self) -> Vec<LayerEntry> {
let mut es = self.entries.borrow().clone();
es.sort_by_key(|e| std::cmp::Reverse(e.z()));
es
}
pub fn top_blocking_modal(&self) -> Option<LayerHandle> {
let es = self.entries.borrow();
es.iter()
.filter(|e| {
e.visible.get() && e.focus == FocusPolicy::BlockBelow && e.kind == LayerKind::Modal
})
.max_by_key(|e| e.z())
.map(|e| e.handle)
}
fn alloc_seq(&self, kind: LayerKind) -> i32 {
let mut arr = self.kind_seq.borrow_mut();
let idx = kind as usize;
let s = arr[idx];
arr[idx] = s + 1;
s
}
pub fn content_root_id(&self) -> Option<ElementId> {
self.content_handle
.get()
.and_then(|h| self.by_handle(h))
.map(|e| e.root_id)
}
pub fn default_overlay_root_id(&self) -> Option<ElementId> {
self.default_overlay_handle
.get()
.and_then(|h| self.by_handle(h))
.map(|e| e.root_id)
}
pub fn default_modal_root_id(&self) -> Option<ElementId> {
self.default_modal_handle
.get()
.and_then(|h| self.by_handle(h))
.map(|e| e.root_id)
}
pub fn set_content_root(&mut self, id: ElementId) {
let handle = LayerHandle(0);
if self.content_handle.get().is_some() {
if let Some(entry) = self.by_handle(handle) {
if entry.root_id != id {
self.tree.remove(entry.root_id);
let mut es = self.entries.borrow_mut();
if let Some(pos) = es.iter().position(|e| e.handle == handle) {
es[pos].root_id = id;
}
}
}
} else {
let handle = LayerHandle(self.next_handle.get());
self.next_handle.set(handle.0 + 1);
let seq = self.alloc_seq(LayerKind::Content);
let entry = LayerEntry {
handle,
kind: LayerKind::Content,
root_id: id,
anchor: Anchor::None,
visible: Cell::new(true),
focus: FocusPolicy::Transparent,
seq,
dismiss_on_outside_click: false,
backdrop: None,
};
self.entries.borrow_mut().push(entry);
self.content_handle.set(Some(handle));
}
}
pub fn push_default_overlay(&mut self, root_id: ElementId) {
if let Some(h) = self.default_overlay_handle.get() {
self.remove(h);
}
let handle = LayerHandle(self.next_handle.get());
self.next_handle.set(handle.0 + 1);
let seq = self.alloc_seq(LayerKind::Overlay);
let entry = LayerEntry {
handle,
kind: LayerKind::Overlay,
root_id,
anchor: Anchor::None,
visible: Cell::new(true),
focus: FocusPolicy::Transparent,
seq,
dismiss_on_outside_click: false,
backdrop: None,
};
self.entries.borrow_mut().push(entry);
self.default_overlay_handle.set(Some(handle));
}
pub fn remove_default_overlay(&mut self) {
if let Some(h) = self.default_overlay_handle.get() {
self.remove(h);
self.default_overlay_handle.set(None);
}
}
pub fn push_default_modal(&mut self, root_id: ElementId) {
if let Some(h) = self.default_modal_handle.get() {
self.remove(h);
}
let handle = LayerHandle(self.next_handle.get());
self.next_handle.set(handle.0 + 1);
let seq = self.alloc_seq(LayerKind::Modal);
let entry = LayerEntry {
handle,
kind: LayerKind::Modal,
root_id,
anchor: Anchor::ScreenCenter,
visible: Cell::new(true),
focus: FocusPolicy::BlockBelow,
seq,
dismiss_on_outside_click: false,
backdrop: Some(crate::geometry::Color::rgba(0, 0, 0, 80)), };
self.entries.borrow_mut().push(entry);
self.default_modal_handle.set(Some(handle));
}
pub fn remove_default_modal(&mut self) {
if let Some(h) = self.default_modal_handle.get() {
self.remove(h);
self.default_modal_handle.set(None);
}
}
pub fn layer_has_content(&self, kind: LayerKind) -> bool {
self.entries
.borrow()
.iter()
.any(|e| e.kind == kind && e.visible.get())
}
fn hit_test_rec(tree: &ElementTree, id: ElementId, px: f32, py: f32) -> Option<ElementId> {
let layout = tree.layout(id);
if !layout.contains(px, py) {
return None;
}
for cid in tree.children_ref(id).iter().rev() {
if let Some(hit) = Self::hit_test_rec(tree, *cid, px, py) {
return Some(hit);
}
}
Some(id)
}
pub fn hit_test_top(&mut self, point: Point) -> Option<(LayerKind, ElementId, LayerHandle)> {
let entries = self.sorted_entries_for_hit();
let cutoff_z = self
.top_blocking_modal()
.and_then(|h| self.by_handle(h))
.map(|e| e.z())
.unwrap_or(-1);
let mut dismissed_handles: Vec<LayerHandle> = Vec::new();
for entry in &entries {
if !entry.visible.get() {
continue;
}
if entry.z() < cutoff_z {
break;
}
if let Some(id) = Self::hit_test_rec(&self.tree, entry.root_id, point.x, point.y) {
return Some((entry.kind, id, entry.handle));
}
if entry.focus == FocusPolicy::Dismissable && entry.dismiss_on_outside_click {
dismissed_handles.push(entry.handle);
}
}
for h in &dismissed_handles {
self.remove(*h);
}
if !dismissed_handles.is_empty() {
}
None
}
pub fn path_to(&self, target: ElementId) -> Vec<ElementId> {
self.tree.path_to(target)
}
}
impl Default for LayerStack {
fn default() -> Self {
Self::new()
}
}