Skip to content

allenli178/compose-rt

 
 

Repository files navigation

compose-rt

Rust Docs Status Latest Version

A positional memoization runtime similar to Jetpack Compose Runtime.

What this means is that Compose is, at its core, a general-purpose tool for managing a tree of nodes of any type. Well a “tree of nodes” describes just about anything, and as a result Compose can target just about anything. – jakewharton

use cases

examples

  • Below example show how to build a declarative GUI similar to Jetpack Compose UI
[dependencies]
compose-rt = "0.12"
downcast-rs = "1.2"
log = "0.4"
env_logger = "0.6"
fltk = { version = "^1.2", features = ["fltk-bundled"] }
#![allow(non_snake_case)]

use compose_rt::{compose, Composer, Recomposer};
use fltk::{
    app, button,
    group::{self, Flex},
    prelude::*,
    text,
    window::Window,
};
use std::{cell::RefCell, rc::Rc};

////////////////////////////////////////////////////////////////////////////
// User application
////////////////////////////////////////////////////////////////////////////
pub struct Movie {
    pub id: usize,
    pub name: String,
    pub img_url: String,
}
impl Movie {
    pub fn new(id: usize, name: impl Into<String>, img_url: impl Into<String>) -> Self {
        Movie {
            id,
            name: name.into(),
            img_url: img_url.into(),
        }
    }
}

#[compose]
pub fn MoviesScreen(movies: &Vec<Movie>) {
    Column(cx, |cx| {
        for movie in movies {
            cx.tag(movie.id, |cx| MovieOverview(cx, &movie));
        }
    });
}

#[compose]
pub fn MovieOverview(movie: &Movie) {
    Column(cx, |cx| {
        Text(cx, &movie.name);

        let count = cx.remember(|| Rc::new(RefCell::new(0usize)));
        let c = count.clone();
        Button(
            cx,
            &format!("{} get {} likes", movie.name, count.borrow()),
            move || *c.borrow_mut() += 1,
        );
        Text(cx, format!("Count {}", count.borrow()));
    });
}

fn main() {
    // Setup logging
    env_logger::Builder::from_default_env()
        .filter_level(log::LevelFilter::Trace)
        .init();

    let app = app::App::default();
    // define root compose
    let root_fn = |cx: &mut Composer, movies| Window(cx, |cx| MoviesScreen(cx, movies));

    let mut recomposer = Recomposer::new(20);

    let movies = vec![Movie::new(1, "A", "IMG_A"), Movie::new(2, "B", "IMG_B")];
    recomposer.compose(|cx| {
        root_fn(cx, &movies);
    });

    while app.wait() {
        recomposer.compose(|cx| {
            root_fn(cx, &movies);
        });
    }
}

////////////////////////////////////////////////////////////////////////////
// Components - Usage of compose-rt
////////////////////////////////////////////////////////////////////////////
#[compose(skip_inject_cx = true)]
pub fn Window<C>(cx: &mut Composer, content: C)
where
    C: Fn(&mut Composer),
{
    cx.group(
        |_| Window::default().with_size(400, 300),
        |_| false,
        content,
        |_, _| {},
        |win| {
            win.end();
            win.show();
        },
    )
}

#[compose(skip_inject_cx = true)]
pub fn Column<C>(cx: &mut Composer, content: C)
where
    C: Fn(&mut Composer),
{
    cx.group(
        |_| {
            let mut flex = Flex::new(0, 0, 400, 300, None);
            flex.set_type(group::FlexType::Column);
            flex
        },
        |_| false,
        content,
        |_, _| {},
        |flex| {
            flex.end();
        },
    );
}

#[compose(skip_inject_cx = true)]
pub fn Text(cx: &mut Composer, text: impl AsRef<str>) {
    let text = text.as_ref();
    cx.memo(
        |_| {
            let mut editor = text::TextEditor::default()
                .with_size(390, 290)
                .center_of_parent();

            let mut buf = text::TextBuffer::default();
            buf.set_text(text);
            editor.set_buffer(buf);
            editor
        },
        |n| n.buffer().unwrap().text().eq(text),
        |n| {
            n.buffer().as_mut().unwrap().set_text(text);
        },
        |_| {},
    );
}

#[compose(skip_inject_cx = true)]
pub fn Button<F>(cx: &mut Composer, text: &str, mut cb: F)
where
    F: 'static + FnMut(),
{
    cx.memo(
        |_| {
            let mut btn = button::Button::new(160, 210, 80, 40, None);
            btn.set_callback(move |_| cb());
            btn
        },
        |n| n.label().eq(text),
        |n| {
            n.set_label(text);
        },
        |_| {},
    );
}

About

A positional memoization runtime

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 100.0%