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::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
collections::HashMap, collections::HashMap,
@@ -6,52 +14,57 @@ use std::{
use crate::{render_scope::RenderScope, state::DependencyHandler}; use crate::{render_scope::RenderScope, state::DependencyHandler};
/// A trait object for any renderable UI element.
pub type BoxedElement = Box<dyn Element + Send + Sync>; 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>; 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 { pub trait Element: Send + Sync {
/// Called to perform rendering for the element.
#[allow(unused)] #[allow(unused)]
fn render(&mut self, scope: &mut RenderScope) {} fn render(&mut self, scope: &mut RenderScope) {}
/// Called after rendering, for follow-up logic or cleanup.
#[allow(unused)] #[allow(unused)]
fn after_render(&mut self, scope: &mut RenderScope) {} fn after_render(&mut self, scope: &mut RenderScope) {}
/// Called to draw child widgets, if any.
#[allow(unused)] #[allow(unused)]
fn draw_child(&self, element: &Arc<Widget>) {} fn draw_child(&self, element: &Arc<Widget>) {}
/// Returns a type-erased reference to this object.
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
/// Returns a mutable type-erased reference to this object.
fn as_any_mut(&mut self) -> &mut dyn Any; 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 { pub trait Component: Send + Sync {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut 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>); pub struct WidgetLoad(BoxedElement, HashMap<TypeId, BoxedComponent>);
impl WidgetLoad { /// A widget with fixed content and no dynamic behavior.
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())
}
}
pub struct StaticWidget(Mutex<BoxedElement>, Mutex<HashMap<TypeId, BoxedComponent>>); 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( pub struct DynWidget(
Mutex<BoxedElement>, Mutex<BoxedElement>,
Mutex<HashMap<TypeId, BoxedComponent>>, Mutex<HashMap<TypeId, BoxedComponent>>,
@@ -60,11 +73,41 @@ pub struct DynWidget(
Mutex<Option<Box<dyn FnMut(WidgetLoad) -> WidgetLoad + Send + Sync>>>, 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 { pub enum Widget {
Static(StaticWidget), Static(StaticWidget),
Dynamic(DynWidget), 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 { impl Widget {
pub fn new_static(e: BoxedElement) -> Self { pub fn new_static(e: BoxedElement) -> Self {
Self::Static(StaticWidget(Mutex::new(e), Mutex::new(HashMap::new()))) 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) { pub fn inject<F: FnMut(WidgetLoad) -> WidgetLoad + 'static + Send + Sync>(&self, f: F) {
*self.4.lock().unwrap() = Some(Box::new(f)); *self.4.lock().unwrap() = Some(Box::new(f));
self.refresh(); self.refresh();
} }
/// Rebuild the widget's content by re-evaluating the original function.
pub fn refresh(&self) { pub fn refresh(&self) {
let mut w = (self.2.lock().unwrap())(); let mut w = (self.2.lock().unwrap())();
@@ -248,6 +293,7 @@ impl DynWidget {
*self.1.lock().unwrap() = w.1; *self.1.lock().unwrap() = w.1;
} }
/// Re-evaluates the widget if any dependency has changed.
pub fn auto_refresh(&self) { pub fn auto_refresh(&self) {
for d in self.3.lock().unwrap().iter() { for d in self.3.lock().unwrap().iter() {
if d.check() { if d.check() {
@@ -256,11 +302,13 @@ impl DynWidget {
} }
} }
/// Adds a dependency to this widget.
pub fn dependency<D: DependencyHandler + 'static>(&self, d: D) { pub fn dependency<D: DependencyHandler + 'static>(&self, d: D) {
d.add(); d.add();
self.3.lock().unwrap().push(Box::new(d)); self.3.lock().unwrap().push(Box::new(d));
} }
/// Adds a boxed dependency.
pub fn dependency_box(&self, d: Box<dyn DependencyHandler>) { pub fn dependency_box(&self, d: Box<dyn DependencyHandler>) {
d.add(); d.add();
self.3.lock().unwrap().push(d); self.3.lock().unwrap().push(d);