//! Widget utilities for defining, registering and updating widgets use std::sync::{Arc, RwLock}; use rustc_hash::FxHashMap; use shipyard::Component; use crate::{ node::{FromAnyValue, NodeType}, node_ref::AttributeMask, node_watcher::{AttributeWatcher, NodeWatcher}, prelude::{NodeImmutable, NodeMut, RealDom}, NodeId, }; /// A watcher that handlers registering and updating widgets #[derive(Default, Clone)] pub struct WidgetWatcher { inner: Arc>>, } impl NodeWatcher for WidgetWatcher { fn on_node_added(&mut self, node: NodeMut) { let mut inner = self.inner.write().unwrap(); inner.on_node_added(node); } fn on_node_removed(&mut self, node: NodeMut) { let mut inner = self.inner.write().unwrap(); inner.on_node_removed(node); } } impl WidgetWatcher { /// Register a widget pub fn register_widget + 'static, O: WidgetUpdater>(&mut self) { let mut inner = self.inner.write().unwrap(); inner.builders.insert( W::NAME, WidgetBuilder { create: |mut node| Box::new(W::create(&mut node)), }, ); } /// Attach the widget watcher to the RealDom pub fn attach(&self, dom: &mut RealDom) { dom.add_node_watcher(self.clone()); dom.add_attribute_watcher(self.clone()); } } impl AttributeWatcher for WidgetWatcher { fn on_attributes_changed(&self, node: NodeMut, attributes: &AttributeMask) { let mut inner = self.inner.write().unwrap(); if let Some(widget) = inner.widgets.get_mut(&node.id()) { widget.dyn_widget.attributes_changed(node, attributes); } } } #[derive(Default)] struct WidgetWatcherInner { builders: FxHashMap<&'static str, WidgetBuilder>, widgets: FxHashMap>, } impl NodeWatcher for WidgetWatcherInner { fn on_node_added(&mut self, node: NodeMut) { let node_type = node.node_type(); if let NodeType::Element(el) = &*node_type { if let Some(builder) = self.builders.get(el.tag.as_str()) { drop(node_type); let id = node.id(); let widget = (builder.create)(node); self.widgets.insert(id, BoxedWidget { dyn_widget: widget }); } } } fn on_node_removed(&mut self, node: NodeMut) { self.widgets.remove(&node.id()); } } #[derive(Component)] struct BoxedWidget { dyn_widget: Box>, } struct WidgetBuilder { create: fn(NodeMut) -> Box>, } /// A controlled element (a.k.a. widget) pub trait Widget: Send + Sync + 'static { /// The tag the widget is registered under. const NAME: &'static str; /// Create a new widget. fn create(root: &mut NodeMut) -> Self; /// Called when the attributes of the widget are changed. fn attributes_changed(&mut self, _root: NodeMut, _attributes: &AttributeMask); } /// A factory for creating widgets pub trait WidgetFactory, V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static { /// The tag the widget is registered under. const NAME: &'static str; /// Create a new widget. fn create(root: &mut NodeMut) -> W; } impl, V: FromAnyValue + Send + Sync> WidgetFactory for W { const NAME: &'static str = W::NAME; fn create(root: &mut NodeMut) -> Self { W::create(root) } } /// A trait for updating widgets pub trait WidgetUpdater: Send + Sync + 'static { /// Called when the attributes of the widget are changed. fn attributes_changed(&mut self, _root: NodeMut, _attributes: &AttributeMask); } impl, V: FromAnyValue + Send + Sync> WidgetUpdater for W { fn attributes_changed(&mut self, root: NodeMut, attributes: &AttributeMask) { self.attributes_changed(root, attributes); } }