106 lines
4.6 KiB
Markdown
106 lines
4.6 KiB
Markdown
|
|
# PropertyLayout
|
|||
|
|
|
|||
|
|
**命名空间**: `XCEngine::Editor::UI`
|
|||
|
|
|
|||
|
|
**类型**: `header-helper + struct`
|
|||
|
|
|
|||
|
|
**源文件**: `editor/src/UI/PropertyLayout.h`
|
|||
|
|
|
|||
|
|
**描述**: 为 Inspector 属性行提供统一的标签列 / 控件列几何计算,并把具体控件绘制委托给回调。
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
`PropertyLayout.h` 是当前 Inspector 体验能否“像一个成熟编辑器”而不是“像一堆临时摆放的 ImGui 控件”的关键基础设施。
|
|||
|
|
|
|||
|
|
它不直接定义浮点框、复选框或向量输入框,而是先回答一个更底层的问题:
|
|||
|
|
|
|||
|
|
- 标签列从哪里开始?
|
|||
|
|
- 控件列从哪里开始?
|
|||
|
|
- 当前行有多高?
|
|||
|
|
- 控件应该拿到多宽的可用空间?
|
|||
|
|
|
|||
|
|
把这层几何计算独立出来以后,`ScalarControls`、`VectorControls`、`PropertyGrid` 这些上层 helper 就可以共享同一套 Inspector 排版规则,而不需要每种控件都重复手算列宽和光标位置。
|
|||
|
|
|
|||
|
|
## 公开 API
|
|||
|
|
|
|||
|
|
| 成员 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| `PropertyLayoutSpec` | 描述标签缩进、控件列起点、标签和控件间距、控件右侧留白。 |
|
|||
|
|
| `PropertyLayoutMetrics` | 保存一行属性被计算出的实际几何结果。 |
|
|||
|
|
| `MakePropertyLayout()` | 生成使用默认 token 的 `PropertyLayoutSpec`。 |
|
|||
|
|
| `PushPropertyLayoutStyles()` | 推入当前属性行需要的样式变量。 |
|
|||
|
|
| `GetPropertyControlWidth(...)` | 根据布局结果返回控件区域宽度。 |
|
|||
|
|
| `SetNextPropertyControlWidth(...)` | 用布局结果直接设置下一个控件的宽度。 |
|
|||
|
|
| `AlignPropertyControlToRight(...)` | 让较窄控件在属性列中右对齐。 |
|
|||
|
|
| `DrawPropertyRow(...)` | 绘制一整行属性,并把具体控件绘制委托给回调。 |
|
|||
|
|
|
|||
|
|
## 当前实现行为
|
|||
|
|
|
|||
|
|
按 `editor/src/UI/PropertyLayout.h` 的实现:
|
|||
|
|
|
|||
|
|
- `PropertyLayoutSpec` 的默认值直接来自 `StyleTokens.h`,例如 `InspectorPropertyControlColumnStart()` 与 `InspectorPropertyLabelInset()`。
|
|||
|
|
- `PushPropertyLayoutStyles()` 当前只推入 `ImGuiStyleVar_FramePadding`,并不负责整套 Inspector 样式。
|
|||
|
|
- `DrawPropertyRow(...)` 会:
|
|||
|
|
- 以 `label` 为 `PushID` 的依据。
|
|||
|
|
- 读取当前内容区宽度并计算一整行的 label / control 几何。
|
|||
|
|
- 直接用 `ImDrawList::AddText(...)` 绘制标签文本,而不是创建一个独立的 ImGui label item。
|
|||
|
|
- 把光标移动到控件列起点,再调用外部回调绘制真正的控件。
|
|||
|
|
- 最后用 `Dummy(...)` 吃掉这一行占用的高度,确保后续布局连续。
|
|||
|
|
|
|||
|
|
这说明它是一层“布局执行器”,不是单纯的常量集合。
|
|||
|
|
|
|||
|
|
## 设计说明
|
|||
|
|
|
|||
|
|
商业引擎编辑器通常会把 Inspector 的“字段布局规则”和“字段编辑控件”拆开,这一点和 Unity Inspector 的使用体验很像:
|
|||
|
|
|
|||
|
|
- 左边是稳定的属性标签列。
|
|||
|
|
- 右边是不同类型字段共享的一列编辑区域。
|
|||
|
|
- 上层组件编辑器只描述“这里是一条 Position / Rotation / Scale”,而不是自己计算每个字段的横向坐标。
|
|||
|
|
|
|||
|
|
这样拆分的收益非常直接:
|
|||
|
|
|
|||
|
|
- 所有组件编辑器天然保持一致的列对齐。
|
|||
|
|
- 以后如果想统一调整 Inspector 视觉密度,只需要修改 token 或布局层。
|
|||
|
|
- `PropertyGrid` 可以专注在“属性语义 + 撤销接线”,而不必再重复几何计算。
|
|||
|
|
|
|||
|
|
## 使用模式
|
|||
|
|
|
|||
|
|
更推荐的使用方式不是直接手算坐标,而是把控件包进 `DrawPropertyRow(...)` 的回调:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
const UI::PropertyLayoutSpec layout = UI::MakePropertyLayout();
|
|||
|
|
|
|||
|
|
UI::DrawPropertyRow("Mass", layout, [&](const UI::PropertyLayoutMetrics& metrics) {
|
|||
|
|
UI::SetNextPropertyControlWidth(metrics);
|
|||
|
|
return ImGui::DragFloat("##value", &mass, 0.1f, 0.0f, 1000.0f, "%.2f");
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果只是常见属性类型,实际工程里更常见的入口是:
|
|||
|
|
|
|||
|
|
- `ScalarControls.h`
|
|||
|
|
- `VectorControls.h`
|
|||
|
|
- `PropertyGrid.h`
|
|||
|
|
|
|||
|
|
它们已经把 `PropertyLayout` 作为底层依赖封装好了。
|
|||
|
|
|
|||
|
|
## 线程语义
|
|||
|
|
|
|||
|
|
- 这一层完全依赖当前帧的 ImGui 上下文和当前窗口布局状态,只能在 UI 线程、ImGui 绘制阶段调用。
|
|||
|
|
- 它不维护后台状态,也不适合离线计算布局。
|
|||
|
|
|
|||
|
|
## 当前限制
|
|||
|
|
|
|||
|
|
- `PushID(label)` 意味着同一作用域里若有重复标签,调用方应额外 `PushID` 以避免冲突。
|
|||
|
|
- 标签是直接画到 draw list 上的,因此不是可交互的独立 ImGui item。
|
|||
|
|
- 当前布局仍然是固定列起点模型,不是响应式表单系统。
|
|||
|
|
- `PushPropertyLayoutStyles()` 目前只做最小样式压栈,离完整的属性主题系统还有距离。
|
|||
|
|
|
|||
|
|
## 相关文档
|
|||
|
|
|
|||
|
|
- [UI](../UI.md)
|
|||
|
|
- [StyleTokens](../StyleTokens/StyleTokens.md)
|
|||
|
|
- [ScalarControls](../ScalarControls/ScalarControls.md)
|
|||
|
|
- [VectorControls](../VectorControls/VectorControls.md)
|
|||
|
|
- [PropertyGrid](../PropertyGrid/PropertyGrid.md)
|