Skip to content

databasedav/haalka

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crates.io Version Docs.rs Following released Bevy versions

in bengali, haalka means "light" (e.g. not heavy) and can also be used to mean "easy"

haalka is an ergonomic reactive Bevy UI library powered by the incredible FRP signals of futures-signals and the convenient async ECS of bevy-async-ecs with API ported from web UI libraries MoonZoon and Dominator.

While haalka is primarily targeted at UI and provides high level UI abstractions as such, its core abstraction can be used to manage signals-powered reactivity for any entity, not just bevy_ui nodes.

assorted features

  • signals integration for all entities, components, and children
  • simple high-level alignment semantics ported from MoonZoon (see align example below)
  • pointer event handling methods
    • hovered change methods (including web-style Enter and Leave events)
    • on click and on-click-outside methods
    • on pressing methods, with throttle-ability
  • cursor-on-hover management
  • global event handling methods
  • mouse wheel scroll handling methods
  • signals-integrated text input, a thin layer on top of bevy_ui_text_input
  • viewport mutation handling methods
  • simple grid layout model ported from MoonZoon
  • macro rules for adding signal helper methods to custom element structs

considerations

  • Reactive updates done by haalka are eventually consistent, that is, once some ECS world state has been updated, any downstream reactions should not be expected to run in the same frame. This is due to the indirection involved with using an async signals library, which dispatches Bevy commands after polling by the async runtime. The resulting "lag" should not be noticeable in most popular cases, e.g. reacting to hover/click state or synchronizing UI (one can run the examples to evaluate this themselves), but in cases where frame perfect responsiveness is critical, one should simply use Bevy-native systems directly.

examples

use bevy::prelude::*;
use haalka::prelude::*;

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

#[derive(Component)]
struct Counter(Mutable<i32>);

fn ui_root() -> impl Element {
    let counter = Mutable::new(0);
    El::<Node>::new()
        .with_node(|mut node| {
            node.height = Val::Percent(100.);
            node.width = Val::Percent(100.);
        })
        .cursor(CursorIcon::default())
        .align_content(Align::center())
        .child(
            Row::<Node>::new()
                .with_node(|mut node| node.column_gap = Val::Px(15.0))
                .item(counter_button(counter.clone(), "-", -1))
                .item(
                    El::<Text>::new()
                        .text_font(TextFont::from_font_size(25.))
                        .text_signal(counter.signal_ref(ToString::to_string).map(Text)),
                )
                .item(counter_button(counter.clone(), "+", 1))
                .update_raw_el(move |raw_el| raw_el.insert(Counter(counter))),
        )
}

fn counter_button(counter: Mutable<i32>, label: &str, step: i32) -> impl Element {
    let hovered = Mutable::new(false);
    El::<Node>::new()
        .with_node(|mut node| node.width = Val::Px(45.0))
        .align_content(Align::center())
        .border_radius(BorderRadius::MAX)
        .cursor(CursorIcon::System(SystemCursorIcon::Pointer))
        .background_color_signal(
            hovered
                .signal()
                .map_bool(|| Color::hsl(300., 0.75, 0.85), || Color::hsl(300., 0.75, 0.75))
                .map(BackgroundColor),
        )
        .hovered_sync(hovered)
        .on_click(move || *counter.lock_mut() += step)
        .child(
            El::<Text>::new()
                .text_font(TextFont::from_font_size(25.))
                .text(Text::new(label)),
        )
}

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.

Or run them locally with cargo.

cargo run --example counter
cargo run --example button
cargo run --example align
cargo run --example scroll
cargo run --example scroll_grid
cargo run --example snake
cargo run --example dot_counter
cargo run --example key_values_sorted
cargo run --example calculator
cargo run --example nested_lists

# ui challenges from https://github.com/bevyengine/bevy/discussions/11100
cargo run --example main_menu
cargo run --example inventory
cargo run --example healthbar
cargo run --example responsive_menu
cargo run --example character_editor

Or with just, e.g. just example snake -r.

Bevy compatibility

bevy haalka
0.16 0.5
0.15 0.4
0.14 0.2
0.13 0.1

development

  • avoid the gh-pages branch and include submodules when fetching the repo
    git clone --single-branch --branch main --recurse-submodules https://github.com/databasedav/haalka.git
  • install just
  • install nickel for modifying CI configuration (nickel must be in your PATH)
  • install File Watcher for automatically syncing nickels

license

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

at your option.

Assets used in examples may be licensed under different terms, see the examples README.

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.

About

ergonomic reactive Bevy UI library powered by FRP signals

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published

Languages