document widget.rs (some unfinished) #13

This commit is contained in:
2025-08-01 20:34:32 -05:00
parent d1008a69c9
commit 095aa14b82
+71 -23
View File
@@ -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<dyn Element + Send + Sync>;
/// A trait object for any component attached to a widget.
pub type BoxedComponent = Box<dyn Component + Send + Sync>;
/// 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<Widget>) {}
/// 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<TypeId, BoxedComponent>);
impl WidgetLoad {
pub fn new<E: Element + 'static>(e: E) -> Self {
Self(Box::new(e), HashMap::new())
}
pub fn component<C: Component + 'static>(mut self, c: C) -> Self {
self.1.entry(c.type_id()).or_insert_with(|| Box::new(c));
self
}
pub fn set_component<C: Component + 'static>(mut self, c: C) -> Self {
self.1.insert(c.type_id(), Box::new(c));
self
}
pub fn get<C: Component + 'static + Clone>(&self) -> Option<C> {
self.1
.get(&TypeId::of::<C>())
.and_then(|c| c.as_any().downcast_ref::<C>())
.map(|c| c.clone())
}
}
/// A widget with fixed content and no dynamic behavior.
pub struct StaticWidget(Mutex<BoxedElement>, Mutex<HashMap<TypeId, BoxedComponent>>);
/// 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<BoxedElement>,
Mutex<HashMap<TypeId, BoxedComponent>>,
@@ -60,11 +73,41 @@ pub struct DynWidget(
Mutex<Option<Box<dyn FnMut(WidgetLoad) -> WidgetLoad + Send + Sync>>>,
);
/// A reference-counted wrapper around either a static or dynamic widget.
///
/// Use `Arc<Widget>` 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: Element + 'static>(e: E) -> Self {
Self(Box::new(e), HashMap::new())
}
/// Attaches a component if one of its type doesn't already exist.
pub fn component<C: Component + 'static>(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<C: Component + 'static>(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<C: Component + 'static + Clone>(&self) -> Option<C> {
self.1
.get(&TypeId::of::<C>())
.and_then(|c| c.as_any().downcast_ref::<C>())
.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<F: FnMut(WidgetLoad) -> 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<D: DependencyHandler + 'static>(&self, d: D) {
d.add();
self.3.lock().unwrap().push(Box::new(d));
}
/// Adds a boxed dependency.
pub fn dependency_box(&self, d: Box<dyn DependencyHandler>) {
d.add();
self.3.lock().unwrap().push(d);