concoct 0.1.0

Cross-platform UI framework

![Concoct](https://github.com/matthunz/viewbuilder/blob/main/logo.png?raw=true)

An experimental cross-platform UI framework in rust.

[![crate](https://img.shields.io/crates/v/concoct.svg)](https://crates.io/crates/concoct)
[![documentation](https://docs.rs/concoct/badge.svg)](https://docs.rs/concoct)
[![Rust](https://github.com/matthunz/concoct/actions/workflows/rust.yml/badge.svg)](https://github.com/matthunz/concoct/actions/workflows/rust.yml)


```rust
fn button(label: &'static str, on_press: impl FnMut() + 'static) {
    container(
        Modifier::default()
            .background_color(RGB::from((110, 133, 247)))
            .clickable(on_press)
            .padding(Rect::from_points(100., 100., 50., 50.)),
        move || text(Modifier::default(), label),
    );
}

#[compose]
fn counter() {
    let count = state(|| 0);

    container(
        Modifier::default()
            .align_items(AlignItems::Center)
            .justify_content(JustifyContent::Center)
            .flex_direction(FlexDirection::Column)
            .flex_grow(1.)
            .gap(Size::from_points(0., 100.)),
        move || {
            text(
                Modifier::default().font_family("serif").font_size(200.),
                count.get().borrow().to_string(),
            );

            container(
                Modifier::default()
                    .flex_direction(FlexDirection::Row)
                    .gap(Size::from_points(100., 0.)),
                move || {
                    button("More", move || *count.get_mut().borrow_mut() += 1);
                    button("Less", move || *count.get_mut().borrow_mut() -= 1);
                },
            );
        },
    )
}
```

![calculator](https://github.com/matthunz/viewbuilder/blob/main/calculator.png?raw=true)
![counter](https://github.com/matthunz/viewbuilder/blob/main/counter.png?raw=true)

# Design
This framework consists of two layers:
* Views:
    - Build the widget tree
    - Rebuild themselves and their children when their state owned is updated
    - Construct the widget layout
* Widgets:
    - Paint to the `skia` canvas
    - Manage event handling

Both views and widgets manage their state using positional memoization. This is achieved with `#[track_caller]` and using the source code location of each node in the tree. For example, when views are rebuilt they are guranteed to be ran in the same order. However, adding or removing elements can disrupt their order, which is where `Id` comes in. The `Id` struct is a combination of the source code [`Location`](https://doc.rust-lang.org/nightly/core/panic/struct.Location.html#method.caller) and an optional user-defined integer key (this key is needed in lists where UI elements are created in a loop or etc).

`Modifier`s are used to connect between the view and widget layer. The design is still in progress but the hope is they can support the following, like in Jetpack Compose:
![compose example](https://miro.medium.com/max/1400/1*vasl4dQ4ODzkuNWQjROlDA.webp)


# Inspiration
* Jetpack Compose
* [xilem](https://github.com/linebender/xilem)
* [compose-rt](https://github.com/cksac/compose-rt)