diff --git a/examples/iter.rs b/examples/iter.rs new file mode 100644 index 0000000..8da4782 --- /dev/null +++ b/examples/iter.rs @@ -0,0 +1,32 @@ +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! { + FlexRow { + for item in items { + static "{item}" + static "Idk {item}" + } + } + } +} 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/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/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/frontend.rs b/src/frontend.rs index 2d47ac2..d53f78c 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,8 +151,22 @@ 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); } + + /// 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/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/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))); 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..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) {} @@ -166,7 +170,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 +288,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 +329,7 @@ impl StaticWidget { impl DynWidget { fn after_render(&self) {} - fn get_elem(&self) -> MutexGuard { + fn get_elem(&'_ self) -> MutexGuard<'_, BoxedElement> { self.element.lock().unwrap() } }