Overview of bevy_immediate_core
mainbevy_immediate_core crate provides the foundational functionality for the bevy_immediate ecosystem. It serves as a core component in implementing immediate mode UI within the Bevy game engine.repository·main·Indexed 40 days ago
https://github.com/ppakalns/bevy_immediateA simple, fast, and modular immediate mode UI library for the Bevy engine. It combines immediate mode programming ergonomics with the performance of Bevy's retained ECS-powered UI. Version 0.8.0 is compatible with Bevy 0.19.
bevy_immediate_core crate provides the foundational functionality for the bevy_immediate ecosystem. It serves as a core component in implementing immediate mode UI within the Bevy game engine.New child entities are created using the .ch, .ch_id, and .ch_with_manual_id family of functions.
Crucial Rule: If an entity can appear, disappear, or is created inside a loop, you must provide a unique ID. If you do not, UI nodes may change order or fail to lay out correctly because Bevy may misidentify entities between frames.
ui.ch(): Uses an internal counter for ID generation. Do not use this for appearing/disappearing entities or inside loops.ui.ch_id(id): Manually provides a unique ID. IDs must be unique among siblings.lid!() and lch!(): Helper macros that use current column and line numbers to generate IDs. These are useful but still require an additional unique ID when used inside loops.ui.with_add_id_pref(id): Sets a prefix for the internal ID generator, allowing you to use ui.ch() while still maintaining unique scopes.// Using manual IDs for loops
for idx in 0..count {
ui.ch_id(("my_loop", idx));
// ...
}
// Using ID prefixes for cleaner code in loops
for idx in 0..count {
let mut ui = ui.with_add_id_pref(lid!(idx));
ui.ch();
ui.ch();
}You can extend the immediate mode API by implementing new capabilities. This involves three main steps:
ImmCapability: Use this to request component access (e.g., cap_req.request_component_write::<T>(app.world_mut())) during the build phase.ImmEntity: Define the actual method (e.g., .selected()) that users will call on the UI builder, using cap_get_component_mut to manipulate the underlying components./// Implements capability to mark entities as selectable.
pub struct CapabilityUiSelectable;
impl ImmCapability for CapabilityUiSelectable {
fn build<Cap: CapSet>(app: &mut bevy_app::App, cap_req: &mut crate::ImmCapAccessRequests<Cap>) {
cap_req.request_component_write::<Selectable>(app.world_mut());
}
}
#[derive(bevy_ecs::component::Component)]
pub struct Selectable {
pub selected: bool,
}
/// Implements methods to set entity selectable
pub trait ImmUiSelectable {
fn selected(self, selected: bool) -> Self;
}
impl<Cap> ImmUiSelectable for ImmEntity<'_, '_, '_, Cap>
where
Cap: ImplCap<CapabilityUiSelectable>,
{
fn selected(mut self, selected: bool) -> Self {
if let Ok(Some(mut comp)) = self.cap_get_component_mut::<Selectable>() {
if comp.selected != selected {
comp.selected = selected;
}
return self;
}
self.entity_commands().insert(Selectable { selected });
self
}
}To use bevy_immediate, add it to your project's dependencies in your Cargo.toml file.
Ensure your Bevy version is compatible with the version of bevy_immediate you are using. For example, bevy_immediate v0.8 is compatible with Bevy 0.19.
[dependencies]
bevy_immediate = "0.8"To modify your UI during program execution without restarting, you can use hot-patching (powered by subsecond).
Requirements:
hotpatching feature for both the bevy_immediate and bevy crates.Launch Command:
BEVY_ASSET_ROOT="." dx serve --hot-patch --features "bevy_immediate/hotpatching" --features "bevy/hotpatching" --example demoThe immediate mode API is centered around two main types:
Imm<'w, 's, Caps>: The primary context for building an immediate mode entity tree. It manages the current hierarchy, entity mapping, and provides methods to spawn new entities or unrooted trees.ImmEntity<'r, 'w, 's, Caps>: A handle to an entity currently being constructed. It allows you to issue commands (like adding children or inserting bundles) specifically for that entity.Workflow:
Imm::ch() or Imm::ch_id(id) to create a new entity. This returns an ImmEntity.ImmEntity::add(f) to build descendants of that entity within a closure f.ImmEntity::on_spawn_insert(f) to queue bundle insertions that only occur when the entity is actually created by Bevy's command buffer.// Example workflow
imm.ch_id("button")
.add(|ui| {
ui.ch().on_spawn_insert(|| MyBundle);
});When creating new entities in the immediate mode UI, you can specify how their identity is generated using the ImmIdBuilder enum. This allows you to choose between automatic, hierarchical, or strictly unique identification:
ImmIdBuilder::Auto: Generates an automatically incrementing ID. This is useful for transient elements that don't need a stable identity across frames.ImmIdBuilder::Hierarchy(ImmId): Combines the provided ImmId with the current parent's ID. This is the standard way to ensure children have stable, scoped identities within a UI tree.ImmIdBuilder::Unique(ImmId): Uses a specific, provided ImmId. Use this with caution; it should only be used when you can guarantee that no other entity will share this exact ID.// Example of how you might conceptually use the builder
// (Note: actual usage depends on the specific API calling the builder)
// 1. Automatic ID
let builder_auto = ImmIdBuilder::Auto;
// 2. Hierarchical ID (scoped to a parent)
let parent_id = imm_id("container");
let builder_hier = ImmIdBuilder::Hierarchy(parent_id);
// 3. Explicit Unique ID
let explicit_id = imm_id(42);
let builder_unique = ImmIdBuilder::Unique(explicit_id);In bevy_immediate_ui, capabilities are grouped into sets that define the available UI features based on the enabled Cargo features. These sets determine which interaction, styling, and widget capabilities are accessible to your immediate mode UI code.
CapsUiBase: The foundation set containing core layout, look, interaction, and text capabilities.CapsUi: Extends CapsUiBase. If the bevy_ui_widgets feature is enabled, it includes activated::CapabilityUiActivated and slider_value::CapabilityUiSliderValue.CapsUiFeathers: The most comprehensive set. If the bevy_feathers feature is enabled, it extends CapsUi with advanced styling and input capabilities like number_input, slider_base_color, button_variant, color_swatch, and color_plane.When building custom UI logic or extending the library, you should target the capability set that matches your project's feature configuration to ensure all required traits are available.
To implement focus-aware floating UI, use the following components:
FocusParent(pub Entity): Attach this to the root entity of a UI hierarchy. The Entity inside should point to the next parent in the focus chain. This allows the plugin to traverse upwards to find the true root of a focused tree.FocusDetectShouldClose: Attach this to the root entity of a UI tree to signal that this tree should be monitored for closing when the user clicks elsewhere.When the plugin determines a UI should close, it will automatically insert the FocusShouldClose component onto the root entity.
// Example setup for a floating UI root
commands.spawn((
FocusParent(some_other_ui_root),
FocusDetectShouldClose,
// ... other UI components
));To extend bevy_immediate with new features, you use a capability-based system.
ImmCapability: Implement this trait to define a specific feature. The build method is used to initialize the necessary Bevy resources for that capability.CapSet: Implement this trait to group multiple capabilities together. The initialize method triggers the build process for all capabilities in the set.ImplCap<T>: A marker trait used to indicate that a CapSet provides a specific capability T.The plugin uses two helper types to manage 2D alignment:
AnchorDefines alignment points along an axis:
Anchor::Start: Left or Top.Anchor::Middle: Middle.Anchor::End: Right or Bottom.Direction<T>A wrapper for storing values for both the X and Y axes.
Direction::splat(val): Creates a direction where both X and Y are the same value..map(|v| ...): Allows transforming the values within the direction.The FloatingUiFocusPlugin manages focus behavior for floating anchored UI hierarchies. It handles closing UI elements when a user interacts with something else on the screen. To use it, add the plugin to your Bevy App.
app.add_plugins(FloatingUiFocusPlugin);