haalka 0.7.1

ergonomic reactive Bevy UI library powered by FRP signals
Documentation
# haalka [হালকা]https://translate.google.com/?sl=bn&tl=en&text=%E0%A6%B9%E0%A6%BE%E0%A6%B2%E0%A6%95%E0%A6%BE&op=translate

[![Crates.io Version](https://img.shields.io/crates/v/haalka?style=for-the-badge)](https://crates.io/crates/haalka)
[![Docs.rs](https://img.shields.io/docsrs/haalka?style=for-the-badge)](https://docs.rs/haalka)
[![Following released Bevy versions](https://img.shields.io/badge/Bevy%20tracking-0.18-lightblue?style=for-the-badge)](https://bevyengine.org/learn/quick-start/plugin-development/#main-branch-tracking)

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

[haalka](https://github.com/databasedav/haalka) is an ergonomic reactive [Bevy](https://github.com/bevyengine/bevy) UI library powered by the [FRP](https://en.wikipedia.org/wiki/Functional_reactive_programming) signals of [jonmo](https://github.com/databasedav/jonmo) with API ported from web UI libraries [MoonZoon](https://github.com/MoonZoon/MoonZoon) and [Dominator](https://github.com/Pauan/rust-dominator).

## assorted features

- simple high-level alignment semantics ported from MoonZoon (see [align example]https://databasedav.github.io/haalka/examples/webgl2/align/ below)
- pointer event handling methods
    - hovered change methods (including web-style [`Enter`]https://docs.rs/haalka/latest/haalka/pointer_event_aware/struct.Enter.html and [`Leave`]https://docs.rs/haalka/latest/haalka/pointer_event_aware/struct.Leave.html events)
    - on click and on-click-outside methods
    - on pressed 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]https://github.com/ickshonpe/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

## [feature flags]https://docs.rs/haalka/latest/haalka/#feature-flags-1

## examples
<p align="center">
  <img src="https://raw.githubusercontent.com/databasedav/haalka/main/docs/static/counter.gif">
</p>

```rust no_run
use bevy::prelude::*;
use haalka::prelude::*;

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

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

fn ui_root() -> impl Element {
    let counter_holder = LazyEntity::new();
    El::<Node>::new()
        .with_node(|mut node| {
            node.height = Val::Percent(100.);
            node.width = Val::Percent(100.);
        })
        .insert(Pickable::default())
        .cursor(CursorIcon::default())
        .align_content(Align::center())
        .child(
            Row::<Node>::new()
                .with_node(|mut node| node.column_gap = Val::Px(15.0))
                .insert(Counter(0))
                .lazy_entity(counter_holder.clone())
                .item(counter_button(counter_holder.clone(), "-", -1))
                .item(
                    El::<Text>::new()
                        .text_font(TextFont::from_font_size(25.))
                        .text_signal(
                            signal::from_component_changed::<Counter>(counter_holder.clone())
                                .map_in(deref_copied)
                                .map_in_ref(ToString::to_string)
                                .map_in(Text)
                                .map_in(Some),
                        ),
                )
                .item(counter_button(counter_holder.clone(), "+", 1)),
        )
}

fn counter_button(counter_holder: LazyEntity, label: &'static str, step: i32) -> impl Element {
    let lazy_entity = LazyEntity::new();
    El::<Node>::new()
        .with_node(|mut node| {
            node.width = Val::Px(45.0);
            node.border_radius = BorderRadius::MAX;
        })
        .insert((Pickable::default(), Hoverable))
        .align_content(Align::center())
        .cursor(CursorIcon::System(SystemCursorIcon::Pointer))
        .lazy_entity(lazy_entity.clone())
        .background_color_signal(
            signal::from_entity(lazy_entity)
                .has_component::<Hovered>()
                .dedupe()
                .map_bool_in(|| Color::hsl(300., 0.75, 0.85), || Color::hsl(300., 0.75, 0.75))
                .map_in(BackgroundColor)
                .map_in(Some),
        )
        .on_click(move |_: In<_>, mut counters: Query<&mut Counter>| {
            if let Ok(mut counter) = counters.get_mut(*counter_holder) {
                **counter += 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](<https://github.com/gpuweb/gpuweb/wiki/Implementation-Status#implementation-status>)) and deployed to github pages.

- [**`counter`**]https://github.com/databasedav/haalka/blob/main/examples/counter.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/counter/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/counter/

    the example above, a simple counter

- [**`button`**]https://github.com/databasedav/haalka/blob/main/examples/button.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/button/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/button/

    a basic button, port of <https://github.com/bevyengine/bevy/blob/b176e1888c04b6c37035b85c115ad3a1266c22dd/examples/ui/button.rs>

- [**`align`**]https://github.com/databasedav/haalka/blob/main/examples/align.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/align/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/align/

    alignment API demo, port of <https://github.com/MoonZoon/MoonZoon/tree/19c6cf6b4d07cd27bee7758977ef1ea4d5b9933d/examples/align> and <https://github.com/MoonZoon/MoonZoon/tree/19c6cf6b4d07cd27bee7758977ef1ea4d5b9933d/examples/align_content>

- [**`scroll`**]https://github.com/databasedav/haalka/blob/main/examples/scroll.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/scroll/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/scroll/

    scrollability API demo, inspired by <https://github.com/mintlu8/bevy-rectray/blob/main/examples/scroll_discrete.rs>

- [**`scroll_grid`**]https://github.com/databasedav/haalka/blob/main/examples/scroll_grid.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/scroll_grid/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/scroll_grid/

    i can't believe it's not scrolling!

- [**`snake`**]https://github.com/databasedav/haalka/blob/main/examples/snake.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/snake/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/snake/

    the classic, with adjustable grid size and tick rate

- [**`dot_counter`**]https://github.com/databasedav/haalka/blob/main/examples/dot_counter.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/dot_counter/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/dot_counter/

    forward ecs changes to the ui, throttled button presses

- [**`dragging`**]https://github.com/databasedav/haalka/blob/main/examples/dragging.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/dragging/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/dragging/

    dragging with z-index stacking

- [**`key_values_sorted`**]https://github.com/databasedav/haalka/blob/main/examples/key_values_sorted.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/key_values_sorted/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/key_values_sorted/

    text inputs, scrolling/viewport control, and reactive lists; promises made promises kept! <https://discord.com/channels/691052431525675048/1192585689460658348/1193431789465776198> (yes I take requests)

- [**`calculator`**]https://github.com/databasedav/haalka/blob/main/examples/calculator.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/calculator/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/calculator/

    simple calculator, spurred by <https://discord.com/channels/691052431525675048/885021580353237032/1263661461364932639>

- [**`nested_lists`**]https://github.com/databasedav/haalka/blob/main/examples/nested_lists.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/nested_lists/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/nested_lists/

    nested dynamic lists, arbitrarily deeply nested retained reactivity, spurred by <https://discord.com/channels/691052431525675048/885021580353237032/1356769984474517617>

- [**`main_menu`**]https://github.com/databasedav/haalka/blob/main/examples/main_menu.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/main_menu/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/main_menu/

    sub menus, sliders, dropdowns, reusable composable widgets, gamepad navigation

- [**`inventory`**]https://github.com/databasedav/haalka/blob/main/examples/inventory.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/inventory/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/inventory/

    grid, icons, drag and drop, tooltips

- [**`healthbar`**]https://github.com/databasedav/haalka/blob/main/examples/healthbar.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/healthbar/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/healthbar/

    3D character anchor, customizable widgets

- [**`responsive_menu`**]https://github.com/databasedav/haalka/blob/main/examples/responsive_menu.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/responsive_menu/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/responsive_menu/

    nine-patch buttons, screen size reactivity

- [**`character_editor`**]https://github.com/databasedav/haalka/blob/main/examples/character_editor.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/character_editor/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/character_editor/

    scrollable buttons, mutable viewport, text input reactivity

- [**`futures_signals_jonmo_compat`**]https://github.com/databasedav/haalka/blob/main/examples/futures_signals_jonmo_compat.rs [webgl2]https://databasedav.github.io/haalka/examples/webgl2/futures_signals_jonmo_compat/ [webgpu]https://databasedav.github.io/haalka/examples/webgpu/futures_signals_jonmo_compat/

    demonstrates that both futures-signals and jonmo signals backends can be used together

Or run them locally with `cargo`.
```bash
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
cargo run --example futures_signals_jonmo_compat --features futures_signals_ui
```
Or with [`just`](https://github.com/casey/just), e.g. `just example snake -r`.

## Bevy compatibility
|bevy|haalka|
|-|-|
|`0.18`|`0.7`|
|`0.17`|`0.6`|
|`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
    ```bash
    git clone --single-branch --branch main --recurse-submodules https://github.com/databasedav/haalka.git
    ```
- install [just]https://github.com/casey/just?tab=readme-ov-file#installation
- install [nickel]https://github.com/tweag/nickel?tab=readme-ov-file#run for modifying CI configuration (`nickel` must be in your PATH)
- install [File Watcher]https://marketplace.visualstudio.com/items?itemName=appulate.filewatcher for automatically syncing nickels

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

- MIT License ([LICENSE-MIT]https://github.com/databasedav/haalka/blob/main/LICENSE-MIT or <http://opensource.org/licenses/MIT>)
- Apache License, Version 2.0 ([LICENSE-APACHE]https://github.com/databasedav/haalka/blob/main/LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)

at your option.

Assets used in examples may be licensed under different terms, see the [`examples` README](https://github.com/databasedav/haalka/blob/main/examples/README.md).

### 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.