Tree
概述
`Tree` 标签路由到 `rml_ui::TreeView`,作为 **MVVM/Stateful 默认渲染器**(默认文件夹/文件图标 + `on_activate`)。**不是** gpui-component 的替代包装——声明式 `<TreeNode>` 若将来落地,应由 engine codegen 直译 `Tree::new`。
基本用法
<!-- 在 CaseActivityPanel 组件内 -->
<Tree on_activate="on_case_activate" />
属性
| 属性 | 类型 | 绑定 | 说明 |
|------|------|------|------|
| — | — | — | Tree 无 RML 静态/绑定属性;数据通过 `TreeState` 在 Rust 侧设置 |
通用组件属性(`label`、`disabled` 等)对 Tree **无效**。
事件
| 事件 | 回调签名 | 说明 |
|------|----------|------|
| `on_activate` | `fn(&mut self, item_id: &SharedString, cx: &mut Context<Self>)` | 用户点击**叶子节点**(非文件夹)时触发 |
数据绑定
树数据不通过 RML 属性绑定,而是在 code-behind 中操作 `TreeState`。Demo 通过框架 `map_case_tree_items` 从 `MainWindow::ID` host 构建:
// case_activity_panel.rml.rs
fn refresh_tree(&mut self, cx: &mut Context<Self>) {
let items = map_case_tree_items(MainWindow::ID, cx);
// state.set_items(items, cx)
}
贡献注册:`#[contribute]` 宏 + `ctor` 自动 bootstrap;案例树条目 `kind = "case"` 由 `HostChromeMapper` 聚合,无需 demo 侧 `register_all`。
子节点 / 插槽
不支持子节点。树项完全由 `TreeState` 管理。
完整示例
`demo/src/shell/main_window.rml`:
<ActivityBar panels={activity_panels} on_panel_change="on_panel_change">
<div if={active_panel_id == "samples"} class="nav-tree">
<CaseActivityPanel />
</div>
</ActivityBar>
`CaseActivityPanel::on_case_activate` 经 demo 的 `case_activation::activate_case` 打开案例 Tab。
常见错误
1. **未在 `on_loaded` 初始化 TreeState** — `CaseActivityPanel` 在 `refresh_tree` 中懒创建;父窗口须 `cx.new` CaseActivityPanel Entity。
2. **字段名不是 `tree_state`** — codegen 在 `CaseActivityPanel` 内路由表硬编码该字段名。
3. **期望文件夹节点触发 `on_activate`** — 仅叶子节点(非 folder)触发回调。
4. **在 RML 中写 `items={...}`** — codegen 不支持,须在 Rust 中 `tree.set_items(...)`。
相关组件
- [activity-bar.md](./activity-bar.md) — 常见父容器
- [贡献点架构](../../09-architecture/contribution-system.md) — `demo.shell` host、`kind=case` 数据流
RML 未覆盖的 API
自定义项渲染、禁用节点、`TreeState` 高级 API 需在 Rust 中直接操作 `TreeState` / 扩展 `TreeView`。