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) {}