Skip to content

feat: reactive ui row templates - #725

Draft
makspll wants to merge 6 commits into
agent/reactive-ui-script-propsfrom
agent/reactive-ui-templates
Draft

makspll wants to merge 6 commits into
agent/reactive-ui-script-propsfrom
agent/reactive-ui-templates

Conversation

@makspll

@makspll makspll commented Nov 13, 2026 •

Copy link
Copy Markdown
Owner

Draft: the API below is a first cut, see the open choices.

ui.list_template(items, function(item) .. end): a list whose function runs once for the whole list. item stands for the item of each row, the nodes and texts the function returns are a template, and every row is made from it in Rust: no script code runs per row.

ui.list_template(visible, function(item)            -- `visible` holds a list of entities
    local data = ui.watch_item(item, Item)          -- the Item component of each row's entity
    local name = ui.path(data, "data.name")
    return ui.node({
        name = ui.format("slot_{}", { name }),
        background_color = ui.when(ui.selected(is_selected, name), "#ffcc00", "#333333"),
        on_click = function(entity) select(entity) end,   -- one function for all rows, given the row's item
    }, {
        ui.text({ text = ui.format("x{}", { ui.path(data, "data.count") }) }),
    })
end)

How a row is made (bind_template):

  • values computed in Rust are bound to the row (Expr::bind): item becomes the item, path(watch_item(..)) reads a watch of the row's entity (owned by the row, removed with it), selected(selector, value) reads its key from the row once. A bound value which reads nothing is a plain property, spawned with the entity.
  • functions (handlers and bound properties) are curried with the item, so they are shared by all rows.
  • the result is an ordinary element, mounted, patched (on reload) and unmounted like any other.

Works with closures restricted to capturing constants (Rune), as nothing has to capture the item. Lua only tests and benches, as the rest of the crate.

Open choices:

  • rows are node and text only: components and regions inside a template are an error (they run script code per instance)
  • watch_item needs the items to be entities. Items which are plain values work with item itself (format("{}", { item }), selected(selector, item))
  • no branching on the item in script code, only through when
  • a separate list_template function, rather than a flag or a wrapper around list's function

Stacked on #724. Same binary, 3 rounds (new Template bench variant, the Paths ui with a templated list):

bench Closures Paths Template
inventory mount or unmount 200 1.78 ms 2.02 ms 0.90 ms
inventory search 1.49 ms 1.68 ms 0.79 ms
inventory add or remove one 125 µs 127 µs 173 µs
inventory select 22.8 µs 18.7 µs 24.4 µs

The two slower rows are the bench's own choices, not the template: its list holds 200 entity references instead of 200 strings (costlier to pass to Rust and back), and its click handler reads the item's name from the component.

@makspll
makspll added this pull request to stack #718 November 13, 2026 11:32
@semanticdiff-com

semanticdiff-com Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

ProjectBevy Scripting
Branchagent/reactive-ui-templates
Testbedlinux-gha

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

🐰 View full continuous benchmarking report in Bencher

…templates

# Conflicts:
#	assets/tests/reactive_ui/inventory/inventory.lua
#	crates/bevy_mod_scripting_reactive_ui/src/expr.rs
#	crates/bevy_mod_scripting_reactive_ui/src/lib.rs
#	crates/bevy_mod_scripting_reactive_ui/src/runtime.rs
…templates

# Conflicts:
#	crates/bevy_mod_scripting_reactive_ui/src/lib.rs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant