bevy_easy_ui
A declarative, fluent builder-pattern abstraction layer on top of Bevy's UI system and bevy_ui_text_input
Version compatibility
| bevy_easy_ui | bevy_ui_text_input | bevy |
|---|---|---|
| 0.1.0, 0.1.1 | 0.7.0 | 0.18.1 |
What is it
bevy_easy_ui turns this:
commands.spawn
.with_children;
…into this:
new
.with_border_color
.with_border
.with_width
.with_height
.with_background_color
.with_child
.spawn;
Every setter is chainable, type-checked, and the trait system prevents misusing a container as a non-container (or vice versa).
Quick start
# Cargo.toml
[]
= "0.18.1"
= "0.1.1"
use *;
use *;
Run with cargo run — a centered dark button with a white border and white text appears.
Examples
Each example is a standalone cargo run --example NAME showcasing a specific widget or pattern.
| Example | What it shows |
|---|---|
hello |
Minimal setup: a centered button with a label |
button_with_observers |
Buttons with Pointer<Over> / Pointer<Out> / Pointer<Click> observers (hover, click feedback) |
image_button |
Icon button built from EasyButton + EasyImage as a child |
rounded_image |
EasyImage with various border_radius values (sharp, small, full, asymmetric) |
scroll |
Scrollable EasyVerticalLayout and EasyHorizontalLayout using Overflow::scroll_y() / scroll_x() + the ScrollPlugin mouse-wheel observer |
viewport |
EasyViewport rendering a live camera output into a UI node |
rich_text |
EasyRichText with per-EasySpan colors, sizes, and justify |
text_input |
EasyTextInput (re-export of bevy_ui_text_input) |
Widgets
The crate ships a set of Easy* builders, each wrapping the matching Bevy component(s) with a fluent API.
| Widget | Bevy base | Kind | Purpose |
|---|---|---|---|
EasyVerticalLayout |
Node + FlexDirection::Column |
container | Flex column layout |
EasyHorizontalLayout |
Node + FlexDirection::Row |
container | Flex row layout |
EasyButton |
Button + Node |
container | Clickable button (accepts children + observers) |
EasyRichText |
Text + TextSpan children |
container | Multi-style text |
EasyLabel |
Text + Node + Label |
non-container | Text marked as a label |
EasyText |
Text + Node + TextFont + TextColor |
non-container | Styled text |
EasySpan |
TextSpan |
non-container | Inline span used inside EasyRichText |
EasyImage |
ImageNode + Node |
non-container | Image (rect, color, flip, mode, atlas) |
EasyTextInput |
bevy_ui_text_input::TextInputNode |
non-container | Re-export of bevy_ui_text_input |
EasyViewport |
Node + ViewportNode |
container | UI node displaying a Camera render target |
Containers (layouts, button, rich_text, viewport) implement the Container trait and expose:
.with_child(impl Into<EasyElement>)— adds a child.with_observer(impl IntoObserverSystem)— attaches a Bevy observer.spawn(&mut Commands)— finalizes and spawns the tree
Non-containers (label, text, image, text_input) implement WithObservers and only expose .with_observer(...) and .spawn(...).
Reusable styles with with_style
Every widget also exposes a with_style(style: <Widget>Style) setter that swaps the whole style bundle at once — Node + EasyBoxStyle + EasyStackStyle (+ EasyTextStyle for text widgets). It's the same shape as a Bevy bundle, but assembled ahead of time.
Use it when you have a few pre-defined looks (e.g. a theme) you want to apply as a unit, without chaining ten with_* calls every time:
use *;
use *;
The available style types are: EasyButtonStyle, EasyVerticalLayoutStyle, EasyHorizontalLayoutStyle, EasyRichTextStyle, EasyLabelStyle, EasyTextWidgetStyle, EasySpanStyle, EasyImageStyle, EasyViewportStyle.
The traits
Four extension traits add the builder setters on top of Bevy's components. They are implemented for every widget that owns the matching Bevy component, so the setters are always available.
EasyNode — Node properties
Size (with_width, with_height, with_min_*, with_max_*), position (with_position, with_top, etc.), alignment (with_align_items, with_justify_content, …), spacing (with_margin, with_padding, with_row_gap, with_column_gap), borders (with_border, with_border_radius; with_border_color is in EasyBoxStyleExt), flex (with_flex_direction, with_flex_wrap, with_flex_grow, with_flex_shrink, with_flex_basis), grid (with_grid_template_*, with_grid_auto_*, with_grid_row, with_grid_column), overflow (with_overflow, with_scrollbar_width, with_overflow_clip_margin), display (with_display, with_box_sizing, with_aspect_ratio).
EasyBoxStyleExt — background, border, shadow
with_background_color, with_border_color, with_box_shadow, with_border_gradient, with_background_gradient, with_outline.
EasyStackStyleExt — z-index
with_z_index, with_global_z_index.
EasyTextStyleExt — text-specific
with_color (text color), with_font_family, with_font_size, with_font_weight, with_smoothing, with_features, with_justify, with_linebreak, with_line_height, with_text_shadow / with_shadow.
EasyImageNode — ImageNode properties
with_image, with_image_color, with_texture_atlas, with_flip_x, with_flip_y, with_rect, with_image_mode.
Scrollable containers
The crate ships a tiny ScrollPlugin that turns the mouse wheel into a Scroll event you can attach to any Overflow::scroll_*() node:
use *;
use *;
Hold Ctrl while scrolling to scroll horizontally instead.
Colors
The prelude re-exports bevy::color::palettes::css::* (WHITE, BLACK, BLUE, DARK_GRAY, LIGHT_BLUE, …) so every color literal slots directly into a with_*_color(...into()) call:
new
.with_background_color
.with_border_color
If you need a custom color, build it with bevy::color::Color::srgba(...) and pass it through .into().
Contribution
Open an issue or a PR if you have suggestions, questions, or want to add a new widget or feature.
Roadmap
The following widgets are planned but not yet wrapped as Easy* builders. They will be implemented by following the same pattern as the existing widgets, on top of the corresponding headless types in bevy_ui_widgets:
EasyCheckbox— wrapsbevy_ui_widgets::Checkbox+Checkable+CheckedEasySlider— wrapsbevy_ui_widgets::Slider+SliderValue+SliderRange(+ optionalSliderStep/SliderPrecision)EasyRadioButton+EasyRadioGroup— wrapsbevy_ui_widgets::RadioButton+RadioGroupEasyScrollbar— wrapsbevy_ui_widgets::Scrollbar+CoreScrollbarThumb
If you'd like to take one of these, the integration checklist below explains the wiring once the widget compiles.
Adding a new widget
The crate follows a consistent pattern across all widgets — pick whichever existing widget is closest to what you want to build, then copy it:
- Bundle —
#[derive(Bundle)] pub struct EasyXxx { ... pub node: Node, pub box_style: EasyBoxStyle, pub stack_style: EasyStackStyle }. The bundle is the raw Bevy components grouped together. - Container —
pub struct EasyXxxContainer { bundle, children: Vec<EasyElement>, observers: Vec<Observer> }. Holds the bundle plus any children/observers queued during building. - Builder —
EasyXxx::new() -> EasyXxxContainerandEasyXxx::default_bundle() -> Self.new()always returns the container, never the bundle, so setters stay chainable. - Accessor impls —
EasyNode,EasyBoxStyleExt,EasyStackStyleExt(andEasyTextStyleExtfor text widgets). They expose thewith_*setters. Container/WithObserversimpl — picks the right trait:Containerif the widget can have children,WithObserversfor non-containers.- Style — a
pub struct EasyXxxStyle { node, box_style, stack_style }withwith_style(...)on the builder, so users can swap the whole look at once.
Integration checklist
Once the widget compiles, wire it into the rest of the crate so users find it under one import:
- Add a
pub modline insrc/widgets/containers/mod.rs(orsrc/widgets/mod.rsfor non-container widgets). - Add a variant
EasyXxxContainer(EasyXxxContainer)insrc/core/element.rsand a matchingFrom<EasyXxxContainer> for EasyElementimpl. - Re-export the bundle, container, and style with
pub use ...::*;insrc/prelude.rs. - Add a
cargo run --example xxxexample inexamples/and reference it in the Examples table of this README.
Filing issues
For bug reports, include the Bevy version, the crate version, a minimal repro, and what you expected vs. what you got. For feature requests, sketch the API you'd like to call — EasyXxx::new().with_*(...).with_child(...).spawn(&mut commands) is the shape we aim for.
Known limitations
This is a 0.1.1 release — the API works and is covered by the eight examples, but it is still a young library with rough edges. Things will move, names will change, and some patterns may not be fully fleshed out yet. Contributions and bug reports are very welcome, and feedback from early users is the fastest way to make the next version better.
If you hit something unexpected, please open an issue — even small reports help prioritize what to harden next.
License
Dual-licensed under MIT or Apache-2.0 at your option.