chore: sync workspace state
This commit is contained in:
107
docs/api/XCEngine/Editor/UI/BuiltInIcons/BuiltInIcons.md
Normal file
107
docs/api/XCEngine/Editor/UI/BuiltInIcons/BuiltInIcons.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# BuiltInIcons
|
||||
|
||||
**命名空间**: `XCEngine::Editor::UI`
|
||||
|
||||
**类型**: `header-helper + enum class`
|
||||
|
||||
**源文件**: `editor/src/UI/BuiltInIcons.h`
|
||||
|
||||
**描述**: 为编辑器的资源树、资源网格和层级树提供内置图标枚举、初始化入口与统一绘制函数。
|
||||
|
||||
## 概述
|
||||
|
||||
`BuiltInIcons.h` 解决的不是“如何显示一张纹理”这个底层问题,而是“编辑器里哪些地方应该用统一图标语言表达文件夹、文件和场景对象”这个产品层问题。
|
||||
|
||||
当前源码里,这一层主要服务于三个界面:
|
||||
|
||||
- `HierarchyPanel` 通过 `GameObject` 图标给场景树节点补前缀。
|
||||
- `ProjectPanel` 通过 `Folder` / `File` 图标绘制左侧目录树和右侧资源卡片。
|
||||
- `ProjectActionRouter` 在拖拽预览等交互里复用同一套图标语义。
|
||||
|
||||
这和商业级编辑器常见的做法一致:业务面板不直接持有纹理资源,不自己决定某一类对象该显示什么图标,而是把“对象类别 -> 图标表现”的决定集中到一层共享 helper 中。这样做的好处是统一、可替换,而且不会让每个面板都自己重复写上传纹理、绘制 fallback 图形、适配尺寸的逻辑。
|
||||
|
||||
## 公开 API
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| `enum class AssetIconKind { Folder, File, GameObject }` | 定义当前编辑器可识别的内置图标类别。 |
|
||||
| `InitializeBuiltInIcons(ImGuiBackendBridge&, ID3D12Device*, ID3D12CommandQueue*)` | 初始化图标系统,上传贴图并分配 ImGui 可见的 SRV 描述符。 |
|
||||
| `ShutdownBuiltInIcons()` | 释放内置图标占用的 GPU 资源与描述符。 |
|
||||
| `DrawAssetIcon(ImDrawList*, const ImVec2& min, const ImVec2& max, AssetIconKind)` | 在给定矩形内绘制指定类别的图标。 |
|
||||
|
||||
## 生命周期
|
||||
|
||||
- 这一层本质上是一个进程级的全局状态,当前实现通过匿名命名空间里的 `g_icons` 保存后端指针和已上传纹理。
|
||||
- 正确顺序是先初始化 `ImGuiBackendBridge`,再调用 `InitializeBuiltInIcons(...)`。
|
||||
- 关闭时应先停止使用这些图标,再调用 `ShutdownBuiltInIcons()`,最后销毁 ImGui 后端。
|
||||
- 当前 `Application::InitializeImGui()` 和 `Application::Shutdown()` 已经按照这个顺序接线。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `editor/src/UI/BuiltInIcons.cpp` 的实现,当前版本有几个值得明确写进文档的事实:
|
||||
|
||||
- 只有 `Folder` 和 `GameObject` 两类图标会尝试从磁盘加载 PNG。
|
||||
- 图标路径不是从项目目录查找,而是从可执行文件目录推导到 `../../resources/Icons/folder_icon.png` 与 `../../resources/Icons/gameobject_icon.png`。
|
||||
- 贴图上传路径目前只支持 D3D12,上传时会创建默认堆纹理、上传堆 buffer、一次性命令列表,并在结束后主动等待命令队列空闲。
|
||||
- `File` 图标当前不是贴图资源,而是运行时用 `ImDrawList` 直接画出的简化文件形状。
|
||||
- 如果 `Folder` / `GameObject` 的 PNG 加载失败,系统不会抛出错误或写日志;它会静默退回到程序化绘制的 fallback 图标。
|
||||
|
||||
这意味着它更接近“编辑器内部视觉资源辅助层”,而不是一个对外稳定暴露的通用图标资源系统。
|
||||
|
||||
## 设计说明
|
||||
|
||||
如果把它和 Unity 的使用体验类比,可以把这层理解成“轻量版的内建编辑器图标入口”。它的目标不是提供一个完整的图标数据库,而是确保最核心的编辑器对象类型在不同面板里看起来一致。
|
||||
|
||||
这样设计的收益是:
|
||||
|
||||
- 面板代码只关心“我要画文件夹图标”,而不关心纹理从哪里来。
|
||||
- 资源加载失败时仍然有 fallback,不会导致整个面板失去可用性。
|
||||
- 以后如果要统一替换图标风格,只需要改这一层,而不是同时改 `HierarchyPanel`、`ProjectPanel` 和拖拽预览逻辑。
|
||||
|
||||
代价也很明确:
|
||||
|
||||
- 当前实现和 D3D12 / ImGui 后端强绑定,还不是渲染后端无关的接口。
|
||||
- 图标集合很小,只覆盖当前编辑器刚需。
|
||||
- 路径解析和加载失败处理还比较工程化,不是面向内容制作流程的资源系统。
|
||||
|
||||
## 线程语义
|
||||
|
||||
- 这套 API 应被视为仅限编辑器 UI / 渲染线程使用。
|
||||
- 初始化和销毁会触发 GPU 资源分配、命令提交与队列等待,不适合在任意后台线程上调用。
|
||||
- `DrawAssetIcon(...)` 依赖当前帧的 `ImDrawList` 和 ImGui 绘制上下文,也只能在 ImGui 绘制阶段调用。
|
||||
|
||||
## 典型用法
|
||||
|
||||
```cpp
|
||||
// 初始化阶段
|
||||
m_imguiBackend.Initialize(...);
|
||||
UI::InitializeBuiltInIcons(
|
||||
m_imguiBackend,
|
||||
m_windowRenderer.GetDevice(),
|
||||
m_windowRenderer.GetCommandQueue());
|
||||
|
||||
// 面板绘制阶段
|
||||
UI::DrawAssetIcon(
|
||||
ImGui::GetWindowDrawList(),
|
||||
iconMin,
|
||||
iconMax,
|
||||
UI::AssetIconKind::Folder);
|
||||
|
||||
// 关闭阶段
|
||||
UI::ShutdownBuiltInIcons();
|
||||
```
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 当前只支持 D3D12 上传路径,没有 OpenGL / Vulkan / 软件后备实现。
|
||||
- 图标种类固定为 `Folder`、`File`、`GameObject` 三类。
|
||||
- 没有热重载、主题切换或 DPI 变体资源管理。
|
||||
- 加载失败时以静默 fallback 为主,没有诊断级错误反馈。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [UI](../UI.md)
|
||||
- [StyleTokens](../StyleTokens/StyleTokens.md)
|
||||
- [Application](../../Application/Application.md)
|
||||
- [HierarchyPanel](../../panels/HierarchyPanel/HierarchyPanel.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
73
docs/api/XCEngine/Editor/UI/DividerChrome/DividerChrome.md
Normal file
73
docs/api/XCEngine/Editor/UI/DividerChrome/DividerChrome.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# DividerChrome
|
||||
|
||||
**命名空间**: `XCEngine::Editor::UI`
|
||||
|
||||
**类型**: `header-helper`
|
||||
|
||||
**源文件**: `editor/src/UI/DividerChrome.h`
|
||||
|
||||
**描述**: 提供编辑器面板分隔线的轻量绘制函数,用统一的 `StyleTokens` 控制颜色与线宽。
|
||||
|
||||
## 概述
|
||||
|
||||
`DividerChrome.h` 很小,但它承担的是“视觉一致性”而不是“复杂交互”。它把横向分隔线、纵向分隔线和当前窗口底边线抽成统一 helper,避免各个面板自己散落写 `AddLine(...)` 与硬编码颜色。
|
||||
|
||||
在商业编辑器里,这类 helper 很常见,因为它们能把“只是画一条线”这件小事也纳入统一设计系统:线宽、颜色、出现位置都不再是面板作者自己临时决定,而是由共享 token 决定。
|
||||
|
||||
## 公开 API
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| `DrawHorizontalDivider(...)` | 在任意 `ImDrawList` 上绘制一条横向分隔线。 |
|
||||
| `DrawVerticalDivider(...)` | 在任意 `ImDrawList` 上绘制一条纵向分隔线。 |
|
||||
| `DrawCurrentWindowBottomDivider(...)` | 读取当前 ImGui 窗口的矩形并在底边绘制横线。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `editor/src/UI/DividerChrome.h` 的实现:
|
||||
|
||||
- 默认颜色来自 `PanelDividerColor()`。
|
||||
- 默认厚度来自 `PanelDividerThickness()`。
|
||||
- 如果传入的 `drawList` 为空,或绘制范围是退化区间,函数会直接返回。
|
||||
- `DrawCurrentWindowBottomDivider(...)` 不会创建额外布局空间;它只是读取当前窗口的 `Pos` 与 `Size` 后直接在底边绘线。
|
||||
|
||||
这意味着它是纯绘制 helper,不管理布局、不创建交互区域,也不记录任何状态。
|
||||
|
||||
## 设计说明
|
||||
|
||||
把 divider 单独抽出来有两个直接好处:
|
||||
|
||||
- `ProjectPanel`、`PanelChrome`、自定义标签栏都可以复用同一套边界视觉语言。
|
||||
- 当设计风格变化时,只要改 token,而不是到处找 `AddLine(...)` 的裸调用。
|
||||
|
||||
可以把它理解成商业引擎编辑器里“panel chrome”的最小组成部分。它不像 splitter 那样负责拖拽,也不像 toolbar scope 那样负责结构;它只负责把层次边界画正确。
|
||||
|
||||
## 使用建议
|
||||
|
||||
- 如果你已经有明确的屏幕坐标,就直接调用 `DrawHorizontalDivider(...)` 或 `DrawVerticalDivider(...)`。
|
||||
- 如果你只是想给当前 child/window 补一条底边线,优先用 `DrawCurrentWindowBottomDivider(...)`。
|
||||
- 这类 helper 适合放在面板收尾阶段调用,而不是提前调用后再继续写会改变窗口高度的控件。
|
||||
|
||||
## 典型用法
|
||||
|
||||
```cpp
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const ImVec2 min = ImGui::GetWindowPos();
|
||||
const ImVec2 max(min.x + ImGui::GetWindowSize().x, min.y + ImGui::GetWindowSize().y);
|
||||
|
||||
UI::DrawHorizontalDivider(drawList, min.x, max.x, max.y - 0.5f);
|
||||
```
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 不负责布局留白,调用者必须自己决定线的位置。
|
||||
- 没有 hover / active 等交互状态,它不是 splitter。
|
||||
- 只是一层对 `ImDrawList::AddLine(...)` 的统一封装,不提供更复杂的边框系统。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [UI](../UI.md)
|
||||
- [StyleTokens](../StyleTokens/StyleTokens.md)
|
||||
- [Core](../Core/Core.md)
|
||||
- [PanelChrome](../PanelChrome/PanelChrome.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
103
docs/api/XCEngine/Editor/UI/DockTabBarChrome/DockTabBarChrome.md
Normal file
103
docs/api/XCEngine/Editor/UI/DockTabBarChrome/DockTabBarChrome.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# DockTabBarChrome
|
||||
|
||||
**命名空间**: `XCEngine::Editor::UI`
|
||||
|
||||
**类型**: `header-helper`
|
||||
|
||||
**源文件**: `editor/src/UI/DockTabBarChrome.h`
|
||||
|
||||
**描述**: 基于 ImGui Docking 内部节点实现自定义停靠标签栏外观、选中逻辑、拖拽排序与拖出浮动行为。
|
||||
|
||||
## 概述
|
||||
|
||||
`DockTabBarChrome.h` 是当前 Editor UI 里技术耦合度最高的一层 helper 之一。它的职责不是创建 dockspace,而是在 ImGui 已经提供 docking 能力的前提下,把默认标签栏替换成一套更像商业编辑器的自定义标签栏。
|
||||
|
||||
当前这套方案由两部分组成:
|
||||
|
||||
- `ConfigureDockTabBarChrome(...)` 递归配置 dock node,关闭原生 tab bar,并确保叶子节点处于可绘制的稳定状态。
|
||||
- `DrawDockedWindowTabStrip()` 在具体面板窗口内部补画自定义标签条。
|
||||
|
||||
从调用关系上看:
|
||||
|
||||
- `DockLayoutController::RenderDockspace()` 负责每帧配置 dockspace 节点。
|
||||
- `PanelChrome::PanelWindowScope` 在 `ImGui::Begin(...)` 成功后调用 `DrawDockedWindowTabStrip()`。
|
||||
|
||||
也就是说,这一层本质上是“布局层 + 面板 chrome 层”的协作产物,而不是某一个面板自己的私有实现。
|
||||
|
||||
## 建议视为公开入口的函数
|
||||
|
||||
虽然头文件里存在很多 inline helper,但从设计意图上,真正应该被其他模块直接依赖的入口主要只有下面三个:
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| `ConfigureDockTabBarChrome(ImGuiDockNode* node)` | 对指定 dock node 递归应用自定义 tab bar 策略。 |
|
||||
| `ConfigureDockTabBarChrome(ImGuiID dockspaceId)` | 通过 dockspace id 找到 root node 并应用同样策略。 |
|
||||
| `DrawDockedWindowTabStrip()` | 在当前面板窗口顶部绘制自定义标签条。 |
|
||||
|
||||
其他诸如 `DockTabOrderCache()`、`ReorderDockTab(...)`、`BeginCustomDockTabUndock(...)` 之类的函数,虽然在头文件可见,但更适合作为当前实现细节理解,而不是稳定的上层调用契约。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `editor/src/UI/DockTabBarChrome.h` 的实现,当前版本具备以下行为:
|
||||
|
||||
- 对叶子 `ImGuiDockNode` 会强制设置 `ImGuiDockNodeFlags_NoTabBar`、`NoWindowMenuButton`、`NoCloseButton`,隐藏 ImGui 原生标签栏。
|
||||
- 通过静态 `std::unordered_map<ImGuiID, std::vector<ImGuiID>>` 维护每个 dock node 的 tab 顺序缓存。
|
||||
- 如果当前叶子节点没有显式选中页签,系统会把第一个窗口设为选中页签。
|
||||
- 自定义标签条支持:
|
||||
- 单击切换选中页签。
|
||||
- 左键拖拽改变页签顺序。
|
||||
- 满足阈值时将页签从当前 dock 节点拖出,进入 ImGui 原生的 undock 流程。
|
||||
- 标签颜色、背景色与分隔线使用 `StyleTokens` 和 ImGui dock style 的组合结果。
|
||||
- 标签宽度当前按文本宽度加固定水平 padding 计算,没有做更复杂的压缩或滚动策略。
|
||||
|
||||
这套实现明显不是简单换皮,而是接管了 tab strip 的一部分交互语义。
|
||||
|
||||
## 设计说明
|
||||
|
||||
为什么要做这一层,而不是直接接受 ImGui 默认标签栏?
|
||||
|
||||
- 商业编辑器通常会把工作区标签视觉做得更克制、更稳定,以便和工具栏、面板边框形成统一外观。
|
||||
- 一旦需要控制页签排序、激活行为、拖出阈值、底部分隔线这些细节,直接用默认样式往往不够。
|
||||
- 把这部分逻辑集中在 `DockTabBarChrome.h` 中,能避免 `Hierarchy`、`Scene`、`Console` 这类面板各自处理 dock 标签外观。
|
||||
|
||||
如果和 Unity 的使用体验类比,这层更像“编辑器工作区标签栏的定制实现”,而不是某个单独窗口组件。
|
||||
|
||||
## 前置条件与线程语义
|
||||
|
||||
- 只能在启用了 ImGui Docking 的编辑器 UI 帧里使用。
|
||||
- `ConfigureDockTabBarChrome(...)` 应在 dockspace 建立后、面板开始绘制前调用;当前由 `DockLayoutController` 统一负责。
|
||||
- `DrawDockedWindowTabStrip()` 需要当前窗口已经 `Begin(...)` 成功且窗口处于 docked 状态;当前由 `PanelWindowScope` 自动调用。
|
||||
- 这一层依赖 `imgui_internal.h` 暴露的内部结构,应视为 UI 主线程专用逻辑,不具备线程安全保证。
|
||||
|
||||
## 典型用法
|
||||
|
||||
```cpp
|
||||
const ImGuiID dockspaceId = ImGui::GetID("MainDockspace.Root");
|
||||
UI::ConfigureDockTabBarChrome(dockspaceId);
|
||||
|
||||
{
|
||||
UI::DockHostStyleScope dockHostStyle;
|
||||
ImGui::DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), dockFlags);
|
||||
}
|
||||
```
|
||||
|
||||
面板窗口不需要自己手写标签条调用,使用 `PanelWindowScope` 时会自动进入:
|
||||
|
||||
```cpp
|
||||
UI::PanelWindowScope panel("Hierarchy");
|
||||
```
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 强依赖 `imgui_internal.h`、`ImGuiWindow`、`ImGuiDockNode` 等内部实现细节,升级 ImGui 时需要重点回归。
|
||||
- 当前没有 close button、window menu button,也没有更复杂的 tab overflow 处理。
|
||||
- tab 顺序缓存是进程内静态状态,不是跨会话布局资产。
|
||||
- 这是 Editor 专用 UI 基建,不适合直接当作通用运行时 UI API 使用。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [UI](../UI.md)
|
||||
- [StyleTokens](../StyleTokens/StyleTokens.md)
|
||||
- [PanelChrome](../PanelChrome/PanelChrome.md)
|
||||
- [DockHostStyle](../DockHostStyle/DockHostStyle.md)
|
||||
- [DockLayoutController](../../Layout/DockLayoutController/DockLayoutController.md)
|
||||
105
docs/api/XCEngine/Editor/UI/PropertyLayout/PropertyLayout.md
Normal file
105
docs/api/XCEngine/Editor/UI/PropertyLayout/PropertyLayout.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# 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)
|
||||
88
docs/api/XCEngine/Editor/UI/SplitterChrome/SplitterChrome.md
Normal file
88
docs/api/XCEngine/Editor/UI/SplitterChrome/SplitterChrome.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# SplitterChrome
|
||||
|
||||
**命名空间**: `XCEngine::Editor::UI`
|
||||
|
||||
**类型**: `header-helper + enum class + struct`
|
||||
|
||||
**源文件**: `editor/src/UI/SplitterChrome.h`
|
||||
|
||||
**描述**: 为编辑器面板提供统一的 splitter 主题颜色与拖拽分隔条绘制 / 交互结果。
|
||||
|
||||
## 概述
|
||||
|
||||
`SplitterChrome.h` 处理的是“两个区域之间如何被拖拽改变尺寸”这个问题。和 `DividerChrome` 不同,它不只是画一条视觉边界线,而是同时承担:
|
||||
|
||||
- 交互命中区域创建。
|
||||
- hover / active 状态计算。
|
||||
- 鼠标光标切换。
|
||||
- 拖拽位移返回。
|
||||
|
||||
当前源码中,最直接的使用者是 `ProjectPanel`:左侧目录树和右侧资源浏览区之间的宽度调节完全依赖这层 helper。
|
||||
|
||||
## 公开 API
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| `enum class SplitterAxis { Vertical, Horizontal }` | 定义 splitter 的主轴方向。 |
|
||||
| `struct SplitterResult` | 返回当前 splitter 的 hover、active 与拖拽增量。 |
|
||||
| `ApplySplitterThemeColors(ImVec4* colors)` | 用当前 token 覆盖 ImGui separator / resize grip 相关颜色。 |
|
||||
| `DrawSplitter(...)` | 绘制 splitter 并返回本帧交互结果。 |
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `editor/src/UI/SplitterChrome.h` 的实现:
|
||||
|
||||
- `DrawSplitter(...)` 使用 `ImGui::InvisibleButton(...)` 创建命中区域。
|
||||
- 命中区域厚度默认来自 `PanelSplitterHitThickness()`,而真正画出来的线宽来自 `PanelSplitterVisibleThickness()`。
|
||||
- 当 splitter 处于 active 状态时:
|
||||
- 纵向 splitter 返回 `MouseDelta.x`
|
||||
- 横向 splitter 返回 `MouseDelta.y`
|
||||
- hover 或 active 时会把鼠标指针切到 `ResizeEW` 或 `ResizeNS`。
|
||||
- 它只返回“这一帧用户拖了多少”,并不直接修改任何布局变量;调用者必须自己把 `delta` 累加到宽度 / 高度并做 clamp。
|
||||
|
||||
因此,这层是“交互采样 + 视觉呈现”,不是完整的布局管理器。
|
||||
|
||||
## 设计说明
|
||||
|
||||
把 splitter 从具体面板里抽出来是非常典型的商业编辑器实践:
|
||||
|
||||
- 面板只关心自己的布局状态,例如 `m_navigationWidth`。
|
||||
- splitter helper 只关心拖拽命中、光标和线条绘制。
|
||||
- 主题层通过 `StyleTokens` 统一控制 splitter 粗细、颜色和命中区宽度。
|
||||
|
||||
这样 `ProjectPanel`、后续的多列 Inspector 或资源导入面板都能使用相同的分隔条行为,而不会出现每个面板“拖动手感都不一样”的问题。
|
||||
|
||||
## 典型用法
|
||||
|
||||
```cpp
|
||||
const UI::SplitterResult splitter =
|
||||
UI::DrawSplitter("##ProjectPaneSplitter", UI::SplitterAxis::Vertical, totalHeight);
|
||||
|
||||
if (splitter.active) {
|
||||
m_navigationWidth += splitter.delta;
|
||||
}
|
||||
```
|
||||
|
||||
调用者通常还应该把结果夹紧到合理区间,例如:
|
||||
|
||||
- 最小导航宽度
|
||||
- 最小内容区宽度
|
||||
|
||||
## 线程语义
|
||||
|
||||
- 只能在 ImGui UI 线程使用。
|
||||
- 每帧结果是即时的,不会自动跨帧缓存。
|
||||
- `ApplySplitterThemeColors(...)` 修改的是调用方传入的颜色数组,通常应在同一帧、同一上下文中使用。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 当前只提供基础 hover / active 与位移返回,不提供吸附、双击重置、动画过渡等高级行为。
|
||||
- 颜色仍然是静态 token,不支持运行时主题切换动画。
|
||||
- 由调用者自己负责宽度约束和布局重算,没有更高层的布局容器抽象。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [UI](../UI.md)
|
||||
- [StyleTokens](../StyleTokens/StyleTokens.md)
|
||||
- [DividerChrome](../DividerChrome/DividerChrome.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
@@ -2,45 +2,90 @@
|
||||
|
||||
**命名空间**: `XCEngine::Editor::UI`
|
||||
|
||||
**类型**: `header-helper`
|
||||
**类型**: `header-helper + struct`
|
||||
|
||||
**源文件**: `editor/src/UI/StyleTokens.h`
|
||||
|
||||
**描述**: 统一定义编辑器 UI 使用的颜色、尺寸、间距和布局 token。
|
||||
**描述**: 集中定义 Editor UI 的颜色、尺寸、间距与树视图样式,是当前编辑器设计系统的单一 token 源。
|
||||
|
||||
## 概述
|
||||
|
||||
`StyleTokens.h` 是当前 UI 层的设计 token 中心。
|
||||
它集中提供了大量 inline token helper,例如:
|
||||
`StyleTokens.h` 是当前 Editor UI 的设计语言中心。它把原本最容易散落在各个面板、各类 helper 和临时控件中的“魔法数字”集中成一组具名 token,让代码能够表达设计意图,而不是只暴露数值本身。
|
||||
|
||||
- Dock 标签颜色
|
||||
- 工具栏高度与 padding
|
||||
- 资产网格尺寸
|
||||
- Inspector 标签列宽
|
||||
- 弹窗按钮尺寸
|
||||
- 向量控件按钮颜色
|
||||
- Console 状态颜色
|
||||
例如:
|
||||
|
||||
- `ProjectNavigationDefaultWidth()` 表达的是“项目导航栏默认宽度”。
|
||||
- `InspectorPropertyControlColumnStart()` 表达的是“Inspector 控件列起点”。
|
||||
- `PanelSplitterHitThickness()` 表达的是“splitter 命中区宽度”。
|
||||
|
||||
这比直接写 `248.0f`、`236.0f`、`4.0f` 更接近商业编辑器的工程实践,因为当 UI 逐步复杂起来后,真正难维护的从来不是控件本身,而是这些散落的视觉约束。
|
||||
|
||||
## 当前 token 分组
|
||||
|
||||
按 `editor/src/UI/StyleTokens.h` 的实现,当前 token 大致可以分成下面几类:
|
||||
|
||||
| 类别 | 代表 token | 主要消费者 |
|
||||
|------|-------------|------------|
|
||||
| Dock host / tab | `DockHostFramePadding()`、`DockTabSelectedColor()` | `DockHostStyle`、`DockTabBarChrome` |
|
||||
| 面板与工具栏 | `PanelWindowPadding()`、`ToolbarPadding()` | `PanelChrome`、各类 panel toolbar |
|
||||
| Project 浏览器 | `ProjectNavigationDefaultWidth()`、`AssetGridSpacing()` | `ProjectPanel`、`Widgets` |
|
||||
| TreeView 导航树 | `NavigationTreeIconSize()`、`NavigationTreePrefixWidth()` | `TreeView`、`HierarchyPanel`、`ProjectPanel` |
|
||||
| Inspector / 属性布局 | `InspectorPropertyControlColumnStart()`、`ControlFramePadding()` | `PropertyLayout`、`ScalarControls`、`VectorControls`、`PropertyGrid` |
|
||||
| Popup / Combo | `PopupWindowPadding()`、`ComboPopupBackgroundColor()` | `Core`、`Widgets` |
|
||||
| 资产卡片与图标 | `AssetTileSize()`、`BuiltInFolderIconBodyColor()` | `Widgets`、`BuiltInIcons` |
|
||||
| Console / 状态栏 | `ConsoleRowHoverFillColor()`、`MenuBarStatusIdleColor()` | `Widgets`、菜单栏与 Console UI |
|
||||
|
||||
此外,`StyleTokens.h` 里还定义了:
|
||||
|
||||
- `struct TreeViewStyle`
|
||||
- `NavigationTreeStyle()`
|
||||
- `HierarchyTreeStyle()`
|
||||
- `ProjectFolderTreeStyle()`
|
||||
|
||||
它们是树视图层的“组合 token”,不是单个数值,而是一组可复用的树节点视觉约束。
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按当前源码,这一层有几个很重要的特点:
|
||||
|
||||
- token 目前全部是 header-only inline 函数,不是配置资产,也不是运行时可热更新的数据表。
|
||||
- 很多 helper 会把这里的 token 当默认值,而不是自己再声明一套局部常量。
|
||||
- `PropertyLayout`、`SplitterChrome`、`DockTabBarChrome`、`BuiltInIcons`、`Widgets` 都直接依赖这里的 token。
|
||||
- 某些 token 之间存在设计上的复用关系,例如 `PanelDividerColor()` 当前直接复用 splitter 的 idle 颜色。
|
||||
|
||||
这意味着 `StyleTokens.h` 不是“装饰性文件”,而是整个 Editor UI 复用层的上游依赖。
|
||||
|
||||
## 设计说明
|
||||
|
||||
这是商业编辑器 UI 非常推荐的做法。
|
||||
不要在控件代码里到处写 `6.0f`、`0.24f`、`104.0f` 这种魔法数字,而应先抽象成 token。
|
||||
为什么这层很重要?
|
||||
|
||||
收益包括:
|
||||
- 当你在重构 `ProjectPanel` 时,不应该顺手发明一套新的资产卡片尺寸。
|
||||
- 当你在做 `HierarchyPanel` 的树前缀图标时,不应该自己再决定一套图标槽宽。
|
||||
- 当你在优化 Inspector 的密度时,真正应该改的是 token,而不是 5 个不同 helper 里的重复常量。
|
||||
|
||||
- 调整风格时能集中修改
|
||||
- 命名本身就表达设计意图
|
||||
- 高层 widget 与具体数值解耦
|
||||
这就是商业级编辑器常说的“设计系统落地到代码层”的意义。对于 XCEngine 来说,这一层还没有演化成完整的主题资产系统,但它已经承担了相同的架构角色。
|
||||
|
||||
## 与其他 UI helper 的关系
|
||||
|
||||
- `PropertyLayout` 使用它定义属性行的列宽、label inset 和 frame padding。
|
||||
- `TreeView` 使用它定义缩进、前缀插槽宽度和图标尺寸。
|
||||
- `SplitterChrome` 与 `DividerChrome` 使用它定义边界线和分隔条视觉。
|
||||
- `Widgets` 使用它定义资产卡片、面包屑、组件 section 和各种通用控件的视觉细节。
|
||||
- `BuiltInIcons` 使用它定义文件 / 文件夹 fallback 图标颜色。
|
||||
|
||||
从依赖方向上说,这一层应该尽量保持在 UI 栈的较底部,避免反向依赖更高层的业务 widget。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 仍然是 header inline 常量函数,而不是数据驱动主题系统
|
||||
- token 数量已经较多,后续可能需要再分层
|
||||
- 当前主要围绕唯一默认主题设计
|
||||
- 当前仍然是“写死在代码里的默认主题”,不是运行时可切换的主题资源系统。
|
||||
- token 命名已经带有明显的面板语义,例如 `ProjectNavigationPanePadding()`,说明它还是偏应用内设计系统,而不是完全抽象的通用 design token。
|
||||
- 还没有颜色模式切换、用户主题文件或样式编辑器一类更高阶能力。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [UI](../UI.md)
|
||||
- [BaseTheme](../BaseTheme/BaseTheme.md)
|
||||
- [PanelChrome](../PanelChrome/PanelChrome.md)
|
||||
- [Widgets](../Widgets/Widgets.md)
|
||||
- [DockHostStyle](../DockHostStyle/DockHostStyle.md)
|
||||
- [PropertyLayout](../PropertyLayout/PropertyLayout.md)
|
||||
- [TreeView](../TreeView/TreeView.md)
|
||||
- [SplitterChrome](../SplitterChrome/SplitterChrome.md)
|
||||
|
||||
122
docs/api/XCEngine/Editor/UI/TreeView/TreeView.md
Normal file
122
docs/api/XCEngine/Editor/UI/TreeView/TreeView.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# TreeView
|
||||
|
||||
**命名空间**: `XCEngine::Editor::UI`
|
||||
|
||||
**类型**: `header-helper + class + struct`
|
||||
|
||||
**源文件**: `editor/src/UI/TreeView.h`
|
||||
|
||||
**描述**: 提供编辑器树形视图的统一节点状态、前缀绘制槽、交互回调和展开状态持久化机制。
|
||||
|
||||
## 概述
|
||||
|
||||
`TreeView.h` 是当前 Editor UI 重构里最重要的公共基础设施之一。它把原本很容易散落在 `HierarchyPanel`、`ProjectPanel` 中的树节点绘制逻辑抽成一层统一接口,让不同面板能够共享:
|
||||
|
||||
- 相同的树节点视觉风格。
|
||||
- 相同的展开 / 收起行为。
|
||||
- 相同的前缀图标插槽模型。
|
||||
- 相同的点击、双击、右键、拖拽扩展接入点。
|
||||
|
||||
如果从使用体验上理解,它很接近商业编辑器中“导航树控件”的内建基础层。`HierarchyPanel` 和 `ProjectPanel` 的左侧目录树并没有各写一套独立的 tree widget,而是都基于这层构建。
|
||||
|
||||
## 公开类型与函数
|
||||
|
||||
| 成员 | 说明 |
|
||||
|------|------|
|
||||
| `class TreeViewState` | 保存节点展开状态的内存态容器。 |
|
||||
| `TreeNodeOptions` | 控制选中、叶子节点、默认展开、点击展开策略等基础选项。 |
|
||||
| `TreeNodeResult` | 返回节点本帧是否打开、是否点击、是否双击、是否右键等交互结果。 |
|
||||
| `TreeNodePrefixContext` | 给前缀绘制回调提供绘制区域、选中状态和 hover 状态。 |
|
||||
| `TreeNodePrefixSlot` | 定义一个可选的前缀绘制槽。 |
|
||||
| `TreeNodeCallbacks` | 定义交互回调和额外渲染回调。 |
|
||||
| `TreeNodeDefinition` | 组合节点选项、持久化 key、样式、前缀槽与回调。 |
|
||||
| `ResetTreeLayout()` | 清空当前树布局的缩进栈,开始一棵新的树。 |
|
||||
| `DrawTreeNode(...)` | 绘制节点并返回本帧结果。 |
|
||||
| `EndTreeNode()` | 与打开状态的节点配对,弹出一层树缩进。 |
|
||||
|
||||
## TreeViewState 的角色
|
||||
|
||||
`TreeViewState` 当前只做一件事:根据 `std::string_view key` 保存节点展开状态。
|
||||
|
||||
它不是场景数据,也不是项目数据,只是某个 UI 实例自己的展开状态缓存。因此:
|
||||
|
||||
- `HierarchyPanel` 用它记住实体树哪些节点已经展开。
|
||||
- `ProjectPanel` 用它记住目录树哪些文件夹已经展开。
|
||||
|
||||
当前实现采用 `std::unordered_map<std::string, bool>`,所以最好的 `persistenceKey` 是稳定且能跨帧复现的标识,例如:
|
||||
|
||||
- 场景对象的 UUID
|
||||
- 资源目录的完整路径
|
||||
|
||||
## 当前实现行为
|
||||
|
||||
按 `editor/src/UI/TreeView.h` 的实现,当前版本有几个关键事实:
|
||||
|
||||
- 树缩进不是保存在某个对象里,而是保存在一个静态 `TreeIndentStack()` 中。
|
||||
- 因此每次绘制一棵新的根树之前都应该先调用 `ResetTreeLayout()`。
|
||||
- `DrawTreeNode(...)` 若返回 `open = true`,调用方在绘制完所有子节点后必须调用 `EndTreeNode()` 与之配对。
|
||||
- 节点背景当前直接使用 `ImGuiCol_Header` / `HeaderHovered` / `HeaderActive` 这一组颜色绘制选中与 hover 态。
|
||||
- 展开箭头由 `Core.h` 里的 `DrawDisclosureArrow(...)` 手工绘制。
|
||||
- prefix slot 允许调用方在标签前插入小图标、状态灯或其他自定义绘制内容。
|
||||
- `callbacks.onInteraction` 适合接选择、右键菜单、双击重命名等语义。
|
||||
- `callbacks.onRenderExtras` 适合接拖拽源 / 拖拽目标之类必须紧跟节点绘制的额外逻辑。
|
||||
|
||||
这说明它不是对 `ImGui::TreeNodeEx(...)` 的轻封装,而是自己接管了树节点的大部分视觉和交互行为。
|
||||
|
||||
## 设计说明
|
||||
|
||||
为什么要单独造这一层,而不是每个面板直接用 ImGui 原生 tree API?
|
||||
|
||||
- `Hierarchy` 和 `Project` 都需要统一的左侧导航树风格,这种复用非常适合抽成共享 helper。
|
||||
- 商业编辑器中的树视图往往不仅是“文本 + 箭头”,还需要图标前缀、整行高亮、拖放热点、重命名切换等语义。
|
||||
- 一旦把节点定义、展开状态和回调插槽标准化,上层面板代码就能更聚焦于业务对象,而不是 UI 细节。
|
||||
|
||||
如果和 Unity 对照,可以把它理解成“Hierarchy / Project 导航树的共用控件层”,而不是某个特定面板的局部技巧。
|
||||
|
||||
## 典型用法
|
||||
|
||||
```cpp
|
||||
UI::ResetTreeLayout();
|
||||
|
||||
UI::TreeNodeDefinition node;
|
||||
node.options.selected = isSelected;
|
||||
node.options.leaf = !hasChildren;
|
||||
node.persistenceKey = stablePath;
|
||||
node.style = UI::ProjectFolderTreeStyle();
|
||||
node.prefix.width = UI::NavigationTreePrefixWidth();
|
||||
node.prefix.draw = DrawFolderPrefix;
|
||||
node.callbacks.onInteraction = [&](const UI::TreeNodeResult& result) {
|
||||
if (result.clicked) {
|
||||
NavigateToFolder();
|
||||
}
|
||||
};
|
||||
|
||||
const UI::TreeNodeResult result =
|
||||
UI::DrawTreeNode(&m_treeState, folder.get(), folder->name.c_str(), node);
|
||||
|
||||
if (result.open) {
|
||||
DrawChildren();
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
```
|
||||
|
||||
## 生命周期与线程语义
|
||||
|
||||
- `TreeViewState` 由具体面板持有,通常作为成员变量存在于面板实例生命周期内。
|
||||
- 整套 API 都依赖当前 ImGui 帧状态,只能在 UI 线程使用。
|
||||
- 它不负责把展开状态写入磁盘或布局文件;当前只是内存级持久化。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 静态 `TreeIndentStack()` 让它更像“约定式 helper”,而不是完全可重入的小部件对象。
|
||||
- 没有键盘导航、滚动到可见、虚拟化大树等更高级的树控件能力。
|
||||
- 展开状态目前只保存在面板实例中,重启编辑器后不会自动恢复。
|
||||
- 当前回调模型偏即时模式,适合编辑器内部使用,但不是声明式 UI 框架。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [UI](../UI.md)
|
||||
- [StyleTokens](../StyleTokens/StyleTokens.md)
|
||||
- [BuiltInIcons](../BuiltInIcons/BuiltInIcons.md)
|
||||
- [HierarchyPanel](../../panels/HierarchyPanel/HierarchyPanel.md)
|
||||
- [ProjectPanel](../../panels/ProjectPanel/ProjectPanel.md)
|
||||
@@ -4,47 +4,106 @@
|
||||
|
||||
**类型**: `submodule`
|
||||
|
||||
**描述**: 编辑器 ImGui 基础设施、主题、控件与面板绘制辅助层。
|
||||
**描述**: 编辑器基于 Dear ImGui 的 UI 基础设施层,负责会话、主题 token、面板 chrome、树视图、属性布局和通用控件封装。
|
||||
|
||||
## 概述
|
||||
|
||||
`UI` 子模块当前内容很多,但核心方向很清楚:
|
||||
`XCEngine::Editor::UI` 不是单个 widget 库,而是一组逐层叠加的编辑器 UI 基建。它的目标是把 Dear ImGui 这种非常自由、非常底层的即时模式 API 收束成一套可维护的“编辑器内建控件层”。
|
||||
|
||||
- 维护 ImGui 会话与布局文件
|
||||
- 封装统一视觉主题
|
||||
- 提供属性面板、工具栏、菜单、场景状态等 UI 辅助
|
||||
当前源码里,这一层大致可以分成四层:
|
||||
|
||||
已文档化的核心页面:
|
||||
1. **会话与后端层**
|
||||
负责 ImGui 上下文、ini 持久化、后端桥接和纹理描述符分配。
|
||||
2. **主题与设计 token 层**
|
||||
负责颜色、尺寸、间距、dock host 风格与常用视觉常量。
|
||||
3. **chrome 与布局层**
|
||||
负责面板窗口、工具栏、树视图、splitter、divider、属性行布局和自定义 dock 标签栏。
|
||||
4. **语义化控件层**
|
||||
负责属性网格、资产卡片、面包屑、弹窗、组件折叠段和各种编辑器专用小部件。
|
||||
|
||||
- [ImGuiSession](ImGuiSession/ImGuiSession.md)
|
||||
- [ImGuiBackendBridge](ImGuiBackendBridge/ImGuiBackendBridge.md)
|
||||
- [BaseTheme](BaseTheme/BaseTheme.md)
|
||||
- [StyleTokens](StyleTokens/StyleTokens.md)
|
||||
- [Core](Core/Core.md)
|
||||
- [PanelChrome](PanelChrome/PanelChrome.md)
|
||||
- [Widgets](Widgets/Widgets.md)
|
||||
- [PopupState](PopupState/PopupState.md)
|
||||
- [ScalarControls](ScalarControls/ScalarControls.md)
|
||||
- [VectorControls](VectorControls/VectorControls.md)
|
||||
- [PropertyGrid](PropertyGrid/PropertyGrid.md)
|
||||
- [DockHostStyle](DockHostStyle/DockHostStyle.md)
|
||||
- [ConsoleFilterState](ConsoleFilterState/ConsoleFilterState.md)
|
||||
- [ConsoleLogFormatter](ConsoleLogFormatter/ConsoleLogFormatter.md)
|
||||
- [SceneStatusWidget](SceneStatusWidget/SceneStatusWidget.md)
|
||||
- [AboutEditorDialog](AboutEditorDialog/AboutEditorDialog.md)
|
||||
这类分层方式非常接近商业级游戏引擎编辑器的组织方式。原因很简单:如果每个面板都直接拼原始 ImGui 调用,代码很快就会变成不可维护的样式杂糅体;而先建设统一 UI 层,后续任何面板重构、主题调整和交互升级都会容易很多。
|
||||
|
||||
`UI/UI.h` 本身只是聚合入口,真正值得阅读的是这些分层 helper:
|
||||
## 聚合头文件
|
||||
|
||||
- `ImGuiSession + ImGuiBackendBridge` 负责上下文与后端绑定
|
||||
- `BaseTheme + StyleTokens` 负责视觉规范
|
||||
- `Core + PanelChrome + Widgets` 负责通用交互壳层
|
||||
- `ScalarControls + VectorControls + PropertyGrid` 负责 Inspector 表单体验
|
||||
**源文件**: `editor/src/UI/UI.h`
|
||||
|
||||
这样拆分的好处,是把 Dear ImGui 容易失控的即时模式代码收束成一套可复用部件库。
|
||||
`UI.h` 当前是一个标准的 umbrella header。它的职责是把 Editor UI 常用 helper 聚合到同一个入口,而不是声明新的运行时类型。
|
||||
|
||||
因此这页文档直接承担 `UI.h` 的说明职责,不再额外创建一个重复的类型页。
|
||||
|
||||
## 当前架构重点
|
||||
|
||||
结合当前 `editor/src/UI/**` 与最近一次 Editor UI 重构,以下几层是现在最值得读的核心:
|
||||
|
||||
- `ImGuiSession + ImGuiBackendBridge`
|
||||
管理 ImGui 上下文、DPI、SRV 描述符与窗口后端对接。
|
||||
- `BaseTheme + StyleTokens + DockHostStyle`
|
||||
管理设计 token 和 dock host 外观,是全局视觉风格的起点。
|
||||
- `Core + DividerChrome + SplitterChrome + DockTabBarChrome + PanelChrome`
|
||||
管理窗口 chrome、底边线、拖拽分隔条与自定义 dock 标签栏。
|
||||
- `TreeView + BuiltInIcons`
|
||||
管理层级树 / 目录树的共享节点表现与图标前缀。
|
||||
- `PropertyLayout + ScalarControls + VectorControls + PropertyGrid`
|
||||
管理 Inspector 的属性布局、标量控件、向量控件和高层属性编辑入口。
|
||||
- `Widgets + PopupState + SceneStatusWidget + AboutEditorDialog`
|
||||
提供更接近编辑器业务语义的通用 widget。
|
||||
|
||||
这次重构里,`TreeView`、`PropertyLayout`、`BuiltInIcons` 和 `DockTabBarChrome` 是最明显的新基础设施,它们共同把原先分散在具体面板中的 UI 技巧沉淀成了复用层。
|
||||
|
||||
## 设计说明
|
||||
|
||||
如果把它和 Unity 一类成熟编辑器对照,可以把 `Editor::UI` 理解成“编辑器内部控件系统”,而不是游戏运行时 UI 框架:
|
||||
|
||||
- 它面向工具开发,不面向游戏内 HUD。
|
||||
- 它可以为编辑器体验服务而使用较强约束的 helper。
|
||||
- 它允许把某些风格、布局和交互策略写死在共享层,以换取整个工具界面的统一性。
|
||||
|
||||
这种设计的好处是:
|
||||
|
||||
- 面板作者写的代码更接近业务语义,而不是样式拼装。
|
||||
- 主题、间距、树视图和 Inspector 布局能在全局范围内保持一致。
|
||||
- 面板之间可以共享拖拽、上下文菜单、资产卡片、树节点前缀等成熟模式。
|
||||
|
||||
代价也很明确:
|
||||
|
||||
- 当前 UI 层明显偏 Editor 私有实现,不是面向外部插件的稳定 ABI。
|
||||
- 很多 helper 仍然是 header-only inline 形式,接口演化速度会比较快。
|
||||
- 当前实现强依赖 Windows + D3D12 + Dear ImGui 的编辑器运行路径。
|
||||
|
||||
## 头文件
|
||||
|
||||
- [AboutEditorDialog](AboutEditorDialog/AboutEditorDialog.md) - `AboutEditorDialog.h`,关于对话框相关 UI。
|
||||
- [BaseTheme](BaseTheme/BaseTheme.md) - `BaseTheme.h`,编辑器基础主题安装。
|
||||
- [BuiltInIcons](BuiltInIcons/BuiltInIcons.md) - `BuiltInIcons.h`,内置资源/对象图标系统。
|
||||
- [ConsoleFilterState](ConsoleFilterState/ConsoleFilterState.md) - `ConsoleFilterState.h`,Console 过滤状态。
|
||||
- [ConsoleLogFormatter](ConsoleLogFormatter/ConsoleLogFormatter.md) - `ConsoleLogFormatter.h`,Console 日志格式化。
|
||||
- [Core](Core/Core.md) - `Core.h`,底层 ImGui helper 与 popup chrome 包装。
|
||||
- [DividerChrome](DividerChrome/DividerChrome.md) - `DividerChrome.h`,统一分隔线绘制。
|
||||
- [DockHostStyle](DockHostStyle/DockHostStyle.md) - `DockHostStyle.h`,dock host 风格压栈。
|
||||
- [DockTabBarChrome](DockTabBarChrome/DockTabBarChrome.md) - `DockTabBarChrome.h`,自定义 dock 标签栏。
|
||||
- [ImGuiBackendBridge](ImGuiBackendBridge/ImGuiBackendBridge.md) - `ImGuiBackendBridge.h`,ImGui 与 D3D12 之间的桥接层。
|
||||
- [ImGuiSession](ImGuiSession/ImGuiSession.md) - `ImGuiSession.h`,ImGui 会话生命周期。
|
||||
- [PanelChrome](PanelChrome/PanelChrome.md) - `PanelChrome.h`,面板窗口 / 工具栏 / 内容区 RAII 外壳。
|
||||
- [PopupState](PopupState/PopupState.md) - `PopupState.h`,延迟弹窗与目标型弹窗状态。
|
||||
- [PropertyGrid](PropertyGrid/PropertyGrid.md) - `PropertyGrid.h`,Inspector 级属性编辑入口。
|
||||
- [PropertyLayout](PropertyLayout/PropertyLayout.md) - `PropertyLayout.h`,属性行几何布局层。
|
||||
- [ScalarControls](ScalarControls/ScalarControls.md) - `ScalarControls.h`,标量属性控件。
|
||||
- [SceneStatusWidget](SceneStatusWidget/SceneStatusWidget.md) - `SceneStatusWidget.h`,场景状态部件。
|
||||
- [SplitterChrome](SplitterChrome/SplitterChrome.md) - `SplitterChrome.h`,分隔条交互与绘制。
|
||||
- [StyleTokens](StyleTokens/StyleTokens.md) - `StyleTokens.h`,Editor UI 设计 token 中心。
|
||||
- [TreeView](TreeView/TreeView.md) - `TreeView.h`,树视图共享基础设施。
|
||||
- [VectorControls](VectorControls/VectorControls.md) - `VectorControls.h`,向量属性控件。
|
||||
- [Widgets](Widgets/Widgets.md) - `Widgets.h`,资产卡片、面包屑、组件折叠段等通用 widget。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 这是 Editor 应用层 UI,不是运行时 `engine/include/XCEngine` 风格的公共引擎 API。
|
||||
- 当前很多接口仍然直接暴露 ImGui 类型,如 `ImDrawList`、`ImVec2`、`ImGuiID`。
|
||||
- 自定义 dock 标签栏、树视图和图标系统都明显面向当前编辑器产品形态,而不是通用 GUI 框架。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Editor 模块](../Editor.md)
|
||||
- [Application](../Application/Application.md)
|
||||
- [Platform](../Platform/Platform.md)
|
||||
- [Editor](../Editor.md)
|
||||
- [Layout](../Layout/Layout.md)
|
||||
- [ComponentEditors](../ComponentEditors/ComponentEditors.md)
|
||||
- [Editor Architecture And Workflow](../../../_guides/Editor/Editor-Architecture-And-Workflow.md)
|
||||
|
||||
Reference in New Issue
Block a user