document widget.rs (some unfinished) #13
This commit is contained in:
+71
-23
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user