跳转到内容
flamo
开始使用

语义 Scene Tree 与 Mounted Entity Tree

Flamo Runtime 同时维护两棵用途不同的树。Semantic Scene Tree 保存 Artifact 节点、instance-scoped 引用和 Scene API 创建节点,是组件脚本可以稳定查询和修改的语义边界。Mounted Entity Tree 表示当前 ECS 中实际绘制、命中、测量和变换的实体;它会随 Variant 切换、Slot 内容替换和 rematerialization 改变。

二者不能共用一个 children API。Tag 需要稳定的精确名称查询,Scroll 需要当前 viewport 的物理内容;如果 scene.findByName() 为了 Scroll 返回 mounted clone,Tag 的 instance-scoped 查询就会在无法描述的物理节点处中断。

需求 使用 不要使用
按 role、id、selector 或精确名称找节点 ctx.scene、ctx.role()、ctx.$()/ctx.$$() ECS children 或 clone id
修改/删除 Scene 节点并保持组件语义 RuntimeNode、ctx.scene.move/remove/apply() 物理 relation 写入
获取当前 Variant/Slot 的实际 entity ctx.mounted.resolve(ref) 从 id 字符串推导 source
获取 viewport 当前 direct children ctx.mounted.children(ref) scene.findByName() 代替物理遍历
读取当前物理几何和可见性 ctx.mounted.probe(ref) 或 snapshot 的 snapshot 设计态 bounds 猜测
同帧移动 content 与 scrollbar thumb ctx.setTransformsBatch() 多个互不协调的逐节点提交

ctx.scene.findByName() 精确、区分大小写,保留重复名称,并按确定性的语义树顺序返回。它不会遍历或返回 mounted ECS clone:

const closeIcons = ctx.scene.findByName('tag-close-icon', {
scope: ctx.nodeId,
});
for (const icon of closeIcons) icon.setCursor('pointer');

当前物理内容通过 ctx.mounted 显式读取:

const tree = ctx.mounted.children('viewport');
if (!tree || tree.children.length === 0) return;
const content = tree.children[0];
const geometry = content.snapshot;
ctx.setTransformsBatch([
{ ref: content.entity, x: nextContentX, y: nextContentY },
{ ref: 'horizontal-thumb', x: nextThumbX, y: thumbY },
]);

children() 只返回 direct physical children,不做递归名称搜索。返回的 entity 是 opaque handle;semanticRef/sourceNodeId/instanceRootId 是 Runtime 尽可能恢复的诊断身份,不能反过来把物理 id 当成持久模型。

mounted snapshot 带有 revision。Variant、Slot 或 scene mutation 改变 revision 后,应重新 resolve;旧 handle 可能已经退出当前 materialization。适合 Scroll 的热路径是:setup 或 revision 变化时读取 children,pointerdown 时冻结本次手势需要的 handle/几何,pointermove 中只更新纯状态并每帧提交一批 transform,pointerup/cancel/lost capture 时统一收口。

ctx.mountedContent() 是 ctx.mounted.children() 的兼容投影,保留 viewport/contents/primary 结构;新脚本使用 ctx.mounted。无论使用哪一个入口,都不得把物理 entity id 写入 Artifact、Behavior Contract 或项目配置。

事件路由使用当前 canonical mounted target,但脚本仍通过 role、精确名称或 semantic ref 声明意图。event.target 和 composedPath() 只属于当前事件事务,不是可持久化身份。

自动隐藏 scrollbar gutter 如果 opacity 为 0 仍需接收 hover/drag,可以显式声明:

gutter.setInteractive({
mode: 'enabled',
pointerEvents: 'box-only',
hitTestWhenTransparent: true,
});

这只让 paint opacity 为 0 的可见节点继续参与 hit-test;visible: false、inert、mode: 'disabled' 和 pointerEvents: 'none' 仍是更强的禁用规则。

完整事件与动态代理用法见事件、节点查询与动态代理;API 签名见 Runtime API。