From e0cf14979060498046c84d2fcd46c768d9b1a029 Mon Sep 17 00:00:00 2001 From: Kleo Dev Date: Tue, 12 Aug 2025 21:12:54 -0400 Subject: [PATCH 1/3] Updated examples with a few small changes to resolve warnings --- src/demos/mod.rs => examples/tour.rs | 10 ++++++++++ src/extensions/mod.rs | 2 +- src/main.rs | 12 ------------ src/widget.rs | 6 +++--- 4 files changed, 14 insertions(+), 16 deletions(-) rename src/demos/mod.rs => examples/tour.rs (91%) delete mode 100644 src/main.rs diff --git a/src/demos/mod.rs b/examples/tour.rs similarity index 91% rename from src/demos/mod.rs rename to examples/tour.rs index 0cee7f5..ffeed5b 100644 --- a/src/demos/mod.rs +++ b/examples/tour.rs @@ -2,6 +2,16 @@ use std::sync::Arc; use osui::prelude::*; +fn main() -> std::io::Result<()> { + let screen = Screen::new(); + screen.extension(InputExtension); + screen.extension(RelativeFocusExtension::new()); + + app(screen.clone()).draw(&screen); + + screen.run() +} + pub fn app(screen: Arc) -> Rsx { let count = use_state(0); diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 7624606..2e375cf 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -96,7 +96,7 @@ impl Context { } } - pub fn get_widgets(&self) -> MutexGuard>> { + pub fn get_widgets(&'_ self) -> MutexGuard<'_, Vec>> { self.screen.widgets.lock().unwrap() } diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index c915f8e..0000000 --- a/src/main.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod demos; -use osui::prelude::*; - -fn main() -> std::io::Result<()> { - let screen = Screen::new(); - screen.extension(InputExtension); - screen.extension(RelativeFocusExtension::new()); - - demos::app(screen.clone()).draw(&screen); - - screen.run() -} diff --git a/src/widget.rs b/src/widget.rs index ed8e802..58b27d1 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -166,7 +166,7 @@ impl Widget { } } - pub fn get_elem(&self) -> MutexGuard { + pub fn get_elem(&'_ self) -> MutexGuard<'_, BoxedElement> { match self { Widget::Static(w) => w.get_elem(), Widget::Dynamic(w) => w.get_elem(), @@ -284,7 +284,7 @@ impl Widget { impl StaticWidget { fn after_render(&self) {} - fn get_elem(&self) -> MutexGuard { + fn get_elem(&'_ self) -> MutexGuard<'_, BoxedElement> { self.element.lock().unwrap() } } @@ -325,7 +325,7 @@ impl StaticWidget { impl DynWidget { fn after_render(&self) {} - fn get_elem(&self) -> MutexGuard { + fn get_elem(&'_ self) -> MutexGuard<'_, BoxedElement> { self.element.lock().unwrap() } } From fde8ad17d6233cf0b6066eaf94f04cf8804c028e Mon Sep 17 00:00:00 2001 From: Kleo Dev Date: Tue, 12 Aug 2025 23:18:35 -0400 Subject: [PATCH 2/3] added Iterator --- examples/iter.rs | 46 ++++++++++++++++++++++++++++++ src/elements/flex.rs | 6 ++++ src/frontend.rs | 66 +++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 34 +++++++++++++++++++++++ src/widget.rs | 4 +++ 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 examples/iter.rs diff --git a/examples/iter.rs b/examples/iter.rs new file mode 100644 index 0000000..22bb6f7 --- /dev/null +++ b/examples/iter.rs @@ -0,0 +1,46 @@ +use osui::prelude::*; + +fn main() -> std::io::Result<()> { + let screen = Screen::new(); + + app().draw(&screen); + + screen.run() +} + +pub fn app() -> Rsx { + let items: State> = use_state(vec![1, 2]); + + std::thread::spawn({ + let items = items.clone(); + move || { + std::thread::sleep(std::time::Duration::from_millis(1000)); + items.get().push(3); + std::thread::sleep(std::time::Duration::from_millis(1000)); + items.get().remove(0); + } + }); + + Rsx(vec![RsxElement::Element( + StaticWidget::new(Box::new(FlexRow::new())), + Rsx(vec![RsxElement::Iter( + Box::new({ + let items = items.clone(); + move || { + Rsx(items + .get_dl() + .iter() + .enumerate() + .map(|(i, _)| { + RsxElement::Element( + StaticWidget::new(Box::new(format!("Item {i}"))), + Rsx(vec![]), + ) + }) + .collect()) + } + }), + Box::new(items), + )]), + )]) +} diff --git a/src/elements/flex.rs b/src/elements/flex.rs index abdc17a..26ff7d4 100644 --- a/src/elements/flex.rs +++ b/src/elements/flex.rs @@ -78,6 +78,12 @@ impl Element for FlexRow { element.inject(|w| w.component(NoRenderRoot)); } + fn undraw_child(&mut self, element: &Arc) { + if let Some(i) = self.children.iter().position(|v| Arc::ptr_eq(v, element)) { + self.children.remove(i); + } + } + fn is_ghost(&mut self) -> bool { true } diff --git a/src/frontend.rs b/src/frontend.rs index 2d47ac2..9ff6b04 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -25,6 +25,12 @@ pub enum RsxElement { Vec>, Rsx, ), + + /// A dynamic iteration of widgets, keeping track of their state. + Iter( + Box Rsx + Send + Sync>, + Box, + ), } /// A container representing a group of RSX elements. @@ -42,7 +48,13 @@ impl Rsx { /// 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>) { + pub fn draw_parent( + self, + screen: &Arc, + parent: Option>, + ) -> Option> { + let mut first_widget = None; + for rsx_elem in self.0 { match rsx_elem { RsxElement::DynElement(f, dep, child) => { @@ -57,6 +69,9 @@ impl Rsx { } child.draw_parent(screen, Some(w.clone())); + if first_widget.is_none() { + first_widget = Some(w) + } } RsxElement::Element(ws, child) => { @@ -68,9 +83,49 @@ impl Rsx { } child.draw_parent(screen, Some(w.clone())); + if first_widget.is_none() { + first_widget = Some(w) + } + } + + RsxElement::Iter(mut rsx, dep) => { + #[allow(unused)] + let mut rsx_len = 0; + let mut fw = { + let r = rsx(); + rsx_len = r.0.len(); + r.draw_parent(screen, parent.clone()) + }; + let parent = parent.clone(); + let s = screen.clone(); + + screen.add_dependency(vec![dep], move || { + if let Some(fww) = &fw { + { + let mut widgets = s.widgets.lock().unwrap(); + if let Some(i) = widgets.iter().position(|v| Arc::ptr_eq(v, fww)) { + for v in (i..(i + rsx_len)).rev() { + if let Some(w) = widgets.get(v) { + if let Some(p) = &parent { + p.get_elem().undraw_child(w); + } + widgets.remove(v); + } + } + } + } + fw = { + let r = rsx(); + rsx_len = r.0.len(); + r.draw_parent(&s, parent.clone()) + }; + } + }); } } } + + first_widget } /// Adds a dynamically constructed element to the RSX tree. @@ -96,6 +151,15 @@ impl Rsx { self.0.push(RsxElement::Element(element, children)); } + /// Adds a statically defined element to the RSX tree with its children. + pub fn iter Rsx + Send + Sync + 'static>( + &mut self, + rsx: F, + dependency: Box, + ) { + self.0.push(RsxElement::Iter(Box::new(rsx), dependency)); + } + /// Appends the elements from another `Rsx` tree into this one. pub fn expand(&mut self, other: &mut Rsx) { self.0.append(&mut other.0); diff --git a/src/lib.rs b/src/lib.rs index 3b7597c..c432b16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ use crate::{ extensions::Extension, prelude::{Context, ElementRenderer}, render_scope::RenderScope, + state::DependencyHandler, widget::{BoxedElement, DynWidget, Element, StaticWidget, Widget, WidgetLoad}, }; @@ -78,6 +79,13 @@ pub struct Screen { extensions: Mutex>>>>, /// If it's still running running: Mutex, + /// If it's still running + dependencies: Mutex< + Vec<( + Vec>, + Box, + )>, + >, } event!(RenderWrapperEvent(*mut RenderScope)); @@ -104,6 +112,7 @@ impl Screen { widgets: Mutex::new(Vec::new()), extensions: Mutex::new(Vec::new()), running: Mutex::new(true), + dependencies: Mutex::new(Vec::new()), }) } @@ -194,6 +203,14 @@ impl Screen { scope.render_widget(&mut renderer, ctx, widget); } + for d in self.dependencies.lock().unwrap().iter_mut() { + for dh in &d.0 { + if dh.check() { + (d.1)(); + break; + } + } + } Ok(()) } @@ -208,6 +225,23 @@ impl Screen { } } +impl Screen { + pub fn add_dependency( + self: &Arc, + dependencies: Vec>, + handler: F, + ) { + for d in &dependencies { + d.add(); + } + + self.dependencies + .lock() + .unwrap() + .push((dependencies, Box::new(handler))); + } +} + struct ScreenRenderer; impl ElementRenderer for ScreenRenderer {} diff --git a/src/widget.rs b/src/widget.rs index 58b27d1..1a28e6e 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -51,6 +51,10 @@ pub trait Element: Send + Sync { #[allow(unused)] fn draw_child(&mut self, element: &Arc) {} + /// Called to undraw child widgets, if any. + #[allow(unused)] + fn undraw_child(&mut self, element: &Arc) {} + #[allow(unused)] fn event(&mut self, event: &dyn Event) {} From 9a03f241d84c4d2cc83a4f87253392731ba45a7c Mon Sep 17 00:00:00 2001 From: Kleo Dev Date: Tue, 12 Aug 2025 23:40:56 -0400 Subject: [PATCH 3/3] Added rsx iteration --- examples/iter.rs | 30 ++++++++---------------------- src/frontend.rs | 5 +++++ src/macros.rs | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/examples/iter.rs b/examples/iter.rs index 22bb6f7..8da4782 100644 --- a/examples/iter.rs +++ b/examples/iter.rs @@ -21,26 +21,12 @@ pub fn app() -> Rsx { } }); - Rsx(vec![RsxElement::Element( - StaticWidget::new(Box::new(FlexRow::new())), - Rsx(vec![RsxElement::Iter( - Box::new({ - let items = items.clone(); - move || { - Rsx(items - .get_dl() - .iter() - .enumerate() - .map(|(i, _)| { - RsxElement::Element( - StaticWidget::new(Box::new(format!("Item {i}"))), - Rsx(vec![]), - ) - }) - .collect()) - } - }), - Box::new(items), - )]), - )]) + rsx! { + FlexRow { + for item in items { + static "{item}" + static "Idk {item}" + } + } + } } diff --git a/src/frontend.rs b/src/frontend.rs index 9ff6b04..d53f78c 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -164,4 +164,9 @@ impl Rsx { pub fn expand(&mut self, other: &mut Rsx) { self.0.append(&mut other.0); } + + /// Appends the elements from another `Rsx` tree into this one. nr stands for no reference + pub fn expand_nr(&mut self, mut other: Rsx) { + self.0.append(&mut other.0); + } } diff --git a/src/macros.rs b/src/macros.rs index 4b4ed3c..384a3ab 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -240,6 +240,20 @@ macro_rules! rsx { /// Would produce a nested `Rsx` tree of static and dynamic widgets. #[macro_export] macro_rules! rsx_inner { + // for loop (iter) + ($r:expr, for $n:ident in $state:ident { $($inner:tt)* } $($rest:tt)*) => { + $r.iter({ + let $state = $state.clone(); + move || { + let mut r = $crate::frontend::Rsx(Vec::new()); + for $n in $state.get().iter() { + r.expand_nr($crate::rsx!{ $($inner)* }); + } + r + }}, Box::new($state)); + $crate::rsx_inner! { $r, $($rest)* }; + }; + // static ($r:expr, $(@$comp:expr;)* static $s:literal $($rest:tt)*) => { let w = $crate::widget::StaticWidget::new(Box::new(format!($s)));