diff --git a/src/widget.rs b/src/widget.rs index 63f7ad9..a2e282d 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1,3 +1,11 @@ +//! Core widget infrastructure for OSUI. +//! +//! This module defines the traits and types that power the widget system, including: +//! - `Element` and `Component`: building blocks for renderable and state-carrying objects +//! - `Widget`: container type for wrapping elements and components +//! - `StaticWidget` and `DynWidget`: concrete widget implementations +//! - Dependency tracking and reactive updates for dynamic widgets + use std::{ any::{Any, TypeId}, collections::HashMap, @@ -6,52 +14,57 @@ use std::{ use crate::{render_scope::RenderScope, state::DependencyHandler}; +/// A trait object for any renderable UI element. pub type BoxedElement = Box; + +/// A trait object for any component attached to a widget. pub type BoxedComponent = Box; +/// Core trait for anything that can be rendered in the UI. +/// +/// Elements are responsible for their own rendering logic and can define hooks +/// for lifecycle events and child rendering. pub trait Element: Send + Sync { + /// Called to perform rendering for the element. #[allow(unused)] fn render(&mut self, scope: &mut RenderScope) {} + + /// Called after rendering, for follow-up logic or cleanup. #[allow(unused)] fn after_render(&mut self, scope: &mut RenderScope) {} + + /// Called to draw child widgets, if any. #[allow(unused)] fn draw_child(&self, element: &Arc) {} + + /// Returns a type-erased reference to this object. fn as_any(&self) -> &dyn Any; + + /// Returns a mutable type-erased reference to this object. fn as_any_mut(&mut self) -> &mut dyn Any; } +/// Optional trait for state or metadata attached to widgets. +/// +/// Components can be used to store data such as layout style, +/// animation state, bindings, or other logic. pub trait Component: Send + Sync { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; } +/// Container for a widget during initial construction. +/// +/// Holds the root element and any associated components. pub struct WidgetLoad(BoxedElement, HashMap); -impl WidgetLoad { - pub fn new(e: E) -> Self { - Self(Box::new(e), HashMap::new()) - } - - pub fn component(mut self, c: C) -> Self { - self.1.entry(c.type_id()).or_insert_with(|| Box::new(c)); - self - } - - pub fn set_component(mut self, c: C) -> Self { - self.1.insert(c.type_id(), Box::new(c)); - self - } - - pub fn get(&self) -> Option { - self.1 - .get(&TypeId::of::()) - .and_then(|c| c.as_any().downcast_ref::()) - .map(|c| c.clone()) - } -} - +/// A widget with fixed content and no dynamic behavior. pub struct StaticWidget(Mutex, Mutex>); +/// A widget with dynamic content and dependency tracking. +/// +/// This widget supports reactive updates and can be rebuilt using +/// a provided `FnMut()` function when dependencies change. pub struct DynWidget( Mutex, Mutex>, @@ -60,11 +73,41 @@ pub struct DynWidget( Mutex WidgetLoad + Send + Sync>>>, ); +/// A reference-counted wrapper around either a static or dynamic widget. +/// +/// Use `Arc` as the standard way to store and pass around widgets in the UI tree. pub enum Widget { Static(StaticWidget), Dynamic(DynWidget), } +impl WidgetLoad { + /// Creates a new `WidgetLoad` with a given root element. + pub fn new(e: E) -> Self { + Self(Box::new(e), HashMap::new()) + } + + /// Attaches a component if one of its type doesn't already exist. + pub fn component(mut self, c: C) -> Self { + self.1.entry(c.type_id()).or_insert_with(|| Box::new(c)); + self + } + + /// Replaces any existing component of the same type. + pub fn set_component(mut self, c: C) -> Self { + self.1.insert(c.type_id(), Box::new(c)); + self + } + + /// Attempts to retrieve a component of the given type. + pub fn get(&self) -> Option { + self.1 + .get(&TypeId::of::()) + .and_then(|c| c.as_any().downcast_ref::()) + .map(|c| c.clone()) + } +} + impl Widget { pub fn new_static(e: BoxedElement) -> Self { Self::Static(StaticWidget(Mutex::new(e), Mutex::new(HashMap::new()))) @@ -232,11 +275,13 @@ impl DynWidget { ) } + /// Replace or modify the widget's structure on reload and init. pub fn inject WidgetLoad + 'static + Send + Sync>(&self, f: F) { *self.4.lock().unwrap() = Some(Box::new(f)); self.refresh(); } + /// Rebuild the widget's content by re-evaluating the original function. pub fn refresh(&self) { let mut w = (self.2.lock().unwrap())(); @@ -248,6 +293,7 @@ impl DynWidget { *self.1.lock().unwrap() = w.1; } + /// Re-evaluates the widget if any dependency has changed. pub fn auto_refresh(&self) { for d in self.3.lock().unwrap().iter() { if d.check() { @@ -256,11 +302,13 @@ impl DynWidget { } } + /// Adds a dependency to this widget. pub fn dependency(&self, d: D) { d.add(); self.3.lock().unwrap().push(Box::new(d)); } + /// Adds a boxed dependency. pub fn dependency_box(&self, d: Box) { d.add(); self.3.lock().unwrap().push(d);