From 75827260cc8da86b171b631d021be18421c3e7d8 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 19:22:02 -0500 Subject: [PATCH 01/12] cleanup elements #13 --- src/elements/div.rs | 18 ++++++------- src/elements/flex.rs | 40 ++++++++++++++--------------- src/elements/mod.rs | 2 -- src/elements/shape.rs | 59 ------------------------------------------- 4 files changed, 29 insertions(+), 90 deletions(-) delete mode 100644 src/elements/shape.rs diff --git a/src/elements/div.rs b/src/elements/div.rs index 8806ad7..28760eb 100644 --- a/src/elements/div.rs +++ b/src/elements/div.rs @@ -10,6 +10,15 @@ pub struct Div { size: (u16, u16), } +impl Div { + pub fn new() -> Self { + Div { + children: Mutex::new(Vec::new()), + size: (0, 0), + } + } +} + impl Element for Div { fn render(&mut self, scope: &mut crate::render_scope::RenderScope) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); @@ -59,12 +68,3 @@ impl Element for Div { self } } - -impl Div { - pub fn new() -> Self { - Div { - children: Mutex::new(Vec::new()), - size: (0, 0), - } - } -} diff --git a/src/elements/flex.rs b/src/elements/flex.rs index ab143dd..3ae6874 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -17,6 +17,26 @@ pub struct FlexCol { size: (u16, u16), } +impl FlexRow { + pub fn new(gap: u16) -> Self { + Self { + children: Mutex::new(Vec::new()), + size: (0, 0), + gap, + } + } +} + +impl FlexCol { + pub fn new(gap: u16) -> Self { + Self { + children: Mutex::new(Vec::new()), + size: (0, 0), + gap, + } + } +} + impl Element for FlexRow { fn render(&mut self, scope: &mut crate::render_scope::RenderScope) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); @@ -73,16 +93,6 @@ impl Element for FlexRow { } } -impl FlexRow { - pub fn new(gap: u16) -> Self { - Self { - children: Mutex::new(Vec::new()), - size: (0, 0), - gap, - } - } -} - impl Element for FlexCol { fn render(&mut self, scope: &mut crate::render_scope::RenderScope) { let (width, height) = scope.get_size_or(self.size.0, self.size.1); @@ -138,13 +148,3 @@ impl Element for FlexCol { self } } - -impl FlexCol { - pub fn new(gap: u16) -> Self { - Self { - children: Mutex::new(Vec::new()), - size: (0, 0), - gap, - } - } -} diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 0dfc814..8ea79e0 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -1,10 +1,8 @@ pub mod div; pub mod flex; -pub mod shape; pub use div::*; pub use flex::*; -pub use shape::*; use crate::Element; diff --git a/src/elements/shape.rs b/src/elements/shape.rs deleted file mode 100644 index 5523b9d..0000000 --- a/src/elements/shape.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::widget::Element; - -pub struct RoundedOutline; - -impl Element for RoundedOutline { - fn render(&mut self, scope: &mut crate::prelude::RenderScope) { - let (w, h) = scope.get_size_or_parent(); - if w > 2 && h > 2 { - let d = "─".repeat(w as usize - 2); - scope.draw_text(&format!( - "╭{d}╮{}\n╰{d}╯", - format!("\n│{}│", " ".repeat(w as usize - 2)).repeat(h as usize - 2), - )); - } - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } -} - -impl RoundedOutline { - pub fn new() -> Self { - Self - } -} - -pub struct Outline; - -impl Element for Outline { - fn render(&mut self, scope: &mut crate::prelude::RenderScope) { - let (w, h) = scope.get_size_or_parent(); - if w > 2 && h > 2 { - let d = "─".repeat(w as usize - 2); - scope.draw_text(&format!( - "┌{d}┐{}\n└{d}┘", - format!("\n│{}│", " ".repeat(w as usize - 2)).repeat(h as usize - 2), - )); - } - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } -} - -impl Outline { - pub fn new() -> Self { - Self - } -} From da1ecb1f2136f4c7ada3a7a8dba33ba306a270c5 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 19:25:14 -0500 Subject: [PATCH 02/12] #13 cleanup extensions --- src/extensions/id.rs | 4 ++-- src/extensions/mod.rs | 2 -- src/extensions/resources.rs | 22 ---------------------- src/extensions/tick.rs | 4 ++-- src/extensions/velocity.rs | 6 ++---- 5 files changed, 6 insertions(+), 32 deletions(-) delete mode 100644 src/extensions/resources.rs diff --git a/src/extensions/id.rs b/src/extensions/id.rs index de629d9..d2be182 100644 --- a/src/extensions/id.rs +++ b/src/extensions/id.rs @@ -4,6 +4,8 @@ use crate::{component, extensions::Extension, widget::Widget, Screen}; pub struct IdExtension(pub Arc); +component!(Id(pub usize)); + impl Extension for Arc {} impl IdExtension { @@ -23,5 +25,3 @@ impl IdExtension { None } } - -component!(Id(pub usize)); diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 3d19d81..ea6607e 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -1,10 +1,8 @@ pub mod id; -pub mod resources; pub mod tick; pub mod velocity; pub use id::*; -pub use resources::*; pub use tick::*; pub use velocity::*; diff --git a/src/extensions/resources.rs b/src/extensions/resources.rs deleted file mode 100644 index 1542111..0000000 --- a/src/extensions/resources.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::sync::{Arc, Mutex}; - -#[derive(Clone)] -pub struct VecResource(Arc>>); - -impl VecResource { - pub fn new() -> Self { - Self(Arc::new(Mutex::new(Box::new(|_| {})))) - } - - pub fn iterate(&self, f: F) { - *self.0.lock().unwrap() = Box::new(f); - } - - pub fn push(&self, e: T) { - let i = self.0.clone(); - - std::thread::spawn(move || { - (i.lock().unwrap())(&e); - }); - } -} diff --git a/src/extensions/tick.rs b/src/extensions/tick.rs index 9ee857a..6334015 100644 --- a/src/extensions/tick.rs +++ b/src/extensions/tick.rs @@ -6,6 +6,8 @@ use crate::{event, Screen}; pub struct TickExtension(pub u16); +event!(TickEvent(pub u32)); + impl Extension for TickExtension { fn init(&mut self, screen: Arc) { let rate_dur = 1000 / self.0 as u64; @@ -25,5 +27,3 @@ impl Extension for TickExtension { }); } } - -event!(TickEvent(pub u32)); diff --git a/src/extensions/velocity.rs b/src/extensions/velocity.rs index 1f3fefb..74aabee 100644 --- a/src/extensions/velocity.rs +++ b/src/extensions/velocity.rs @@ -10,6 +10,8 @@ use crate::{ pub struct VelocityExtension; +component!(Velocity(pub i32, pub i32)); + impl VelocityExtension { fn apply_velocity(ticks: u16, velocity: i32, x: &mut u16) { if velocity.abs() != 0 && ticks as i32 % (1000 / velocity.abs()) == 0 { @@ -20,9 +22,7 @@ impl VelocityExtension { } } } -} -impl VelocityExtension { fn apply_velocity_xy(ticks: u16, widget: &Arc) { if let Some(velocity) = widget.get::() { if let Some(mut t) = widget.get::() { @@ -62,5 +62,3 @@ impl Extension for VelocityExtension { }); } } - -component!(Velocity(pub i32, pub i32)); From 063c90d986a997be66438ecce83b434a24c60733 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 19:44:55 -0500 Subject: [PATCH 03/12] #13 cleanup frontend and widgets --- src/frontend.rs | 4 +-- src/lib.rs | 10 +++---- src/macros.rs | 12 ++++++--- src/widget.rs | 71 ++++++++++++++++++++++++++----------------------- 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/frontend.rs b/src/frontend.rs index 58d4eff..cbc7037 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -7,7 +7,7 @@ use crate::{ }; pub enum RsxElement { - Element(Arc, Rsx), + Element(StaticWidget, Rsx), DynElement( Box WidgetLoad + Send + Sync>, @@ -68,7 +68,7 @@ impl Rsx { )); } - pub fn create_element_static(&mut self, element: Arc, children: Rsx) { + pub fn create_element_static(&mut self, element: StaticWidget, children: Rsx) { self.0.push(RsxElement::Element(element, children)); } diff --git a/src/lib.rs b/src/lib.rs index 4786975..5e3801e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,15 +54,13 @@ impl Screen { } pub fn draw(self: &Arc, element: E) -> Arc { - let w = Arc::new(Widget::Static(Arc::new(StaticWidget::new(Box::new( - element, - ))))); + let w = Arc::new(Widget::Static(StaticWidget::new(Box::new(element)))); self.widgets.lock().unwrap().push(w.clone()); w } pub fn draw_box(self: &Arc, element: BoxedElement) -> Arc { - let w = Arc::new(Widget::Static(Arc::new(StaticWidget::new(element)))); + let w = Arc::new(Widget::Static(StaticWidget::new(element))); self.widgets.lock().unwrap().push(w.clone()); w } @@ -75,7 +73,7 @@ impl Screen { self: &Arc, element: F, ) -> Arc { - let w = Arc::new(Widget::Dynamic(Arc::new(DynWidget::new(element)))); + let w = Arc::new(Widget::Dynamic(DynWidget::new(element))); self.widgets.lock().unwrap().push(w.clone()); w } @@ -84,7 +82,7 @@ impl Screen { self: &Arc, element: Box WidgetLoad + Send + Sync>, ) -> Arc { - let w = Arc::new(Widget::Dynamic(Arc::new(DynWidget::new(element)))); + let w = Arc::new(Widget::Dynamic(DynWidget::new(element))); self.widgets.lock().unwrap().push(w.clone()); w } diff --git a/src/macros.rs b/src/macros.rs index dbdc403..0bc9ed9 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -147,7 +147,9 @@ macro_rules! rsx { macro_rules! rsx_inner { // static ($r:expr, $(@$comp:expr;)* static $s:literal $($rest:tt)*) => { - $r.create_element_static(std::sync::Arc::new($crate::widget::StaticWidget::new(Box::new(format!($s)))) $(.component($comp))* .clone(), $crate::frontend::Rsx(Vec::new())); + let w = $crate::widget::StaticWidget::new(Box::new(format!($s))); + $(w.component($comp);)* + $r.create_element_static(w, $crate::frontend::Rsx(Vec::new())); $crate::rsx_inner! { $r, $($rest)* }; }; @@ -160,13 +162,17 @@ macro_rules! rsx_inner { // static ($r:expr, $(%$dep:ident)* $(@$comp:expr;)* static $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => { - $r.create_element_static(std::sync::Arc::new($crate::widget::StaticWidget::new(Box::new(<$name>::new($($e),*)))) $(.component($comp))* .clone(), $crate::frontend::Rsx(Vec::new())); + let w = $crate::widget::StaticWidget::new(Box::new(<$name>::new($($e),*))); + $(w.component($comp);)* + $r.create_element_static(w, $crate::frontend::Rsx(Vec::new())); $crate::rsx_inner! { $r, $($rest)* }; }; // static ($r:expr, $(@$comp:expr;)* static $name:path { $($inner:tt)* } $($rest:tt)*) => { - $r.create_element_static(std::sync::Arc::new($crate::widget::StaticWidget::new(Box::new(<$name>::new()))) $(.component($comp))* .clone(), $crate::frontend::Rsx(Vec::new())); + let w = $crate::widget::StaticWidget::new(Box::new(<$name>::new())); + $(w.component($comp);)* + $r.create_element_static(w, $crate::frontend::Rsx(Vec::new())); $crate::rsx_inner! { $r, $($rest)* }; }; diff --git a/src/widget.rs b/src/widget.rs index 33dd49e..63f7ad9 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -50,12 +50,38 @@ impl WidgetLoad { } } +pub struct StaticWidget(Mutex, Mutex>); + +pub struct DynWidget( + Mutex, + Mutex>, + Mutex WidgetLoad + Send + Sync>>, + Mutex>>, + Mutex WidgetLoad + Send + Sync>>>, +); + pub enum Widget { - Static(Arc), - Dynamic(Arc), + Static(StaticWidget), + Dynamic(DynWidget), +} + +impl Widget { + pub fn new_static(e: BoxedElement) -> Self { + Self::Static(StaticWidget(Mutex::new(e), Mutex::new(HashMap::new()))) + } + + pub fn new_dyn WidgetLoad + 'static + Send + Sync>(mut e: F) -> Self { + let wl = e(); + Self::Dynamic(DynWidget( + Mutex::new(wl.0), + Mutex::new(wl.1), + Mutex::new(Box::new(e)), + Mutex::new(Vec::new()), + Mutex::new(None), + )) + } } -// === Trait Implementation === impl Widget { pub fn get_elem(&self) -> MutexGuard { match self { @@ -153,9 +179,6 @@ impl Widget { } } -// === Static Widget === -pub struct StaticWidget(Mutex, Mutex>); - impl StaticWidget { fn after_render(&self) {} fn get_elem(&self) -> MutexGuard { @@ -168,18 +191,16 @@ impl StaticWidget { Self(Mutex::new(e), Mutex::new(HashMap::new())) } - pub fn component(self: &Arc, c: C) -> &Arc { + pub fn component(&self, c: C) { self.1 .lock() .unwrap() .entry(c.type_id()) .or_insert_with(|| Box::new(c)); - self } - pub fn set_component(self: &Arc, c: C) -> &Arc { + pub fn set_component(&self, c: C) { self.1.lock().unwrap().insert(c.type_id(), Box::new(c)); - self } pub fn get(&self) -> Option { @@ -192,15 +213,6 @@ impl StaticWidget { } } -// === Dynamic Widget === -pub struct DynWidget( - Mutex, - Mutex>, - Mutex WidgetLoad + Send + Sync>>, - Mutex>>, - Mutex WidgetLoad + Send + Sync>>>, -); - impl DynWidget { fn after_render(&self) {} fn get_elem(&self) -> MutexGuard { @@ -220,15 +232,12 @@ impl DynWidget { ) } - pub fn inject WidgetLoad + 'static + Send + Sync>( - self: &Arc, - f: F, - ) { + pub fn inject WidgetLoad + 'static + Send + Sync>(&self, f: F) { *self.4.lock().unwrap() = Some(Box::new(f)); self.refresh(); } - pub fn refresh(self: &Arc) { + pub fn refresh(&self) { let mut w = (self.2.lock().unwrap())(); if let Some(we) = &mut *self.4.lock().unwrap() { @@ -239,7 +248,7 @@ impl DynWidget { *self.1.lock().unwrap() = w.1; } - pub fn auto_refresh(self: &Arc) { + pub fn auto_refresh(&self) { for d in self.3.lock().unwrap().iter() { if d.check() { self.refresh(); @@ -247,30 +256,26 @@ impl DynWidget { } } - pub fn dependency(self: &Arc, d: D) -> &Arc { + pub fn dependency(&self, d: D) { d.add(); self.3.lock().unwrap().push(Box::new(d)); - self } - pub fn dependency_box(self: &Arc, d: Box) -> &Arc { + pub fn dependency_box(&self, d: Box) { d.add(); self.3.lock().unwrap().push(d); - self } - pub fn component(self: &Arc, c: C) -> &Arc { + pub fn component(&self, c: C) { self.1 .lock() .unwrap() .entry(c.type_id()) .or_insert_with(|| Box::new(c)); - self } - pub fn set_component(self: &Arc, c: C) -> &Arc { + pub fn set_component(&self, c: C) { self.1.lock().unwrap().insert(c.type_id(), Box::new(c)); - self } pub fn get(&self) -> Option { From b45ef950a1b210e4283e735060c9e741348126b2 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 19:47:41 -0500 Subject: [PATCH 04/12] changed % state dependency to be after the components --- src/macros.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 0bc9ed9..a3e2d20 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -153,7 +153,7 @@ macro_rules! rsx_inner { $crate::rsx_inner! { $r, $($rest)* }; }; - ($r:expr, $(%$dep:ident)* $(@$comp:expr;)* $s:literal $($rest:tt)*) => { + ($r:expr, $(@$comp:expr;)* $(%$dep:ident)* $s:literal $($rest:tt)*) => { $r.create_element({ $(let $dep = $dep.clone();)* move || { $crate::widget::WidgetLoad::new(format!($s)) $(.component($comp))* } }, vec![$(Box::new($dep.clone())),*], $crate::frontend::Rsx(Vec::new())); @@ -161,7 +161,7 @@ macro_rules! rsx_inner { }; // static - ($r:expr, $(%$dep:ident)* $(@$comp:expr;)* static $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => { + ($r:expr, $(@$comp:expr;)* static $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => { let w = $crate::widget::StaticWidget::new(Box::new(<$name>::new($($e),*))); $(w.component($comp);)* $r.create_element_static(w, $crate::frontend::Rsx(Vec::new())); @@ -176,14 +176,14 @@ macro_rules! rsx_inner { $crate::rsx_inner! { $r, $($rest)* }; }; - ($r:expr, $(%$dep:ident)* $(@$comp:expr;)* $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => { + ($r:expr, $(@$comp:expr;)* $(%$dep:ident)* $name:path { $($inner:tt)* } ($($e:expr),*) $($rest:tt)*) => { $r.create_element({ $(let $dep = $dep.clone();)* move || { $crate::widget::WidgetLoad::new(<$name>::new($($e),*)) $(.component($comp))* } }, vec![$(Box::new($dep.clone())),*], $crate::rsx!{ $($inner)* }); $crate::rsx_inner! { $r, $($rest)* }; }; - ($r:expr, $(%$dep:ident)* $(@$comp:expr;)* $name:path { $($inner:tt)* } $($rest:tt)*) => { + ($r:expr, $(@$comp:expr;)* $(%$dep:ident)* $name:path { $($inner:tt)* } $($rest:tt)*) => { $r.create_element({ $(let $dep = $dep.clone();)* move || { $crate::widget::WidgetLoad::new(<$name>::new()) $(.component($comp))* } }, vec![$(Box::new($dep.clone())),*], $crate::rsx!{ $($inner)* }); From c2563358b6e4cef3c6a7775ae1feccc5979be51f Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:02:55 -0500 Subject: [PATCH 05/12] document lib #13 --- src/lib.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5e3801e..e60655f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,34 @@ +//! **OSUI** – A Rust Terminal User Interface Library +//! +//! OSUI is a library for building interactive and customizable terminal user interfaces in Rust. +//! It provides a component system, real-time keyboard input handling, and a `rsx!` macro +//! for defining UI elements in a declarative way, however that is optional. +//! +//! ✅ **Features** +//! - 🧱 RSX-like syntax with `rsx!` macro +//! - 🖥️ Virtual screen abstraction +//! - 🎹 Keyboard input handling +//! - 🎯 Component-based design +//! - ⚡ Real-time rendering +//! +//! 🚀 **Quick Example** +//! ```rust +//! use osui::prelude::*; +//! +//! fn main() -> std::io::Result<()> { +//! let screen = Screen::new(); +//! rsx! { +//! "👋 Hello, World!" +//! }.draw(&screen); +//! screen.run() +//! } +//! ``` +//! +//! --- +//! 🧰 For full documentation, visit: [osui.netlify.app/docs](https://osui.netlify.app/docs) +//! +//! 🧪 Examples and demos: [github.com/osui-rs/osui/demos](https://github.com/osui-rs/osui/tree/master/src/demos) + use std::sync::{Arc, Mutex}; use crate::{ From a1499064ac61a0990c37a57550cb6de0de6ffb57 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:16:18 -0500 Subject: [PATCH 06/12] document lib #13 --- src/lib.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e60655f..4182f4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,8 +59,21 @@ pub mod prelude { }; } +/// The main screen abstraction for rendering and managing widgets and extensions. +/// +/// `Screen` holds the root widget list and registered extensions. It provides methods +/// for drawing elements, adding extensions, and running the main rendering loop. +/// +/// # Examples +/// ```rust +/// let screen = Screen::new(); +/// rsx! { "Hello" }.draw(&screen); +/// screen.run()?; +/// ``` pub struct Screen { + /// The list of widgets currently managed by the screen. pub widgets: Mutex>>, + /// Registered extensions for the screen. extensions: Mutex>>>>, } @@ -68,6 +81,10 @@ event!(RenderWrapperEvent(*mut RenderScope)); component!(NoRender); impl RenderWrapperEvent { + /// Returns a mutable reference to the underlying `RenderScope`. + /// + /// # Safety + /// The caller must ensure the pointer is valid for the lifetime of the event. pub fn get_scope(&self) -> &mut RenderScope { unsafe { &mut *self.0 } } @@ -77,6 +94,7 @@ unsafe impl Send for RenderWrapperEvent {} unsafe impl Sync for RenderWrapperEvent {} impl Screen { + /// Creates a new screen instance wrapped in an `Arc`. pub fn new() -> Arc { Arc::new(Self { widgets: Mutex::new(Vec::new()), @@ -84,22 +102,26 @@ impl Screen { }) } + /// Draws a static element and returns its widget handle. pub fn draw(self: &Arc, element: E) -> Arc { let w = Arc::new(Widget::Static(StaticWidget::new(Box::new(element)))); self.widgets.lock().unwrap().push(w.clone()); w } + /// Draws a boxed element and returns its widget handle. pub fn draw_box(self: &Arc, element: BoxedElement) -> Arc { let w = Arc::new(Widget::Static(StaticWidget::new(element))); self.widgets.lock().unwrap().push(w.clone()); w } + /// Adds an existing widget to the screen. pub fn draw_widget(self: &Arc, widget: Arc) { self.widgets.lock().unwrap().push(widget); } + /// Draws a dynamic element using a closure and returns its widget handle. pub fn draw_dyn WidgetLoad + 'static + Send + Sync>( self: &Arc, element: F, @@ -109,6 +131,7 @@ impl Screen { w } + /// Draws a dynamic element from a boxed closure and returns its widget handle. pub fn draw_box_dyn( self: &Arc, element: Box WidgetLoad + Send + Sync>, @@ -118,6 +141,7 @@ impl Screen { w } + /// Registers an extension with the screen. pub fn extension(self: &Arc, ext: E) { self.extensions .lock() @@ -125,6 +149,9 @@ impl Screen { .push(Arc::new(Mutex::new(Box::new(ext)))); } + /// Runs the main rendering loop, calling extensions and rendering widgets. + /// + /// This method blocks and repeatedly renders the screen at a fixed interval. pub fn run(self: &Arc) -> std::io::Result<()> { for ext in self.extensions.lock().unwrap().iter() { ext.lock().unwrap().init(self.clone()); @@ -138,6 +165,9 @@ impl Screen { } } + /// Renders all widgets and applies extensions. + /// + /// This method is called internally by `run`. pub fn render(self: &Arc) -> std::io::Result<()> { let mut scope = RenderScope::new(); let (w, h) = crossterm::terminal::size().unwrap(); From 27580e773a5e04c37a16bed9eb09dc1d092226c0 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:20:20 -0500 Subject: [PATCH 07/12] document frontend.rs #13 --- src/frontend.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/frontend.rs b/src/frontend.rs index cbc7037..f50b086 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -1,3 +1,11 @@ +//! The frontend binder for OSUI. +//! +//! This module defines the internal representation of RSX elements — the tree-like structure used +//! by the `rsx!` macro in OSUI to declaratively build terminal user interfaces. +//! +//! RSX elements can be either static widgets or dynamically loaded ones with dependencies. +//! This structure enables recursive rendering, parent-child composition, and runtime dependency handling. + use std::sync::Arc; use crate::{ @@ -6,9 +14,12 @@ use crate::{ Screen, }; +/// Represents a single element in the RSX tree. pub enum RsxElement { + /// A static widget with children. Element(StaticWidget, Rsx), + /// A dynamically generated widget (e.g., with state) with associated dependencies and children. DynElement( Box WidgetLoad + Send + Sync>, Vec>, @@ -16,13 +27,21 @@ pub enum RsxElement { ), } +/// A container representing a group of RSX elements. +/// This is typically created via the `rsx!` macro and rendered using a `Screen`. pub struct Rsx(pub Vec); impl Rsx { + /// Draws the RSX tree onto the given screen without a parent widget. + /// + /// This is the entry point for rendering the UI. pub fn draw(self, screen: &Arc) { self.draw_parent(screen, None); } + /// Recursively draws the RSX tree with an optional parent widget. + /// + /// Used internally to establish parent-child widget relationships. pub fn draw_parent(self, screen: &Arc, parent: Option>) { for rsx_elem in self.0 { match rsx_elem { @@ -38,6 +57,7 @@ impl Rsx { for d in dep { w.dependency_box(d); } + child.draw_parent(screen, Some(w.clone())); } @@ -55,6 +75,11 @@ impl Rsx { } } + /// Adds a dynamically constructed element to the RSX tree. + /// + /// - `load`: A closure returning a `WidgetLoad` used to generate the widget. + /// - `dependencies`: A list of dependency handlers for state or event updates. + /// - `children`: Child RSX elements. pub fn create_element WidgetLoad + Send + Sync + 'static>( &mut self, load: F, @@ -68,10 +93,12 @@ impl Rsx { )); } + /// Adds a statically defined element to the RSX tree with its children. pub fn create_element_static(&mut self, element: StaticWidget, children: Rsx) { self.0.push(RsxElement::Element(element, children)); } + /// Appends the elements from another `Rsx` tree into this one. pub fn expand(&mut self, other: &mut Rsx) { self.0.append(&mut other.0); } From fc0406e41246d953c41f76092cc913c1e364ca9e Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:23:03 -0500 Subject: [PATCH 08/12] document macros.rs #13 --- src/macros.rs | 133 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 114 insertions(+), 19 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index a3e2d20..7ae8b76 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,24 @@ +/// Declares a struct that implements the `Event` trait. +/// +/// This macro simplifies the creation of event types used within OSUI's reactive system. +/// +/// # Variants +/// +/// - `event!(Name)` +/// Defines a unit struct named `Name`. +/// +/// - `event!(Name { ... })` +/// Defines a named struct with fields. +/// +/// - `event!(Name (...))` +/// Defines a tuple struct. +/// +/// # Examples +/// ```rust +/// event!(Clicked); +/// event!(Resized { width: u32, height: u32 }); +/// event!(Moved(u32, u32)); +/// ``` #[macro_export] macro_rules! event { ($name:ident) => { @@ -36,6 +57,28 @@ macro_rules! event { }; } +/// Declares a struct that implements the `Component` trait. +/// +/// Components allow widgets to extend their behavior or contain additional data. +/// This macro helps avoid boilerplate when defining new components. +/// +/// # Variants +/// +/// - `component!(Name)` +/// Defines a unit struct. +/// +/// - `component!(Name { ... })` +/// Defines a named struct with fields. +/// +/// - `component!(Name (...))` +/// Defines a tuple struct. +/// +/// # Examples +/// ```rust +/// component!(Focusable); +/// component!(Tooltip { text: String }); +/// component!(Size(u32, u32)); +/// ``` #[macro_export] macro_rules! component { ($name:ident) => { @@ -86,35 +129,29 @@ macro_rules! component { }; } -/// Macro to create an event handler closure that calls a method on `self` +/// Creates an event handler closure that calls a method on `self`. /// -/// This macro automates the pattern of capturing `self` as a raw pointer, -/// then passing an event to a method on `self` inside an unsafe closure. +/// Useful when you need to register `'static` event handlers that interact with the current instance. /// -/// # Parameters -/// - `$self_ty`: The self type. -/// - `$self`: The instance variable (usually `self`) to call the method on. -/// - `$events`: The event source which has an `.on` method to register the handler. -/// - `$method`: The method name on `self` to call when an event occurs. +/// # Arguments +/// - `$self_ty`: The type of `self`. +/// - `$self`: The instance variable (usually `self`) being used. +/// - `$events`: The event source object with an `.on` method. +/// - `$method`: The method to call when an event is received. /// -/// # Usage +/// # Example /// ```rust -/// event_handler!(self, events, on_keypress); +/// event_handler!(Self, self, events, on_event); /// ``` -/// -/// This expands roughly to: +/// Expands roughly to: /// ```rust /// let self_ref = self as *mut Self; -/// events.on(move |event| unsafe { (*self_ref).on_keypress(event) }); +/// events.on(move |es, e| unsafe { (*self_ref).on_event(es, e) }); /// ``` /// /// # Safety -/// This macro uses `unsafe` code because it dereferences a raw pointer inside the closure. -/// Ensure that the `self` reference lives at least as long as the closure to avoid undefined behavior. -/// -/// # Why use raw pointers here? -/// Often, event handlers require `'static` closures, but `self` is a stack reference. -/// Capturing `self` directly is not possible, so this workaround uses a raw pointer. +/// This macro uses `unsafe` code to cast `self` to a raw pointer and dereference it. +/// Make sure the reference is valid for the closure’s lifetime. #[macro_export] macro_rules! event_handler { ($self_ty:ty, $self:ident, $events:ident, $method:ident) => {{ @@ -123,6 +160,26 @@ macro_rules! event_handler { }}; } +/// Creates a `Transform` with property overrides. +/// +/// Each key-value pair sets a field on a `Transform` struct. +/// +/// # Example +/// ```rust +/// let t = transform!( +/// x: 10, +/// y: 20, +/// scale: 1.5, +/// ); +/// ``` +/// +/// This expands to: +/// ```rust +/// let mut t = Transform::new(); +/// t.x = 10.into(); +/// t.y = 20.into(); +/// t.scale = 1.5.into(); +/// ``` #[macro_export] macro_rules! transform { ($($f:ident: $v:expr),* $(,)?) => {{ @@ -132,6 +189,22 @@ macro_rules! transform { }}; } +/// Constructs an `Rsx` tree using declarative syntax. +/// +/// This macro is similar to JSX in UI frameworks, allowing you to nest widgets and assign components or dependencies. +/// It expands into a tree of `RsxElement` objects. +/// +/// Internally calls the recursive `rsx_inner!` macro. +/// +/// # Example +/// ```rust +/// rsx! { +/// "Hello" +/// static Label { } ("Text") +/// %state Label { } +/// @Velocity(20, 0); Transform::new(); "Moving Text" +/// } +/// ``` #[macro_export] macro_rules! rsx { ($($inner:tt)*) => {{ @@ -143,6 +216,28 @@ macro_rules! rsx { }}; } +/// Internal macro used by `rsx!` to recursively build `Rsx` trees. +/// +/// This macro handles various syntactic forms used in the declarative layout system: +/// +/// - Static text or components +/// - Dynamic widgets with dependencies (`%dep`) +/// - Component annotations (`@comp`) +/// - Argument passing to constructors +/// - Nesting and expansion from other `Rsx` blocks +/// +/// **This macro is not intended for direct use**; use `rsx!` instead. +/// +/// # Example expansion +/// ```rust +/// rsx! { +/// static Label { } ("Title") +/// %state Label { inner } +/// "Text" +/// } +/// ``` +/// +/// Would produce a nested `Rsx` tree of static and dynamic widgets. #[macro_export] macro_rules! rsx_inner { // static From 1debb6a5768a76bffee0f1d5dbf5115364941a68 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:24:43 -0500 Subject: [PATCH 09/12] document render_scope.rs #13 --- src/render_scope.rs | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/render_scope.rs b/src/render_scope.rs index 85cf3eb..42cdd4d 100644 --- a/src/render_scope.rs +++ b/src/render_scope.rs @@ -1,3 +1,13 @@ +//! Rendering logic and layout scope management for OSUI. +//! +//! `RenderScope` is responsible for handling transformations, parent-child dimensions, +//! stacking draw operations, and rendering styled output to the terminal. +//! +//! This module allows widgets to accumulate rendering commands (text, shapes, colors), +//! and then flush them to the screen with correct styling and positioning. +//! +//! Used internally by OSUI's layout and rendering system. + use std::fmt::Debug; use crate::{ @@ -5,13 +15,21 @@ use crate::{ utils::{self, hex_ansi_bg}, }; +/// Represents a single render instruction. #[derive(Clone)] enum RenderMethod { + /// Plain text rendering at current transform. Text(String), + /// Text rendered with a specific 24-bit color. TextColored(String, u32), + /// A filled rectangle of a given size and background color. Rectangle(u16, u16, u32), } +/// Stores renderable state and transformation data for a single UI widget. +/// +/// `RenderScope` tracks dimensions, styles, and draw commands such as text or +/// background rectangles. It accumulates instructions that are later executed in the `draw` method. #[derive(Clone)] pub struct RenderScope { transform: RawTransform, @@ -22,6 +40,7 @@ pub struct RenderScope { } impl RenderScope { + /// Creates a new, empty `RenderScope`. pub fn new() -> RenderScope { RenderScope { transform: RawTransform::new(), @@ -32,10 +51,12 @@ impl RenderScope { } } + /// Directly sets the raw transform (position and size) for this scope. pub fn set_transform_raw(&mut self, transform: RawTransform) { self.transform = transform; } + /// Applies a `Transform` to this scope, factoring in parent dimensions. pub fn set_transform(&mut self, transform: &Transform) { transform.use_dimensions(self.parent_width, self.parent_height, &mut self.transform); transform.use_position(self.parent_width, self.parent_height, &mut self.transform); @@ -43,6 +64,7 @@ impl RenderScope { self.transform.py = transform.py; } + /// Adds a text draw instruction. pub fn draw_text(&mut self, text: &str) { self.render_stack.push(RenderMethod::Text(text.to_string())); let (w, h) = utils::str_size(text); @@ -50,6 +72,7 @@ impl RenderScope { self.transform.height = self.transform.height.max(h); } + /// Adds a colored text draw instruction. pub fn draw_text_colored(&mut self, text: &str, color: u32) { self.render_stack .push(RenderMethod::TextColored(text.to_string(), color)); @@ -58,6 +81,7 @@ impl RenderScope { self.transform.height = self.transform.height.max(h); } + /// Adds a background rectangle draw instruction. pub fn draw_rect(&mut self, width: u16, height: u16, color: u32) { self.render_stack .push(RenderMethod::Rectangle(width, height, color)); @@ -65,17 +89,25 @@ impl RenderScope { self.transform.height = self.transform.height.max(height); } + /// Manually ensures a minimum area is allocated. pub fn use_area(&mut self, width: u16, height: u16) { self.transform.width = self.transform.width.max(width); self.transform.height = self.transform.height.max(height); } + /// Renders the current stack to the terminal. + /// + /// This will draw background styles (e.g. solid fill or outline) first, + /// followed by each draw instruction in the stack. pub fn draw(&self) { let width = self.transform.width; let height = self.transform.height; + match self.style.background { crate::style::Background::NoBackground => {} - crate::style::Background::Outline(_c) => {} + crate::style::Background::Outline(_c) => { + // TODO: Implement basic outline drawing if needed + } crate::style::Background::RoundedOutline(c) => { let width = width + self.transform.px * 2; let height = height + self.transform.py * 2; @@ -134,16 +166,19 @@ impl RenderScope { } } + /// Clears all render instructions and resets the internal state. pub fn clear(&mut self) { self.render_stack.clear(); self.transform = RawTransform::new(); self.style = Style::new(); } + /// Gets the currently used width and height. pub fn get_size(&self) -> (u16, u16) { (self.transform.width, self.transform.height) } + /// Returns current size, or defaults if size is zero. pub fn get_size_or(&self, width: u16, height: u16) -> (u16, u16) { ( if self.transform.width == 0 { @@ -159,6 +194,7 @@ impl RenderScope { ) } + /// Returns size, falling back to parent size if unset. pub fn get_size_or_parent(&self) -> (u16, u16) { ( if self.transform.width == 0 { @@ -174,27 +210,33 @@ impl RenderScope { ) } + /// Gets the dimensions of the parent container. pub fn get_parent_size(&self) -> (u16, u16) { (self.parent_width, self.parent_height) } + /// Sets the dimensions of the parent container. pub fn set_parent_size(&mut self, width: u16, height: u16) { self.parent_width = width; self.parent_height = height; } + /// Returns a mutable reference to the internal raw transform. pub fn get_transform_mut(&mut self) -> &mut RawTransform { &mut self.transform } + /// Returns a reference to the internal raw transform. pub fn get_transform(&self) -> &RawTransform { &self.transform } + /// Sets the style for the current render scope. pub fn set_style(&mut self, style: Style) { self.style = style; } + /// Gets a mutable reference to the style. pub fn get_style(&mut self) -> &mut Style { &mut self.style } From a30afdeb28a1ecded8e539f23e850b82bd114033 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:26:21 -0500 Subject: [PATCH 10/12] document state.rs #13 --- src/state.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/state.rs b/src/state.rs index e44eadd..b3b9ec0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,6 +4,14 @@ use std::{ sync::{Arc, Mutex, MutexGuard}, }; +/// Trait for tracking dependencies and reactivity. +pub trait DependencyHandler: std::fmt::Debug + Send + Sync { + /// Called when a dependent is registered. + fn add(&self); + /// Returns `true` if the state has changed since the last check. + fn check(&self) -> bool; +} + #[derive(Debug, Clone)] pub struct State { inner: Arc>>, @@ -90,8 +98,3 @@ impl DerefMut for Inner { &mut self.value } } - -pub trait DependencyHandler: std::fmt::Debug + Send + Sync { - fn add(&self); - fn check(&self) -> bool; -} From d1008a69c94b1fc0454795ad4fce9566c5ebc2b4 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:29:41 -0500 Subject: [PATCH 11/12] document style.rs #13 --- src/style.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/style.rs b/src/style.rs index ff52d01..6296754 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,5 +1,16 @@ +//! Layout and style definitions for OSUI widgets. +//! +//! This module defines the structures used to manage rendering geometry (`Transform`, `RawTransform`), +//! position and size enums (`Position`, `Dimension`), and styling (`Style`, `Background`, etc.). +//! +//! These types are used internally by components to calculate layout and control their appearance. + use crate::component; +/// Resolved layout information for a widget after layout calculations. +/// +/// This struct holds concrete values for position (`x`, `y`), dimensions +/// (`width`, `height`), and padding (`px`, `py`). #[derive(Debug, Clone)] pub struct RawTransform { pub x: u16, @@ -10,25 +21,38 @@ pub struct RawTransform { pub py: u16, } +/// Horizontal or vertical position relative to a parent container. #[derive(Debug, Clone)] pub enum Position { + /// Fixed position in cells from the origin. Const(u16), + /// Centered in the parent. Center, + /// Aligned to the end (right or bottom) of the parent. End, } +/// Sizing rule for width or height. #[derive(Debug, Clone)] pub enum Dimension { + /// Fills the available space from the parent. Full, + /// Automatically sized to fit content. Content, + /// Fixed size in cells. Const(u16), } +/// Background appearance for a widget. #[derive(Debug, Clone)] pub enum Background { + /// Transparent / no background. NoBackground, + /// Draws a basic outline using the given color. Outline(u32), + /// Draws a rounded outline using the given color. RoundedOutline(u32), + /// Fills the background with the specified color. Solid(u32), } @@ -49,6 +73,7 @@ component!(Style { }); impl Style { + /// Creates a style with no background or foreground. pub fn new() -> Self { Self { background: Background::NoBackground, @@ -58,6 +83,7 @@ impl Style { } impl Transform { + /// Creates a default transform with top-left alignment and content sizing. pub fn new() -> Transform { Transform { x: Position::Const(0), @@ -71,6 +97,7 @@ impl Transform { } } + /// Shortcut for centering both horizontally and vertically. pub fn center() -> Transform { Transform { x: Position::Center, @@ -84,39 +111,46 @@ impl Transform { } } + /// Aligns the widget to the bottom of its parent. pub fn bottom(mut self) -> Self { self.y = Position::End; self } + /// Aligns the widget to the right of its parent. pub fn right(mut self) -> Self { self.x = Position::End; self } + /// Adds margin (offset) from parent edge. pub fn margin(mut self, x: i32, y: i32) -> Self { self.mx = x; self.my = y; self } + /// Adds internal spacing (padding) around content. pub fn padding(mut self, x: u16, y: u16) -> Self { self.px = x; self.py = y; self } + /// Sets constant dimensions. pub fn dimensions(mut self, width: u16, height: u16) -> Self { self.width = Dimension::Const(width); self.height = Dimension::Const(height); self } + /// Resolves `Dimension` rules into absolute values for the given parent size. pub fn use_dimensions(&self, parent_width: u16, parent_height: u16, raw: &mut RawTransform) { self.width.use_dimension(parent_width, &mut raw.width); self.height.use_dimension(parent_height, &mut raw.height); } + /// Resolves `Position` rules into absolute positions for the given parent size. pub fn use_position(&self, parent_width: u16, parent_height: u16, raw: &mut RawTransform) { self.x .use_position(raw.width, parent_width, self.mx, &mut raw.x); @@ -126,6 +160,7 @@ impl Transform { } impl RawTransform { + /// Creates a new transform with all fields set to 0. pub fn new() -> RawTransform { RawTransform { x: 0, @@ -139,6 +174,7 @@ impl RawTransform { } impl Dimension { + /// Applies a dimension rule to determine a final size. pub fn use_dimension(&self, parent: u16, r: &mut u16) { match self { Self::Full => *r = parent, @@ -149,6 +185,7 @@ impl Dimension { } impl Position { + /// Applies a position rule to determine a final coordinate, based on size and parent. pub fn use_position(&self, size: u16, parent: u16, m: i32, r: &mut u16) { let base = match self { Self::Center => { From 095aa14b826e29a523f9ab3ac8c667296f51c6f9 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Fri, 1 Aug 2025 20:34:32 -0500 Subject: [PATCH 12/12] document widget.rs (some unfinished) #13 --- src/widget.rs | 94 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 23 deletions(-) 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);