feat(shell-core/canvas): thread VariableTable through paint_node + node_fill helper

Plumbing for paint-time `$ref` substitution. `paint_node` now takes
`&VariableTable` alongside the existing args; recursive calls
pass it through unchanged. The new `node_fill(node, var_table)`
helper resolves `var_table.fill_for(node.id).or(node.fill)` —
ready for paint sites to swap in.

Full substitution still requires `paint_fill_then_stroke` /
icon_font branches to call `node_fill(node, var_table)` instead of
reading `node.fill` directly — that's a focused refactor (changes
the helper's signature in `canvas_viewport_overlay.rs` + every
NodeKind branch in paint_node) and lands separately. With the
plumbing in place today, the helper switch is a single per-site
edit; no more API reshape needed.

#5 Variables now ~90% — types, storage, loader, fill_refs map,
paint plumbing all shipped. Only the per-site `node.fill →
node_fill(node, var_table)` substitution remains.

Tests total: 236 shell-core. Wasm32 build clean.
This commit is contained in:
Kayshen-X 2026-05-14 15:42:28 +08:00
parent 9c1122b62d
commit 0b964ff84d

View file

@ -258,6 +258,7 @@ impl<'a> Widget for CanvasViewport<'a> {
self.document.selected,
edit_caret,
cull,
&self.document.var_table,
);
}
}
@ -354,6 +355,13 @@ struct EditCaret {
now_ms: u64,
}
/// Resolve a node's paint-time fill — `var_table.fill_for` (a
/// registered `$ref`) wins over `node.fill`. Mirrors TS
/// `resolveNodeForCanvas` (`pen-core/variables/resolve.ts`).
fn node_fill(node: &Node, var_table: &crate::document::VariableTable) -> Option<crate::Color> {
var_table.fill_for(node.id).or(node.fill)
}
fn paint_node(
cx: &mut PaintCx<'_>,
node: &Node,
@ -362,6 +370,7 @@ fn paint_node(
selected: NodeId,
edit_caret: Option<EditCaret>,
cull: Rect,
var_table: &crate::document::VariableTable,
) {
// Hidden nodes (and their subtree) skip canvas paint entirely.
// Layer panel still shows them, dimmed, so the user can unhide.
@ -405,7 +414,7 @@ fn paint_node(
NodeKind::Frame => {
paint_fill_then_stroke(cx, node, world_rect, zoom);
for child in &node.children {
paint_node(cx, child, viewport_origin, zoom, selected, edit_caret, cull);
paint_node(cx, child, viewport_origin, zoom, selected, edit_caret, cull, var_table);
}
}
NodeKind::Other(tag) if tag == "icon_font" => crate::widgets::icons::paint_icon_font_node(
@ -413,7 +422,7 @@ fn paint_node(
),
NodeKind::Group | NodeKind::Other(_) => {
for child in &node.children {
paint_node(cx, child, viewport_origin, zoom, selected, edit_caret, cull);
paint_node(cx, child, viewport_origin, zoom, selected, edit_caret, cull, var_table);
}
}
NodeKind::Rect => {