bevy_immediate: Immediate Mode UI for Bevy
A simple, fast, and modular UI library for Bevy, combining immediate mode ergonomics with Bevy ECS-powered retained UI.
- Develop complex UI as simple Rust code.
- UI visuals, styling is fully customizable.
- Extend immediate mode with custom extensions / capabilities.
๐ Web Demo ๐
Features
- Immediate mode entity hierarchy management
Build interactive entity hierarchies with a clean API. - Fully compatible with Bevy
Heavy lifting is done by Bevy ECS andbevy_uiretained mode UI. - Custom extension support
Add custom capabilities like.clicked(),.selected(true),.hovered(). Extension use integrated with rust type system for autocompletion and compile time check support. - Inbuilt support for UI use case
Contains extensions that implement necessary logic for constructing UI. - Reusable widgets
Implement widgets using functional or bevy native style. - Hot-patching support
- Supported out of the box. See hotpatching.
- Alternative: hot_lib_reloader.
- Fast
Only visits each entity once per tick and does minimal amount of changes. Heavy lifting is done by Bevy's retained UI. - Parallelizable
Minimal data access requirements allow systems to run in parallel with other systems without exclusive world access. - Simple
Define UI in straightforward functions, free from macro/observer/trigger boilerplate. - Modular
Develop your UI by writing UI in small composable parts. Extend functionality with modular extensions. - Integration-friendly
Works with other libraries (e.g., reloadable CSS style with bevy_flair).
โ ๏ธ Note: This library is under active development. Expect some breaking changes, but they will be minimized.
Version compatibility
| bevy_immediate | bevy | MSRV |
|---|---|---|
| 0.3 | 0.17 | 1.88 |
| 0.2 | 0.17 | 1.88 |
| 0.1 | 0.16 | 1.85 |
To use add bevy_immediate to your project dependencies in Cargo.toml file.
See CHANGELOG for changes between versions.
Examples
Examples can be viewed: (cargo run --example demo).
- Hello world - Minimal usage example
- Power user - Customized API for complex use cases
- Plain UI - Create your UI as a single system
- Bevy inbuilt widgets:
- Widgets - Showcases how to use widgets from bevy
- Scrollarea - Showcases how to create reusable scrollareas
- Text edit - Showcases text edit integration using bevy_ui_text_input crate.
- Reusable widget implementation
- Functional widget - Implement widgets as plain functions
- Native widget - Implement native Bevy-like widgets
- Widget use - Use functional and native widgets together
- Main menu example - Build a simple main menu with selectable buttons
- Floating elements
- Tooltips - Add tooltips.
- Anchored UI - Build drop down menus, comboboxes, popups that utilize anchored floating UI.
- Floating windows - Resizable, draggable floating windows.
- Extensions
- Extension implementation - Write your own capabilities (e.g.
.clicked()or.selected(...)) - Using extensions - Use a custom predefined set of extensions
- Extension implementation - Write your own capabilities (e.g.
- Style - Contains UI styling implementation for examples
- Hot-Patching example - Modify UI during program execution: See Hotpatching section.
Examples are located in ./examples/
Interactive UI example
Using bevy_feathers and bevy_ui_widgets.
// Checkbox
ui.ch
.on_spawn_insert
.checked;
// Toggle switch
ui.ch
.on_spawn_insert
.interactions_disabled // Control whether interactions are enabled
.checked;
// Button that counts clicks
let mut button = ui.ch.on_spawn_insert
.add;
if button.activated
Power user example
Here's a more advanced example where user has added their own API.
;
;
Extend functionality by implementing new capability
You can add new capabilities with just a few lines of code.
Hereโs how .selected(...) is implemented.
/// Implements capability to mark entities as selectable.
;
/// Marks component as being selectable
/// Implements methods to set entity selectable
New entity creation
New child entities can be created with .ch, .ch_id, .ch_with_manual_id family of functions.
For child entity creation that could appear, disappear, that are created inside loop: unique id must be provided.
Provided id is combined with parent id. Id must be unique between siblings.
Examples:
ui.ch_id;
ui.ch_id;
lch!;
for idx in 0..count
ui.ch; // Has internal counter for id generation, but can not be used
// for appearing, disappearing entities.
// Because between frames entities may get misidentified.
for idx in 0..count
lid, lch helper macros use current column, line numbers to generate auto id. But still inside loops you need to provide additional unique id.
Hotpatching
Follow: Instructions & Limitations
Launch examples with:
BEVY_ASSET_ROOT="." dx serve --hot-patch --features "bevy_immediate/hotpatching" --features "bevy/hotpatching" --example demo
Make sure that you enable hotpatching feature bevy_immediate and bevy crates so that UI is recreated upon hotpatch.
Try to modify and save ./examples/hot_patching.rs or any other example and see changes in the live demo.
FAQ
UI nodes are changing order and not correctly laid out
Make sure that you assign unique id using ch_id for ui nodes that
can appear, disappear.
How do I avoid collisions with resources or queries in my systems?
- Queries: Add
Without<ImmMarker<Caps>>to your query filter. - Resources: Avoid direct conflicts, or use .ctx() / .ctx_mut() APIs to access resources used by capabilities.
Contributing
Contributions are welcome!
- Add your improvements to examples
- Suggest or implement new capabilities useful for UI creation
Publish your own crate that is built using bevy_immediate!
Inspiration
- Originally created for Settletopia
- Inspired by egui_taffy.
- Initial idea discussion
Future work
-
Easier definition of new capability sets
- Tried transitive capability implementation (works only inside one crate)
- Tried transitive trait implementation (works only inside one crate)
- Tried TupleList approach (conflicting trait implementations)
- ???
-
Create reusable logic for:
- Bevy ui widgets
- Bevy scroll areas
- Tooltips
- Popups
- Draggable, resizable windows (like
egui::Window)