haalka
repository·main·Indexed 41 days ago
https://github.com/databasedav/haalkaAn ergonomic, reactive UI library for the Bevy game engine powered by Functional Reactive Programming (FRP) via jonmo signals. It provides a web-like developer experience with high-level alignment semantics, pointer event handling, and signal-integrated text input, drawing inspiration from MoonZoon and Dominator. Version 0.7.1 is compatible with Bevy 0.18.
What's inside haalka
- haalka is an ergonomic reactive UI library for the Bevy game engine. It is powered by jonmo signals (Functional Reactive Programming) and features an API inspired by web UI libraries like MoonZoon and Dominator. It provides high-level alignment semantics, pointer event handling, and signal-integrated text input.
License information
mainAll code in the
haalkarepository is dual-licensed under either the MIT License or the Apache License, Version 2.0, at the user's option.Note: Assets used in examples may be subject to different licensing terms; refer to the
examples/README.mdfor details.Core features of haalka
mainhaalka provides several high-level UI capabilities:
- Alignment & Layout: Simple high-level alignment semantics and a grid layout model (ported from MoonZoon).
- Pointer Events:
- Hover detection (including
EnterandLeaveevents). - Click handling (
on_click) andon-click-outside. - Pressed methods with throttling support.
- Hover detection (including
- Cursor Management:
cursor-on-hovermanagement. - Input: Signals-integrated text input (built on
bevy_ui_text_input). - Navigation & Viewport: Mouse wheel scroll handling and viewport mutation handling.
- Macros: Macro rules for adding signal helper methods to custom element structs.
How Pull Request previews are deployed
mainPull request previews are triggered via a maintainer review.
- A maintainer must submit a
/deploycomment review from the pull request's Files changed → Review changes dialog. - This works for both fork and same-repository pull requests.
- The deployment is bound to the exact reviewed commit. If the PR changes, a new
/deployreview is required. - Submitting a new
/deployreview cancels any previous preview build or deployment for that PR. - New commits to a PR will automatically cancel older in-progress preview workflows for that same PR.
- New pushes to
mainwill cancel older in-progress example deployments.
- A maintainer must submit a
Run haalka examples locally
mainYou can run the provided examples using
cargo runwith the--exampleflag.Example commands:
cargo run --example counter cargo run --example button cargo run --example align cargo run --example scroll cargo run --example snakeIf you have
justinstalled, you can also use it to run examples:just example snake -rcargo run --example counterGenerate GitHub Actions workflows with just sync_nickels
mainAfter configuring your
.nclandjustfileas described, run the following command to generate the GitHub Actions workflows:just sync_nickelsSet up the development environment
mainTo set up a local development environment for
haalka, follow these steps:- Clone the repository: Use the following command to avoid the
gh-pagesbranch and ensure all submodules are included:git clone --single-branch --branch main --recurse-submodules https://github.com/databasedav/haalka.git - Install
just: A command runner used for project tasks. - Install
nickel: Required for modifying CI configuration. Ensurenickelis in yourPATH. - Install File Watcher: (VS Code extension) Recommended for automatically syncing nickels.
git clone --single-branch --branch main --recurse-submodules https://github.com/databasedav/haalka.git- Clone the repository: Use the following command to avoid the
Setup haalka in a Bevy App
mainTo use haalka, add
HaalkaPluginto your BevyApp. You typically spawn your UI root using theui_root()helper within aStartupsystem. Note thatui_root()returns animpl Elementwhich can be spawned into the BevyWorld.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(); } // Example ui_root implementation fn ui_root() -> impl Element { El::<Node>::new() .child(El::<Text>::new().text("Hello Haalka")) } fn camera(mut commands: Commands) { commands.spawn(Camera2d); }Integrate kaaj into Nickel workflows
mainTo use
kaajas a Nickel package for GitHub Actions output targets, add it to yourNickel-pkg.ncldependencies. You can then import specific targets (likebuild_example,ci,release, etc.) into your.nclfile and configure them with your repository URL and deployment targets.# In Nickel-pkg.ncl dependencies = { kaaj = 'Git { url = "https://github.com/databasedav/haalka", ref = 'Tag "v0.1" }, } # In your .ncl file let { build_example, ci, cleanup_pr_previews, deploy_reviewed_pr_preview, examples_on_main, pr_previews, release, reviewed_pr_preview, .. } = import kaaj in let REPO = "https://github.com/databasedav/haalka" in let GITHUB_PAGES_URL = "https://databasedav.github.io/haalka" in { build_example_ = build_example "webgpu", ci_ = ci, cleanup_pr_previews_ = cleanup_pr_previews REPO, pr_previews_ = pr_previews, reviewed_pr_preview_ = reviewed_pr_preview, deploy_reviewed_pr_preview_ = deploy_reviewed_pr_preview REPO GITHUB_PAGES_URL, examples_on_main_ = examples_on_main REPO, release_ = release, }Set up kaaj in a Justfile
mainTo use the
kaajJustfile for Bevy-related CI tasks (like building or serving examples), you must fetch thekaaj.justfile usingcurland import it into your localjustfile. You can configureexclude_examplesandexport_nickelsto control which examples are processed and which Nickel targets are exported to GitHub Actions workflows.KAAJ_HAALKA_COMMIT := "5091123d38b0a0eafd3b7d6b46350f6072748f0b" fetch_kaaj_justfile: curl https://raw.githubusercontent.com/databasedav/haalka/{{ KAAJ_HAALKA_COMMIT }}/kaaj/justfile > kaaj.just import? 'kaaj.just' exclude_examples := '"example1", "example2"' export_nickels := "ci build_example pr_previews reviewed_pr_preview deploy_reviewed_pr_preview examples_on_main cleanup_pr_previews release"What is an Element in haalka?
mainAn
Elementis the fundamental UI building block in haalka. It is a type that wraps ajonmo::Builderand provides high-level UI capabilities.Elements are categorized into two types:
- Primitive Elements: Types like
El<Node>orColumn<Node>that directly hold ajonmo::Builder. These implement theElementtrait directly. - Widgets: Custom UI components that wrap other elements. These implement the
ElementWrappertrait, which automatically providesBuilderWrapperandElementimplementations.
All
Elements can be aligned using haalka's alignment semantics and can be granted additional abilities like pointer event awareness, viewport mutability, or scrollability.use bevy::prelude::*; use haalka::prelude::*; // Example of a custom Widget using ElementWrapper struct MyWidget { el: El<Node>, data: usize, } impl ElementWrapper for MyWidget { type EL = El<Node>; fn element_mut(&mut self) -> &mut Self::EL { &mut self.el } } // Widgets can then implement UI abilities impl GlobalEventAware for MyWidget {} impl PointerEventAware for MyWidget {} impl ViewportMutable for MyWidget {} impl MouseWheelScrollable for MyWidget {}- Primitive Elements: Types like
Create custom widgets using ElementWrapper
mainThe
ElementWrappertrait is the foundation for building complex, reusable "widgets". A widget is a struct that wraps anElement(or anotherElementWrapper) and can implement additional UI traits to extend its functionality.To create a widget, implement
ElementWrapperby defining the type of element it wraps and providing a mutable reference to it viaelement_mut().use bevy::prelude easily; use haalka::futures_signals::prelude::*; struct MyWidget { el: El<Node>, data: Mutable<usize>, } impl ElementWrapper for MyWidget { type EL = El<Node>; fn element_mut(&mut self) -> &mut Self::EL { &mut self.el } } // You can now grant the widget additional abilities impl GlobalEventAware for MyWidget {} impl PointerEventAware for MyWidget {} impl ViewportMutable for MyWidget {} impl MouseWheelScrollable for MyWidget {}