dip 0.2.1

Write cross-platform application with React-like declarative UI framework and scalable ECS architecture all in Rust.
Documentation
use dip::{bevy::log::LogPlugin, prelude::*};

fn main() {
    App::new()
        // Step 7. Put it all together
        .add_plugin(DesktopPlugin::<UiState, UiAction, NoAsyncAction>::new(Root))
        // ui_state attribute macro automatically generates UiStatePlugin
        .add_plugin(UiStatePlugin)
        // automatically generated by ui_action macro
        .add_plugin(UiActionPlugin)
        .add_plugin(LogPlugin)
        .add_system(handle_increment)
        .add_system(handle_decrement)
        .add_system(handle_reset)
        .run();
}

#[allow(non_snake_case)]
fn Root(cx: Scope) -> Element {
    // Step 5. Select state
    let count = use_read(&cx, COUNT);

    let disabled = count.value == 0;

    let window = use_window::<UiAction, NoAsyncAction>(&cx);

    cx.render(rsx! {
        h1 { "Counter Example" }
        p { "count: {count.value}" }
        button {
            // Step 6. Dispatch the action !
            onclick: move |_| window.send(UiAction::decrement()),
            disabled: "{disabled}",
            "-",
        }
        button {
            onclick: move |_| window.send(UiAction::reset()),
            disabled: "{disabled}",
            "Reset"
        }
        button {
            onclick: move |_| window.send(UiAction::increment()),
            "+",
        }
    })
}

// Step 1. Define UiState
// Name each root state with key and provide a type for it. Each state needs to be a named custom type (either struct or enum). If you want to provide primitive type (i.e. u32) or standard type like String, then try to wrap it in a custom struct like `Count` in this example.
// This limitation exists because underneath the hood, `ui_state` macro automatically generates code that insert each
// root state as resource in bevy world. If you provide the same type between resources,
// then systems cannot determine which resource you're querying to.
#[ui_state]
struct UiState {
    count: Count,
}

#[derive(Clone, Debug, Default)]
pub struct Count {
    value: u32,
}

// Step 2. Define actions
#[derive(Clone, Debug)]
pub struct Increment;

#[derive(Clone, Debug)]
pub struct Decrement;

#[derive(Clone, Debug)]
pub struct Reset;

// Step 3. Implement action creators
// `struct #action_creator_impl_name;` is automatically generated by ui_action macro so you don't
// need to define it. Each method should return one of the actions you defined in step 2.
#[ui_action]
impl ActionCreator {
    fn increment() -> Increment {
        Increment
    }

    fn decrement() -> Decrement {
        Decrement
    }

    fn reset() -> Reset {
        Reset
    }
}

// Step 4. Implement systems to handle each action you defined in step 2
fn handle_increment(mut events: EventReader<Increment>, mut count: ResMut<Count>) {
    for _ in events.iter() {
        info!("🧠 Increment");
        // 5. Mutate root state resource.
        // Once all system gets run concurrently, dip rerenders root component with the new state.
        count.value += 1;
    }
}

fn handle_decrement(mut events: EventReader<Decrement>, mut count: ResMut<Count>) {
    for _ in events.iter() {
        info!("🧠 Decrement");
        count.value -= 1;
    }
}

fn handle_reset(mut events: EventReader<Reset>, mut count: ResMut<Count>) {
    for _ in events.iter() {
        info!("🧠 Reset");
        count.value = 0;
    }
}