Crates.io | recompose |
lib.rs | recompose |
version | |
source | src |
created_at | 2025-01-15 19:02:09.526388 |
updated_at | 2025-02-07 19:11:22.9106 |
description | Declarative framework for the Bevy game engine |
homepage | |
repository | https://github.com/ad-kr/recompose |
max_upload_size | |
id | 1517975 |
Cargo.toml error: | TOML parse error at line 17, column 1 | 17 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include` |
size | 0 |
recompose
is a crate for Bevy that provides a declarative way to compose structures in
a way that is easy to write, and is ECS- and borrow-checker friendly.
It is most useful for UI-building, but can be applied to other ECS-structures as well. For more information, check out the examples and docs.
use bevy::prelude::*;
use recompose::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(RecomposePlugin)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands.spawn((Root::new(squares), Node::default()));
}
// `Fn(&mut Scope) -> impl Compose` implements Compose, so we can use functions for simple composables.
fn squares(cx: &mut Scope) -> impl Compose {
let count = cx.use_state(42);
Node {
display: Display::Flex,
column_gap: Val::Px(8.0),
..default()
}
.children((
Square(Srgba::RED.into()),
Square(Srgba::GREEN.into()),
Square(Srgba::BLUE.into()),
Text::new(count.to_string()).to_compose(),
))
}
#[derive(Clone)]
struct Square(Color);
// For more complex composables with input, we can implement Compose directly on a struct.
impl Compose for Square {
fn compose<'a>(&self, _: &mut Scope) -> impl Compose + 'a {
(
Node {
width: Val::Px(32.0),
height: Val::Px(32.0),
..default()
},
BackgroundColor(self.0),
)
.to_compose()
}
}
If you've ever used React or similar libraries, you might be familiar with the concept of "hooks". In recompose
,
"hooks" are functions that interact and/or modify the Scope
. Read the How it works section for
more details.
Unlike React, not all hooks are required to follow the "rules of hooks" - being called in the same order, and never
conditionally, in a loop and so on. Functions that must obey these rules are prefixed wiht use_
.
Some of the hooks available:
State
let count = cx.use_state(42); // Get a state that persists between recompositions.
cx.set_state(&count, *count + 1); // Set the state to a new value.
Composable lifetime
cx.use_mount(|| { /* Do something */}); // Called when the composable is first composed.
cx.effect(|| { /* Do something */}, (&count, &name)); // Called only when dependencies have changed.
ECS World interaction
// Run a system each time the composable is recomposed.
cx.run_system(|names: Query<&Name>, mut state: SetState| {
state.set(&count, 30); // Set the state to a new value.
state.modify(&count, |count| *count + 1); // Modify the state.
});
// Run a system once, when the composable is first composed.
cx.use_system_once(|| { /* .. */ });
recompose
is built around the concept of composables that implement the Compose
trait. The
compose
function of the Compose
trait modifes the given Scope
(a Scope
can be thought of as a node in a tree-structure) and may return a new Compose
, which is then added as
the current Scope
s' child. The compose function is called when the composable is first added, when one of the
scope's state changes, or when the parent composable "recomposes".
Some notable Compose
implementations:
Fn(&mut Scope) -> impl Compose
- A function that takes a mutable reference to a Scope
and returns a
composable. Useful for simple composables that don't need any input.Spawn
- Spawns a new entity with the from a bundle.DynCompose
- Allows for dynamic composables that "erase" their type definition.Option<C>
- Composes C
if the option is Some
, otherwise does nothing.(C0, .., C9)
- Compose multiple composables at once.Vec<C>
- Compose any number of composables. This requires that the items implement the
Key
-trait.
Keyed
- Implements the Key
-trait and can be used to wrap any composable. The added
advantage is that the type is "erased" so that composables of different types can be composed in the same
Vec
.()
- Empty composable that does nothing. Implementing Compose
for ()
lets us skip returning anything from
the compose
function.Bundle
Bundle
-trait does not implemented the Compose
-trait, however it does implement the
BundleExtension
-trait which lets us convert a Bundle
into a
Spawn
-composable very easily. Through BundleExtension
, it also implements
ModfiyFunctions
-trait which lets us use functions like
children
, observe
.Bevy | recompose |
---|---|
0.15 | 0.1-0.3 |
Recompose is heavily inspired by the actuate crate, which also provides a declarative
way to construct ECS structures. This crate strives to be more robust, safer and less error-prone than actuate
.
The goal of recompose
is not necessarily to be the most performant solution, but rather one that is easy to use
and easy to understand.