feat(ui)!: move the widget tree into C++ #1

Merged
evan merged 1 commits from feat/cpp-node-tree into main 2026-08-02 23:19:30 +00:00
Owner

A node was a Lua table of ~625 bytes, of which 21 keys pushed it over a
power-of-two hash boundary and eight were style copies inheritance had
splattered down from its parent. A 400 node screen cost ~250 KB and could not
coexist with wifi's buffers.

The tree now lives in src/ui/layout.h as a 16 byte struct in a flat arena, and
splits by lifetime: Node holds what hit testing and repainting need forever,
Spec holds what only measure/place read and is dropped when layout ends. Style
is sparse and resolved by walking parents, so a node naming no colours costs
nothing. Re-layout rebuilds from Lua rather than retaining the inputs.

401 nodes:  8218 B steady, 21050 B peak
       Lua: ~250000 B steady

sdcard/lib/ui.lua stays the toolkit and keeps every constructor signature, but
returns integer handles: 627 lines to 374. Composition, the palette and custom
painters are still Lua on the SD card; only primitives now need a reflash.

BREAKING CHANGE: ui constructors return handles, not tables. Use
ui.setText(id, text) and keep per-node app data in a table keyed by id.

A node was a Lua table of ~625 bytes, of which 21 keys pushed it over a power-of-two hash boundary and eight were style copies inheritance had splattered down from its parent. A 400 node screen cost ~250 KB and could not coexist with wifi's buffers. The tree now lives in src/ui/layout.h as a 16 byte struct in a flat arena, and splits by lifetime: Node holds what hit testing and repainting need forever, Spec holds what only measure/place read and is dropped when layout ends. Style is sparse and resolved by walking parents, so a node naming no colours costs nothing. Re-layout rebuilds from Lua rather than retaining the inputs. 401 nodes: 8218 B steady, 21050 B peak Lua: ~250000 B steady sdcard/lib/ui.lua stays the toolkit and keeps every constructor signature, but returns integer handles: 627 lines to 374. Composition, the palette and custom painters are still Lua on the SD card; only primitives now need a reflash. BREAKING CHANGE: ui constructors return handles, not tables. Use ui.setText(id, text) and keep per-node app data in a table keyed by id.
evan added 1 commit 2026-08-02 23:13:20 +00:00
A node was a Lua table of ~625 bytes, of which 21 keys pushed it over a
power-of-two hash boundary and eight were style copies inheritance had
splattered down from its parent. A 400 node screen cost ~250 KB and could not
coexist with wifi's buffers.

The tree now lives in src/ui/layout.h as a 16 byte struct in a flat arena, and
splits by lifetime: Node holds what hit testing and repainting need forever,
Spec holds what only measure/place read and is dropped when layout ends. Style
is sparse and resolved by walking parents, so a node naming no colours costs
nothing. Re-layout rebuilds from Lua rather than retaining the inputs.

    401 nodes:  8218 B steady, 21050 B peak
           Lua: ~250000 B steady

sdcard/lib/ui.lua stays the toolkit and keeps every constructor signature, but
returns integer handles: 627 lines to 374. Composition, the palette and custom
painters are still Lua on the SD card; only primitives now need a reflash.

BREAKING CHANGE: ui constructors return handles, not tables. Use
ui.setText(id, text) and keep per-node app data in a table keyed by id.
evan merged commit 7feb00265a into main 2026-08-02 23:19:30 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: evan/slate32#1