A high-performance and borrow-checker friendly framework for declarative programming in Rust. This crate provides a generic library that lets you define reactive components (also known as composables).
- Declarative and reactive scenes for Bevy
- Efficient and borrow-checker friendly state management: Manage state with components and hooks, all using zero-cost smart pointers
- Generic core for custom backends
// Counter UI example.
use actuate::prelude::*;
use bevy::prelude::*;
// Counter composable.
#[derive(Data)]
struct Counter {
start: i32,
}
impl Compose for Counter {
fn compose(cx: Scope<Self>) -> impl Compose {
let count = use_mut(&cx, || cx.me().start);
spawn(Node {
flex_direction: FlexDirection::Column,
..default()
})
.content((
spawn(Text::new(format!("High five count: {}", count))),
spawn(Text::new("Up high"))
.observe(move |_: Trigger<Pointer<Click>>| SignalMut::update(count, |x| *x += 1)),
spawn(Text::new("Down low"))
.observe(move |_: Trigger<Pointer<Click>>| SignalMut::update(count, |x| *x -= 1)),
if *count == 0 {
Some(spawn(Text::new("Gimme five!")))
} else {
None
},
))
}
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d::default());
// Spawn a composition with a `Counter`, adding it to the Actuate runtime.
commands.spawn((Node::default(), Composition::new(Counter { start: 0 })));
}
fn main() {
App::new()
.add_plugins((DefaultPlugins, ActuatePlugin))
.add_systems(Startup, setup)
.run();
}
Composables can borrow from their ancestors, as well as state.
use actuate::prelude::*;
#[derive(Data)]
struct User<'a> {
// `actuate::Cow` allows for either a borrowed or owned value.
name: Cow<'a, String>,
}
impl Compose for User<'_> {
fn compose(cx: Scope<Self>) -> impl Compose {
spawn(Text::new(cx.me().name.to_string()))
}
}
#[derive(Data)]
struct App {
name: String
}
impl Compose for App {
fn compose(cx: Scope<Self>) -> impl Compose {
// Get a mapped reference to the app's `name` field.
let name = Signal::map(cx.me(), |me| &me.name).into();
User { name }
}
}
To add this crate to your project:
cargo add actuate --features full
This crate is inspired by Xilem and uses a similar approach to type-safe reactivity. The main difference with this crate is the concept of scopes, components store their state in their own scope and updates to that scope re-render the component.
State management is inspired by React and Dioxus.
Previous implementations were in Concoct but were never very compatible with lifetimes.