use std::collections::HashMap;
use std::sync::{Arc, Weak};
use tracing::{debug, trace};
use crate::error::{LayoutError, LayoutResult};
use crate::types::{ElementId, ElementMetadata, Region, Visibility};
pub type ElementWeakRef = Weak<dyn Element>;
pub type ElementRef = Arc<dyn Element>;
#[derive(Debug, Clone)]
pub struct ElementHandle {
id: ElementId,
weak_ref: ElementWeakRef,
}
impl ElementHandle {
pub fn new(id: ElementId, weak_ref: ElementWeakRef) -> Self {
Self { id, weak_ref }
}
pub fn id(&self) -> ElementId {
self.id
}
pub fn upgrade(&self) -> Option<ElementRef> {
self.weak_ref.upgrade()
}
pub fn is_alive(&self) -> bool {
self.weak_ref.strong_count() > 0
}
}
pub trait Element: Send + Sync {
fn id(&self) -> ElementId;
fn on_metadata_update(&self, metadata: &ElementMetadata);
fn on_render(&self);
fn on_keyboard(&self, event: &KeyboardEvent) -> bool;
fn on_mouse(&self, event: &MouseEvent) -> bool;
fn on_focus_gain(&self);
fn on_focus_loss(&self);
fn on_tick(&self);
}
#[derive(Debug)]
pub struct ElementRegistry {
elements: HashMap<ElementId, (ElementMetadata, ElementWeakRef)>,
}
impl Default for ElementRegistry {
fn default() -> Self {
Self::new()
}
}
impl ElementRegistry {
pub fn new() -> Self {
Self {
elements: HashMap::new(),
}
}
pub fn register(
&mut self,
metadata: ElementMetadata,
element: ElementRef,
) -> LayoutResult<ElementHandle> {
let id = metadata.id;
if self.elements.contains_key(&id) {
return Err(LayoutError::element_already_registered(id));
}
let weak_ref = Arc::downgrade(&element) as ElementWeakRef;
let handle_ref = weak_ref.clone();
self.elements.insert(id, (metadata, weak_ref));
debug!("Registered element: {}", id);
Ok(ElementHandle::new(id, handle_ref))
}
pub fn unregister(&mut self, id: ElementId) -> LayoutResult<()> {
if self.elements.remove(&id).is_none() {
return Err(LayoutError::element_not_found(id));
}
debug!("Unregistered element: {}", id);
Ok(())
}
pub fn get_metadata(&self, id: ElementId) -> LayoutResult<&ElementMetadata> {
self.elements
.get(&id)
.map(|(metadata, _)| metadata)
.ok_or_else(|| LayoutError::element_not_found(id))
}
pub fn get_metadata_mut(&mut self, id: ElementId) -> LayoutResult<&mut ElementMetadata> {
self.elements
.get_mut(&id)
.map(|(metadata, _)| metadata)
.ok_or_else(|| LayoutError::element_not_found(id))
}
pub fn get_weak_ref(&self, id: ElementId) -> LayoutResult<ElementWeakRef> {
self.elements
.get(&id)
.map(|(_, weak_ref)| weak_ref.clone())
.ok_or_else(|| LayoutError::element_not_found(id))
}
pub fn get_strong_ref(&self, id: ElementId) -> LayoutResult<ElementRef> {
self.get_weak_ref(id)?
.upgrade()
.ok_or_else(|| LayoutError::element_not_found(id))
}
pub fn update_metadata(
&mut self,
id: ElementId,
mut update: impl FnMut(&mut ElementMetadata),
) -> LayoutResult<()> {
let (metadata, weak_ref) = self
.elements
.get_mut(&id)
.ok_or_else(|| LayoutError::element_not_found(id))?;
update(metadata);
if let Some(strong_ref) = weak_ref.upgrade() {
strong_ref.on_metadata_update(metadata);
}
trace!("Updated metadata for element: {}", id);
Ok(())
}
pub fn set_visibility(&mut self, id: ElementId, visibility: Visibility) -> LayoutResult<()> {
self.update_metadata(id, |metadata| {
metadata.visibility = visibility;
})
}
pub fn set_z_order(&mut self, id: ElementId, z_order: u32) -> LayoutResult<()> {
self.update_metadata(id, |metadata| {
metadata.z_order = z_order;
})
}
pub fn all_ids(&self) -> Vec<ElementId> {
self.elements.keys().copied().collect()
}
pub fn elements_by_region(&self, region: Region) -> Vec<(ElementId, ElementMetadata)> {
let mut elements: Vec<_> = self
.elements
.iter()
.filter(|(_, (metadata, _))| metadata.region == region && metadata.is_visible())
.map(|(id, (metadata, _))| (*id, metadata.clone()))
.collect();
elements.sort_by(|a, b| b.1.z_order.cmp(&a.1.z_order));
elements
}
pub fn focusable_elements(&self) -> Vec<(ElementId, ElementMetadata)> {
let mut elements: Vec<_> = self
.elements
.iter()
.filter(|(_, (metadata, _))| metadata.can_receive_focus())
.map(|(id, (metadata, _))| (*id, metadata.clone()))
.collect();
elements.sort_by(|a, b| {
(a.1.region as u32)
.cmp(&(b.1.region as u32))
.then(b.1.z_order.cmp(&a.1.z_order))
});
elements
}
pub fn cleanup_dead_refs(&mut self) -> usize {
let initial_count = self.elements.len();
self.elements
.retain(|_, (_, weak_ref)| weak_ref.strong_count() > 0);
let cleaned = initial_count - self.elements.len();
if cleaned > 0 {
debug!("Cleaned up {} dead element references", cleaned);
}
cleaned
}
pub fn len(&self) -> usize {
self.elements.len()
}
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
}
use super::events::{KeyboardEvent, MouseEvent};
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn test_element_id_generation() {
let id1 = ElementId::new();
let id2 = ElementId::new();
assert_ne!(id1, id2);
}
#[test]
fn test_metadata_defaults() {
let id = ElementId::new();
let metadata = ElementMetadata::new(id, Region::Center);
assert_eq!(metadata.id, id);
assert_eq!(metadata.region, Region::Center);
assert!(metadata.is_visible());
assert!(!metadata.focusable);
}
#[test]
fn test_metadata_builders() {
let id = ElementId::new();
let metadata = ElementMetadata::new(id, Region::Top)
.with_visibility(Visibility::Hidden)
.with_z_order(10)
.with_focusable(true)
.with_fixed_height(3);
assert!(!metadata.is_visible());
assert_eq!(metadata.z_order, 10);
assert!(metadata.focusable);
assert_eq!(metadata.fixed_height, Some(3));
}
#[test]
fn test_registry_registration() {
let mut registry = ElementRegistry::new();
assert!(registry.is_empty());
let id = ElementId::new();
let metadata = ElementMetadata::new(id, Region::Center);
let _weak_ref = Weak::<DummyElement>::new();
let handle = registry.register(metadata, Arc::new(DummyElement::new(id)));
assert!(handle.is_ok());
assert_eq!(registry.len(), 1);
}
#[test]
fn test_registry_duplicate_registration() {
let mut registry = ElementRegistry::new();
let id = ElementId::new();
let metadata = ElementMetadata::new(id, Region::Center);
let weak_ref = Weak::<DummyElement>::new();
let _ = registry.register(metadata.clone(), Arc::new(DummyElement::new(id)));
let result = registry.register(metadata, Arc::new(DummyElement::new(id)));
assert!(result.is_err());
}
#[test]
fn test_registry_unregister() {
let mut registry = ElementRegistry::new();
let id = ElementId::new();
let metadata = ElementMetadata::new(id, Region::Center);
let _ = registry.register(metadata, Arc::new(DummyElement::new(id)));
assert_eq!(registry.len(), 1);
let result = registry.unregister(id);
assert!(result.is_ok());
assert!(registry.is_empty());
}
#[test]
fn test_registry_get_metadata() {
let mut registry = ElementRegistry::new();
let id = ElementId::new();
let metadata = ElementMetadata::new(id, Region::Center);
let _ = registry.register(metadata, Arc::new(DummyElement::new(id)));
let result = registry.get_metadata(id);
assert!(result.is_ok());
assert_eq!(result.unwrap().id, id);
}
#[test]
fn test_registry_cleanup() {
let mut registry = ElementRegistry::new();
let id1 = ElementId::new();
let id2 = ElementId::new();
let metadata1 = ElementMetadata::new(id1, Region::Center);
let metadata2 = ElementMetadata::new(id2, Region::Top);
let _ = registry.register(metadata1, Arc::new(DummyElement::new(id1)));
let _ = registry.register(metadata2, Arc::new(DummyElement::new(id2)));
assert_eq!(registry.len(), 2);
let cleaned = registry.cleanup_dead_refs();
assert_eq!(cleaned, 2);
assert!(registry.is_empty());
}
struct DummyElement {
id: ElementId,
}
impl DummyElement {
fn new(id: ElementId) -> Self {
Self { id }
}
}
impl Element for DummyElement {
fn id(&self) -> ElementId {
self.id
}
fn on_metadata_update(&self, _metadata: &ElementMetadata) {}
fn on_render(&self) {}
fn on_keyboard(&self, _event: &KeyboardEvent) -> bool {
false
}
fn on_mouse(&self, _event: &MouseEvent) -> bool {
false
}
fn on_focus_gain(&self) {}
fn on_focus_loss(&self) {}
fn on_tick(&self) {}
}
}