# bevy_reactor builder pattern
## Introduction
You know, they say "the third time's the charm". Well, *third time* was in the rear-view mirror quite a while ago: I've lost count of the number of times I've completely revamped my Bevy UI framework, I think it's up to like eight or nine.
This latest revamp completely discards the concept of "Views" (an idea taken from Xilem) in favor of a more functional/builder-like approach, similar to what is seen in egui and sickle_ui.
> [!NOTE]
> If you're not familiar with the *View* paradigm, a lot of this section won't make sense. The short version: views were nodes in a tree that was constructed for the purpose of building and dynamically updating an entity tree - in effect, a blueprint or scaffolding that defined the shape and attributes of the final output.
It's kind of amazing how much difference a subtle change can make: the introduction of ghost nodes is what unlocked this new approach. Ghost nodes allows reactive generators, such as `cond`, `switch`, and `for_each` to be independent: once created, the nodes no longer need to have any connection to the framework they created them. They are completely framework-agnostic. It means we can truly separate the *scaffolding* from the *edifice*.
The View paradigm had other issues, mainly around mutability - because they get built incrementally via a fluent API, they need to be mutable, but all of the data they contain eventually needs to be transferred to the ECS world, via either copy or move. Some kinds of data, such as closures (and fine-grained systems use a lot of closures) are not easily transferrable without extra work, such as wrapping them in an Option or Arc.
The new approach extends the existing Bevy `WorldEntityMut`, adding new methods to create reactive elements. This style of API will be familiar and comfortable for most Bevy users. It also allows parameters, such as closures and components, to be immediately consumed, obviating the need for copyable/cloneable data.
This approach discards views almost entirely. I say, "almost" because there is one vestige left: templates. However, templates no longer use views internally.
Note that this approach does require mutable access to `World`, because that's needed for `EntityWorldMut`. This doesn't rule out a future alternative which uses `EntityMut`, and which can be used with `Commands`, however this would require duplicating large parts of the code. Even though the reactive elements being created would be the same in both approaches, the code to construct those elements would have to be different. (In particular, the styling system currently assumes it can add and remove components at will.)
## Signals and Reactions
The lowest layer of `bevy_reactor` defines signals and reactions, and this has mostly not changed. This layer defines a number of reactive primitives:
* **ReactionCell** is a `Component` which contains a `Reaction` trait object. There are many kinds of reactions, used for things like dynamically updating a component attribute or creating child entities.
* **TrackingScope** is a `Component` which stores the list of data dependencies.
* **Signals** are copyable/clonable handles which reference data sources such as mutables, derived computations, and so on. They are designed in a way that lets them easily be captured by closures.
* **Reaction Context** (`Rcx`) is a parameter which is passed into reaction functions, allowing access to reactive data sources. `Rcx` only allows read-access to the world; there's another version, `Ecx` (effect context), which is used for side-effectful reactions and permits a limited set of mutations.
Generally, reactions fall into two categories:
* "Control-flow" reactions, such as `cond`, `switch` and `for_each`. These are ghost nodes that dynamically generate their children. They have a tracking scope and a reaction cell on the node itself.
* "Attribute-modifying" reactions are attached to target entities as children and modify their parent. They are very similar to entity-based observers in this respect. These are implemented as child ghost nodes, and also have a reaction cell and tracking scope.
## Builders
The new API adds methods to `EntityWorldMut` using extension traits.
### Example: `insert_if`
Let's start with an simple example: we're going to add a new method, `.insert_if()`, which is similar to the existing `.insert()` method, except that it inserts the component conditionally. This means not only inserting the component when the condition is true, but removing it when the condition is false.
Also unlike `.insert()`, which only executes once at build time, the reaction created by `.insert_if()` sticks around for as long as the entity lives, continually updating the component whenever the condition changes.
> [!NOTE]
> It would be possible to implement this same idea in a non-reactive framework by evaluating the condition every frame.
One possible use case for this might be inserting a `Disabled` component on a `Button` whenever the button should be disabled:
```rust=
button.insert_if(disabled_signal, || Disabled);
```
The first argument is the condition. This condition can be either a `Signal<bool>`, or a closure which accepts a reaction context:
```rust=
button.insert_if(
|rcx: &Rcx| rcx.read_resource::<GamePaused>.0,
|| Disabled
);
```
The second argument is a function which constructs the component to be inserted. The reason this is a closure, rather than just passing in the component directly, is twofold:
* If the condition is false, we want to avoid the overhead of constructing the component.
* As the condition changes, we may end up inserting the same component multiple times. This would mean keeping around a copy of the component and would also require it to be cloneable. By constructing the component anew each time, we avoid this problem.
> [!NOTE]
> For simplicity of removal, we only support single components, not bundles.
Notice that the second argument to `.insert_if()` takes no parameters: it's not reactive, meaning that it won't react on it's own. This is a common pattern which we'll see more examples later, in which there are two closures:
* The first closure is reactive, and computes an output value.
* The second closure is not reactive, and only runs after the first closure reacts. It takes as input the output of the first closure and uses that value to apply effects to the target.
Part of the reason for doing this is because the `Rcx` param contains an immutable world borrow, making it difficult to make changes to the world within the function. That borrow is dropped before the second function is called.
So how does `.insert_if()` actually work? Here's what it does under the hood:
* Spawns a new child of the entity.
* Inserts a tracking scope, a reaction cell, and a `GhostNode` marker.
* Runs the reaction once. (All reactions need to be run once in order to "prime the pump", that is, to populate the tracking scope with dependencies)
The reaction cell contains a `ConditionalInsertComponentReaction`, which is a struct that implements the `Reaction` trait. `Reaction` has one method, `react`, which in this case either adds or removes the component.
### Other methods
Some other reactive attribute methods that have been added are:
* `.style()` and `.styles()`, which accept style builder functions.
* `.style_dyn()` which computes styles dynamically.
* `.create_children()` - see next section.
(Other methods, such as `.insert_dyn()` coming soon!)
### Child Builders
In my original prototype, I added new methods to `WorldChildBuilder` to allow creating reactive children; however eventually I created my own (similar) struct called `UiBuilder` which has additional features.
The method `.create_children(builder)` is almost identical to the existing `.with_children(builder)` method, except that the builder function gets passed a `UiBuilder`.
`UiBuilder` supports the normal `spawn()` and `spawn_empty()` methods which create a new entity as a child of the parent. It also provides additional methods for control flow:
* `.cond()` creates a conditional branch, essentially an "if" statement which constructs either the "true" branch or the "false" branch depending on a test condition. If the condition changes, then the previous branch is despawned and the new branch constructed.
* `.switch()` is a more elaborate conditional branch, similar to a C "switch" statement.
* `.for_each()` (Not yet implemented) will accept a reactive array value, and generate a child for each array element.
* `.invoke()` is used to call a template (see next section).
Here's an example of how to use `.cond()`:
```rust
builder.cond(
|rcx: &Rcx| {
let counter = rcx.read_resource::<Counter>();
counter.count & 1 == 0
},
|builder| {
builder.text("[Even]");
},
|builder| {
builder.text("[Odd]");
},
)
```
Just like `.insert_if()`, you can pass either a signal or a reactive boolean function as the test condition.
`UiBuilder` also has convenience methods for creating text nodes:
* `.text()` creates a static text node with a single section.
* `.text_computed()` creates a dynamic text node.
Both of these methods insert the `UseInheritedTextStyles` marker, which allows them to inherit font and text properties from their parent nodes.
Finally, `UiBuilder` has a number of utility functions for creating "owned" objects. Owned objects are objects that are not technically child nodes (they aren't visible), but which are despawned along with the parent entity:
* `.create_callback()` allows you to register a one-shot system that is owned by the parent entity.
* `.create_effect()` creates a generalized, side-effectful reaction.
One might question why these methods live on `UiBuilder` rather than on `EntityWorldMut`. Part of the reason is that `UiBuilder` is what gets passed to templates. But another reason is that these objects generally aren't targeted at a single entity - that is, they are *owned* by the current context, but they are *used* by the children of that context.
## Templates
One important feature of the UI framework is to be able to define "widgets", that is, re-usable modular sub-trees which have predefined behaviors.
One simple way to implement this would be to make widgets functions: to create a button, call `button()`. The problem with this approach is that most widgets have a large number of parameters, and so the function signature would quickly get complicated.
Also, most of the time we're only setting a small number of parameters, so we end up writing a lot of boilerplate to pass in the default values.
To avoid this, we could use the "parameter builder" pattern, where you have some struct that contains all the function parameters, and use a fluent API to populate them. This object then gets passed into the function.
But if we're going to go through the trouble of defining a struct with all these properties, then why do we need a standalone function at all? Why not just make it a method on the parameter object and "cut out the middleman"?
This is what the `UiTemplate` trait does. It's actually a very simple trait:
```rust
pub trait UiTemplate {
fn build(&self, builder: &mut UiBuilder);
}
```
A template is nothing more than a struct which implements this trait. The struct can have whatever properties it needs, and can be initialized in whatever way you want. Once the template is constructed, you can execute it by calling `builder.invoke(template)`.
Here's an example of invoking the Obsidian `Button` template:
```rust=
builder
.invoke(
Button::new()
.variant(ButtonVariant::Primary)
.labeled("Primary"),
)
.invoke(
Button::new()
.variant(ButtonVariant::Danger)
.labeled("Danger"),
)
```
Note that templates don't live very long: they are constructed and immediately executed; once executed, they are dropped.
Interally, the button template just calls the builder argument:
```rust!
impl UiTemplate for Button {
fn build(&self, builder: &mut UiBuilder) {
// Note: This example is massively simplified.
// The real button template is 2 pages long.
let label: String = self.label.into();
let button = builder.spawn()
.style(button_style)
.observe(click_handler)
.create_children(|builder| {
builder.text(label)
})
});
}
}
```
> [!Warning]
> If you are familiar with React.js, you might be confused by the fact that `bevy_reactor` templates don't provide root-level reactivity. Unlike React, where the "component function" is executed multiple times, the `UiTemplate::build` method is only executed once. The only "reactions" are the ones you explicitly spawn, by calling reactive methods such as `.insert_if()` and `.cond()`.
>
> As a general rule, if you don't see an `Rcx` parameter in the function signature, then it's probably not a reactive function.