A positional memoization runtime similar to Jetpack Compose Runtime.
Below example show how to build a declarative GUI using compose-rt
toml
[dependencies]
compose-rt = "0.4"
downcast-rs = "1.2"
log = "0.4"
env_logger = "0.6"
fake = "2.4"
```rust
use composert::{compose, ComposeNode, Composer}; use downcastrs::impl_downcast; use fake::{Fake, Faker}; use std::{ any::TypeId, cell::{RefCell, RefMut}, fmt::Debug, rc::Rc, };
////////////////////////////////////////////////////////////////////////////
// User application
////////////////////////////////////////////////////////////////////////////
pub struct Movie {
id: usize,
name: String,
imgurl: String,
}
impl Movie {
pub fn new(id: usize, name: impl Into
pub fn MoviesScreen(movies: &Vec
pub fn MovieOverview(movie: &Movie) { Column(cx, |cx| { Text(cx, &movie.name); Image(cx, &movie.img_url); RandomRenderObject(cx, &movie.name);
let count = cx.state(Rc::new(RefCell::new(0usize)));
Text(cx, format!("compose count {}", count.borrow()));
*count.borrow_mut() += 1;
});
}
fn main() { // Setup logging envlogger::Builder::fromdefaultenv() .filterlevel(log::LevelFilter::Trace) .init();
// define root compose
let root_fn = |cx: &mut Composer, movies| MoviesScreen(cx, movies);
let mut cx = Composer::new(10);
// first run
let movies = vec![Movie::new(1, "A", "IMG_A"), Movie::new(2, "B", "IMG_B")];
root_fn(&mut cx, &movies);
// end compose, Recomposer allow you to access root
let recomposer = cx.finalize();
if let Some(root) = recomposer.root::<Rc<RefCell<RenderFlex>>>() {
// call paint of render tree
let mut context = PaintContext::new();
root.borrow().paint(&mut context);
}
cx = recomposer.compose();
// rerun with new input
let movies = vec![
Movie::new(1, "AA", "IMG_AA"),
Movie::new(3, "C", "IMG_C"),
Movie::new(2, "B", "IMG_B"),
];
root_fn(&mut cx, &movies);
let recomposer = cx.finalize();
// end compose, Recomposer allow you to access root
if let Some(root) = recomposer.root::<Rc<RefCell<RenderFlex>>>() {
// call paint of render tree
let mut context = PaintContext::new();
root.borrow().paint(&mut context);
}
}
//////////////////////////////////////////////////////////////////////////// // Components - Usage of compose-rt ////////////////////////////////////////////////////////////////////////////
pub fn Column
pub fn Text(cx: &mut Composer, text: impl AsRef
pub fn Image(cx: &mut Composer, url: impl AsRef
pub fn RandomRenderObject(cx: &mut Composer, text: impl AsRef
if ty_id == TypeId::of::<RenderLabel>() {
let mut label = RefMut::map(n, |x| x.downcast_mut::<RenderLabel>().unwrap());
label.0 = t.to_string();
} else if ty_id == TypeId::of::<RenderImage>() {
let mut img = RefMut::map(n, |x| x.downcast_mut::<RenderImage>().unwrap());
let url = format!("http://image.com/{}.png", t);
img.0 = url;
};
},
|_| {},
);
}
//////////////////////////////////////////////////////////////////////////// // Rendering backend - Not scope of compose-rt //////////////////////////////////////////////////////////////////////////// pub struct PaintContext { depth: usize, } impl PaintContext { pub fn new() -> Self { Self { depth: 0 } } }
pub trait RenderObject: Debug + ComposeNode { fn paint(&self, context: &mut PaintContext); } impl_downcast!(RenderObject);
pub struct RenderFlex {
children: Vec
impl RenderFlex { pub fn new() -> Self { RenderFlex { children: Vec::new(), } } }
impl RenderObject for RenderFlex {
fn paint(&self, context: &mut PaintContext) {
println!(
"{}
pub struct RenderLabel(String); impl RenderObject for RenderLabel { fn paint(&self, context: &mut PaintContext) { println!("{}", "\t".repeat(context.depth), self.0); } }
pub struct RenderImage(String);
impl RenderObject for RenderImage {
fn paint(&self, context: &mut PaintContext) {
println!("{}", "\t".repeat(context.depth), self.0);
}
}
```