Crate jonmo

Crate jonmo 

Source
Expand description

§jonmo জন্ম

Crates.io Version Docs.rs Following released Bevy versions

in bengali, jonmo means "birth"

jonmo provides an ergonomic, functional, and declarative API for specifying Bevy system dependency graphs, where “output” handles to nodes of the graph are canonically referred to as “signals”. Building upon these signals, jonmo offers a high level entity builder which enables one to declare reactive entities, components, and children using a familiar fluent syntax with semantics and API ported from the incredible FRP signals of futures-signals and its web UI dependents MoonZoon and Dominator.

The runtime of jonmo is quite simple; every frame, the outputs of systems are forwarded to their dependants, recursively. The complexity and power of jonmo really emerges from its monadic signal combinators, defined within the SignalExt, SignalVecExt, and SignalMapExt traits (ported from futures-signals’ traits of the same name), which internally manage special Bevy systems that allow for the declarative composition of complex data flows with minimalistic, high-level, signals-oriented methods.

§assorted features:

  • fine-grained reactivity for all entities, components, and children
  • diff-less constant-time reactive updates for collections (available through MutableVec and MutableBTreeMap)
  • automated system lifecycle management when using the builder API, simple component on-remove hook when not
  • polling API for when one needs an escape hatch from the regular push-based output semantics (polling is used sparsely internally for some combinators)
  • either wrappers (a la https://github.com/rayon-rs/either) and type-erased signals (via boxing) for cheap and flexible management of distinct signal types from different branches of logic
  • no_std compatible

§feature flags

§examples

//! Simple counter example, ported from a similar example in haalka.

use bevy::prelude::*;
use jonmo::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, JonmoPlugin))
        .add_systems(
            Startup,
            (
                |world: &mut World| {
                    ui_root().spawn(world);
                },
                camera,
            ),
        )
        .run();
}

#[derive(Component, Clone, Deref, DerefMut)]
struct Counter(i32);

fn ui_root() -> JonmoBuilder {
    let counter_holder = LazyEntity::new();

    JonmoBuilder::from(Node {
        width: Val::Percent(100.0),
        height: Val::Percent(100.0),
        justify_content: JustifyContent::Center,
        align_items: AlignItems::Center,
        ..default()
    })
    .child(
        JonmoBuilder::from(Node {
            flex_direction: FlexDirection::Row,
            column_gap: Val::Px(15.0),
            align_items: AlignItems::Center,
            padding: UiRect::all(Val::Px(25.)),
            ..default()
        })
        .insert(Counter(0))
        .entity_sync(counter_holder.clone())
        .child(counter_button(counter_holder.clone(), PINK, "-", -1))
        .child(
            JonmoBuilder::from((Node::default(), TextFont::from_font_size(25.)))
                .component_signal(
                    SignalBuilder::from_component_lazy(counter_holder.clone())
                        .map_in(|counter: Counter| *counter)
                        .dedupe()
                        .map_in_ref(ToString::to_string)
                        .map_in(Text)
                        .map_in(Some),
                ),
        )
        .child(counter_button(counter_holder, BLUE, "+", 1)),
    )
}

fn counter_button(counter_holder: LazyEntity, color: Color, label: &'static str, step: i32) -> JonmoBuilder {
    JonmoBuilder::from((
        Node {
            width: Val::Px(45.0),
            justify_content: JustifyContent::Center,
            align_items: AlignItems::Center,
            ..default()
        },
        BorderRadius::MAX,
        BackgroundColor(color),
    ))
    .observe(move |on: On<Pointer<Click>>, mut counters: Query<&mut Counter>| {
        if matches!(on.button, PointerButton::Primary) {
            if let Ok(mut counter) = counters.get_mut(counter_holder.get()) {
                **counter += step;
            }
        }
    })
    .child(JonmoBuilder::from((Text::from(label), TextFont::from_font_size(25.))))
}

fn camera(mut commands: Commands) {
    commands.spawn(Camera2d);
}

§on the web

All examples are compiled to wasm for both webgl2 and webgpu (check compatibility) and deployed to github pages.

  • basic webgl2 webgpu

    a simple increasing timer without using the entity builder, showcasing the least invasive way to start using jonmo signals in existing Bevy apps

  • basic_builder_inject webgl2 webgpu

    a simple increasing timer injecting the entity builder into an existing entity, showcasing a less invasive way to start using jonmo signals in existing Bevy apps.

  • basic_builder webgl2 webgpu

    a simple increasing timer using the entity builder, showcasing the recommended, idiomatic way to use jonmo signals

  • counter webgl2 webgpu

    the example above, a simple counter

  • lifetimes webgl2 webgpu

    a reactive enumerated list of colors, each with an independent lifetime timer

  • letters webgl2 webgpu

    key press counter with swappable save states, showcasing map reactivity

  • filters webgl2 webgpu

    diverse filtering options for a list of items, showcasing vector reactivity

§Bevy compatibility

bevyjonmo
0.170.4
0.160.3
0.150.1

§license

All code in this repository is dual-licensed under either:

at your option.

§your contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

§feature flags

  • builder (enabled by default) — Enables access to jonmo’s high-level entity builder, JonmoBuilder.
  • tracing (enabled by default) — Enables access to signal ext .debug methods, which conveniently logs signal outputs at any step.
  • time (enabled by default) — Enables access to SignalExt::throttle, which delays subsequent outputs by some Duration.
  • debugDebugUiPlugin which enables toggling bevy_dev_tools::ui_debug_overlay::DebugUiPlugin with the F1 key; requires a camera to be marked with the IsDefaultCamera component.
  • std (enabled by default) — Uses the Rust standard library.
  • critical-section — Rely on critical-section for synchronization primitives.
  • web — Enables access to browser APIs in a web context.

Modules§

builder
Reactive entity builder ported from Dominator’s DomBuilder.
graph
Signal graph management and runtime.
prelude
use jonmo::prelude::*; imports everything one needs to use start using jonmo.
signal
Signal builders and combinators for constructing reactive System dependency graphs, see SignalExt.
signal_map
Data structures and combinators for constructing reactive System dependency graphs on top of BTreeMap mutations, see MutableBTreeMap and SignalMapExt.
signal_vec
Data structures and combinators for constructing reactive System dependency graphs on top of Vec mutations, see MutableVec and SignalVecExt.
utils

Structs§

JonmoPlugin
Includes the systems required for jonmo to function.
SignalProcessing
SystemSet that can be used to schedule systems around signal processing.