use serde::{Deserialize, Serialize};
use crate::{Rect, ScreenPosition, Size, wire::LogicalOverlay};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct RenderedOverlay {
pub logical: LogicalOverlay,
pub position: ScreenPosition,
pub size: Size,
pub z_index: u32,
pub is_modal: bool,
}
impl RenderedOverlay {
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn new(logical: LogicalOverlay, position: ScreenPosition, size: Size) -> Self {
let z_index = logical.priority;
Self {
logical,
position,
size,
z_index,
is_modal: false,
}
}
#[must_use]
pub const fn as_modal(mut self) -> Self {
self.is_modal = true;
self
}
#[must_use]
pub const fn with_z_index(mut self, z_index: u32) -> Self {
self.z_index = z_index;
self
}
#[must_use]
pub const fn bounds(&self) -> Rect {
Rect::new(self.position.x, self.position.y, self.size.width, self.size.height)
}
#[must_use]
pub fn id(&self) -> &str {
&self.logical.id
}
#[must_use]
pub fn kind(&self) -> &str {
&self.logical.kind
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub struct OverlayStack {
overlays: Vec<RenderedOverlay>,
next_z_index: u32,
}
impl OverlayStack {
#[must_use]
pub const fn new() -> Self {
Self {
overlays: Vec::new(),
next_z_index: 0,
}
}
pub fn push(&mut self, mut overlay: RenderedOverlay) {
if overlay.z_index < self.next_z_index {
overlay.z_index = self.next_z_index;
}
self.next_z_index = overlay.z_index + 1;
self.overlays.push(overlay);
}
pub fn remove(&mut self, id: &str) -> bool {
if let Some(pos) = self.overlays.iter().position(|o| o.id() == id) {
self.overlays.remove(pos);
true
} else {
false
}
}
#[must_use]
pub fn get(&self, id: &str) -> Option<&RenderedOverlay> {
self.overlays.iter().find(|o| o.id() == id)
}
#[must_use]
pub fn get_mut(&mut self, id: &str) -> Option<&mut RenderedOverlay> {
self.overlays.iter_mut().find(|o| o.id() == id)
}
#[must_use]
pub fn has_modal(&self) -> bool {
self.overlays.iter().any(|o| o.is_modal)
}
#[must_use]
pub fn topmost_modal(&self) -> Option<&RenderedOverlay> {
self.overlays
.iter()
.filter(|o| o.is_modal)
.max_by_key(|o| o.z_index)
}
#[must_use]
pub fn in_z_order(&self) -> Vec<&RenderedOverlay> {
let mut sorted: Vec<_> = self.overlays.iter().collect();
sorted.sort_by_key(|o| o.z_index);
sorted
}
#[must_use]
pub const fn len(&self) -> usize {
self.overlays.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.overlays.is_empty()
}
pub fn clear(&mut self) {
self.overlays.clear();
self.next_z_index = 0;
}
#[must_use]
pub fn at_position(&self, pos: ScreenPosition) -> Option<&RenderedOverlay> {
self.overlays
.iter()
.filter(|o| o.bounds().contains(pos))
.max_by_key(|o| o.z_index)
}
}
#[cfg(test)]
#[path = "overlay_tests.rs"]
mod tests;