docs: update API docs for rendering and script editors
This commit is contained in:
@@ -6,36 +6,54 @@
|
||||
|
||||
**源文件**: `editor/src/ComponentEditors/ComponentEditorRegistry.h`
|
||||
|
||||
**描述**: 持有并注册所有组件编辑器实例,按组件类型名为 Inspector 提供查询入口。
|
||||
**描述**: 持有并注册所有组件编辑器实例,按组件类型名为 Inspector 提供统一查询入口。
|
||||
|
||||
## 概述
|
||||
|
||||
`ComponentEditorRegistry` 是当前组件编辑器系统的注册中心。
|
||||
`ComponentEditorRegistry` 是当前 Inspector 组件编辑器系统的注册中心。它负责:
|
||||
|
||||
它负责:
|
||||
- 拥有 `IComponentEditor` 实例
|
||||
- 按组件实例查找 editor
|
||||
- 按组件类型名查找 editor
|
||||
- 暴露所有已注册 editor,供 Inspector 侧遍历和构建“添加组件”入口
|
||||
|
||||
- 注册 `IComponentEditor`
|
||||
- 根据组件实例查找对应 editor
|
||||
- 根据组件类型名查找 editor
|
||||
- 暴露全部 editor 列表
|
||||
## 当前注册列表
|
||||
|
||||
## 当前实现说明
|
||||
按 `editor/src/ComponentEditors/ComponentEditorRegistry.cpp`,构造函数当前会注册:
|
||||
|
||||
- 采用单例 `Get()`。
|
||||
- 构造函数里会自动注册:
|
||||
- `TransformComponentEditor`
|
||||
- `CameraComponentEditor`
|
||||
- `LightComponentEditor`
|
||||
- `m_editors` 负责拥有这些 editor 对象。
|
||||
- `m_editorsByType` 提供按组件类型名的快速查找。
|
||||
1. `TransformComponentEditor`
|
||||
2. `CameraComponentEditor`
|
||||
3. `LightComponentEditor`
|
||||
4. `MeshFilterComponentEditor`
|
||||
5. `MeshRendererComponentEditor`
|
||||
6. [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md)
|
||||
|
||||
这说明脚本组件编辑器已经是 registry 里的正式一员,而不是 Inspector 特判分支。
|
||||
|
||||
## 数据结构
|
||||
|
||||
- `m_editors`:`std::vector<std::unique_ptr<IComponentEditor>>`,负责真正拥有 editor 对象
|
||||
- `m_editorsByType`:按 `GetComponentTypeName()` 建立的快速索引
|
||||
|
||||
`RegisterEditor()` 当前会先把裸指针写入 `m_editorsByType`,再把 `unique_ptr` 推入 `m_editors`。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 当前注册列表写死在构造函数中,不是插件式发现机制。
|
||||
- 注册列表写死在构造函数里,不是插件式发现机制。
|
||||
- 如果后注册 editor 与已有类型名重复,`m_editorsByType` 会被覆盖,但 `m_editors` 里仍会保留多个对象。
|
||||
- `FindEditor(component)` 依赖组件的 `GetName()` 返回值与 editor 的 `GetComponentTypeName()` 完整对齐。
|
||||
|
||||
## 与 ScriptComponentEditor 的关系
|
||||
|
||||
`ScriptComponentEditor` 通过这里注册后,InspectorPanel 就能像处理普通组件一样处理脚本组件:
|
||||
|
||||
- 按 `ScriptComponent` 类型查找到 editor
|
||||
- 调用其 `Render()` 获取脚本类选择器和脚本字段 UI
|
||||
- 继续复用统一的撤销与 Inspector 布局体系
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ComponentEditors](../ComponentEditors.md)
|
||||
- [IComponentEditor](../IComponentEditor/IComponentEditor.md)
|
||||
- [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md)
|
||||
- [InspectorPanel](../../panels/InspectorPanel/InspectorPanel.md)
|
||||
|
||||
@@ -4,30 +4,47 @@
|
||||
|
||||
**类型**: `submodule`
|
||||
|
||||
**描述**: 组件属性编辑器层,负责把运行时组件映射到 Inspector 中的具体编辑 UI。
|
||||
**描述**: Inspector 组件编辑器层,负责把运行时组件映射成可撤销、可交互的专用编辑 UI。
|
||||
|
||||
## 概述
|
||||
|
||||
当前目录包含:
|
||||
当前组件编辑器系统走的是典型的“按组件类型注册专用 editor”路线:
|
||||
|
||||
- [IComponentEditor](IComponentEditor/IComponentEditor.md) 定义统一契约
|
||||
- [ComponentEditorRegistry](ComponentEditorRegistry/ComponentEditorRegistry.md) 负责注册、拥有和查找 editor
|
||||
- 各个具体 editor 只处理自己的组件 UI 与交互规则
|
||||
|
||||
这比把所有组件字段塞进一个通用 Inspector 渲染器更容易扩展,也更方便为特殊组件接入脚本元数据、引用选择器和撤销逻辑。
|
||||
|
||||
## 当前已记录的 editor
|
||||
|
||||
- [IComponentEditor](IComponentEditor/IComponentEditor.md)
|
||||
- [ComponentEditorRegistry](ComponentEditorRegistry/ComponentEditorRegistry.md)
|
||||
- [TransformComponentEditor](TransformComponentEditor/TransformComponentEditor.md)
|
||||
- [CameraComponentEditor](CameraComponentEditor/CameraComponentEditor.md)
|
||||
- [LightComponentEditor](LightComponentEditor/LightComponentEditor.md)
|
||||
- [ScriptComponentEditor](ScriptComponentEditor/ScriptComponentEditor.md)
|
||||
|
||||
这说明 Inspector 当前走的是“按组件类型注册专用 editor”的路线。这和商业编辑器里常见的 custom inspector / property drawer 思路是一致的。
|
||||
## 当前注册但尚未单独补页的 editor
|
||||
|
||||
当前这组 editor 也已经体现出比较明确的分层:
|
||||
按 `ComponentEditorRegistry.cpp` 当前实现,registry 还会注册:
|
||||
|
||||
- `IComponentEditor` 定义统一契约
|
||||
- `ComponentEditorRegistry` 负责注册和查找
|
||||
- 具体 editor 只处理本组件的 Inspector 体验
|
||||
- `MeshFilterComponentEditor`
|
||||
- `MeshRendererComponentEditor`
|
||||
|
||||
这比把所有组件字段硬塞进一个大而全的 Inspector 渲染器更容易扩展。
|
||||
## ScriptComponentEditor 在这层里的位置
|
||||
|
||||
[ScriptComponentEditor](ScriptComponentEditor/ScriptComponentEditor.md) 是这套系统里最依赖运行时元数据的一个 editor。它不是直接读取 `ScriptComponent` 自身的字段列表,而是通过 `ScriptEngine::TryGetScriptFieldModel()` 获取:
|
||||
|
||||
- 当前脚本类是否已分配 / 可用 / 缺失
|
||||
- 字段元数据
|
||||
- 默认值、stored override 和 live managed value 的合成结果
|
||||
- stored-only / type mismatch 一类问题状态
|
||||
|
||||
因此它也是 `ComponentEditors` 子模块和 `Scripting` 子模块的主要交汇点。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Editor 模块](../Editor.md)
|
||||
- [panels](../panels/panels.md)
|
||||
- [ComponentEditorRegistry](ComponentEditorRegistry/ComponentEditorRegistry.md)
|
||||
- [ScriptComponentEditor](ScriptComponentEditor/ScriptComponentEditor.md)
|
||||
- [ScriptComponentEditorUtils](ScriptComponentEditorUtils/ScriptComponentEditorUtils.md)
|
||||
- [InspectorPanel](../panels/InspectorPanel/InspectorPanel.md)
|
||||
- [Components](../../Components/Components.md)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# ScriptComponentEditor
|
||||
|
||||
**命名空间**: `XCEngine::Editor`
|
||||
|
||||
**类型**: `class`
|
||||
|
||||
**源文件**: `editor/src/ComponentEditors/ScriptComponentEditor.h`
|
||||
|
||||
**描述**: `ScriptComponent` 的专用 Inspector 编辑器,负责脚本类选择、脚本字段绘制、override 写回和运行时不可用提示。
|
||||
|
||||
## 概述
|
||||
|
||||
`ScriptComponentEditor` 是当前 Inspector 里最动态的一类组件编辑器。它实现了 [IComponentEditor](../IComponentEditor/IComponentEditor.md),但和普通“直接编辑组件成员”的 editor 不同,它要先经过 [ScriptEngine](../../../Scripting/ScriptEngine/ScriptEngine.md) 查询脚本字段模型,再决定能显示什么、能编辑什么。
|
||||
|
||||
当前 `Render()` 的主流程是:
|
||||
|
||||
1. 把传入组件 `dynamic_cast` 成 [ScriptComponent](../../../Scripting/ScriptComponent/ScriptComponent.md)
|
||||
2. 绘制脚本类选择器
|
||||
3. 调用 `ScriptEngine::TryGetScriptFieldModel()` 获取当前脚本类状态和字段快照
|
||||
4. 根据类状态显示 hint、缺失信息或字段列表
|
||||
5. 对每个字段决定走可编辑控件还是只读显示
|
||||
6. 必要时提供 `Reset` 按钮清除 stored override
|
||||
|
||||
## 脚本类选择器
|
||||
|
||||
脚本类下拉框的数据来自 `ScriptEngine::TryGetAvailableScriptClasses()`。
|
||||
|
||||
- 已分配脚本类且能在当前 runtime 中找到时,显示规范化类名
|
||||
- 已分配但当前加载的脚本程序集里找不到时,显示 `"(Missing)"`
|
||||
- 未分配时显示 `None`
|
||||
|
||||
如果当前没有加载到脚本程序集,editor 还会读取 `Application::Get().GetScriptRuntimeStatus()`,并按状态决定是否显示:
|
||||
|
||||
- `Rebuild Scripts`
|
||||
- `Reload Scripts`
|
||||
|
||||
这意味着脚本组件编辑器不仅是字段编辑 UI,也是脚本运行时状态的主要反馈入口之一。
|
||||
|
||||
## 字段元数据来源
|
||||
|
||||
字段列表不是直接从 `ScriptComponent` 本地成员生成的,而是来自 `ScriptEngine::TryGetScriptFieldModel()` 合成的 `ScriptFieldModel`:
|
||||
|
||||
- 脚本类元数据来自 `IScriptRuntime::TryGetClassFieldMetadata()`
|
||||
- 默认值来自运行时侧的类默认值采集
|
||||
- live managed value 优先来自 `TryGetManagedFieldValue()`
|
||||
- 如果没有 live instance,则回退到 `ScriptComponent::GetFieldStorage()` 中的 stored override
|
||||
- 如果 storage 里存在脚本类已不再声明的字段,会以 `StoredOnly` 形式补到模型里
|
||||
|
||||
这也是 editor 能区分“可编辑字段”“只剩 stored override 的遗留字段”“类型不匹配字段”的原因。
|
||||
|
||||
## 当前支持的字段类型
|
||||
|
||||
按 `RenderScriptFieldEditor()` 当前实现,editor 支持这些字段控件:
|
||||
|
||||
- `Float`
|
||||
- `Double`
|
||||
- `Bool`
|
||||
- `Int32`
|
||||
- `UInt64`
|
||||
- `String`
|
||||
- `Vector2`
|
||||
- `Vector3`
|
||||
- `Vector4`
|
||||
- `GameObject`
|
||||
|
||||
其中:
|
||||
|
||||
- `String` 使用内部 `StringFieldEditState` 做编辑缓存,避免外部同步打断输入
|
||||
- `GameObject` 通过 `UI::DrawReferencePickerControl()` 和场景对象 UUID / ID 映射完成引用选择
|
||||
|
||||
## 可编辑性与 reset 规则
|
||||
|
||||
当前规则由 [ScriptComponentEditorUtils](../ScriptComponentEditorUtils/ScriptComponentEditorUtils.md) 决定:
|
||||
|
||||
- `classStatus == Available` 且字段确实由当前类声明时,字段可编辑
|
||||
- 类缺失或未分配时,stored-only 字段也允许继续编辑 / 保留
|
||||
- 当字段存在 stored value,或当前值来自 managed runtime 时,显示 `Reset`
|
||||
|
||||
写回路径则统一走:
|
||||
|
||||
- `ScriptEngine::ApplyScriptFieldWrites()`
|
||||
- `ScriptEngine::ClearScriptFieldOverrides()`
|
||||
|
||||
并在 Inspector 层接入 `IUndoManager::BeginInteractiveChange(...)`。
|
||||
|
||||
## 当前实现边界
|
||||
|
||||
- 当前不支持多对象联合编辑。
|
||||
- `StringFieldEditState` 以 `scriptComponentUUID + fieldName` 为键缓存,没有额外清理失效键的机制。
|
||||
- 对不支持的字段类型会退回只读文本显示,而不是显示通用编辑器。
|
||||
- `CanAddTo()` 当前对任意非空 `GameObject` 都返回 `true`,没有做“每对象只能有一个 ScriptComponent”之类的额外限制。
|
||||
|
||||
## 测试与真实调用点
|
||||
|
||||
- `ComponentEditorRegistry` 当前会自动注册这个 editor。
|
||||
- `tests/Editor/test_script_component_editor_utils.cpp` 覆盖了它依赖的显示名、字段可编辑性、issue 文本和 runtime 状态提示规则。
|
||||
- Inspector 真实字段模型和写回语义依赖 `ScriptEngine.cpp` 当前实现。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ComponentEditors](../ComponentEditors.md)
|
||||
- [ComponentEditorRegistry](../ComponentEditorRegistry/ComponentEditorRegistry.md)
|
||||
- [ScriptComponentEditorUtils](../ScriptComponentEditorUtils/ScriptComponentEditorUtils.md)
|
||||
- [ScriptEngine](../../../Scripting/ScriptEngine/ScriptEngine.md)
|
||||
- [ScriptComponent](../../../Scripting/ScriptComponent/ScriptComponent.md)
|
||||
@@ -0,0 +1,83 @@
|
||||
# ScriptComponentEditorUtils
|
||||
|
||||
**命名空间**: `XCEngine::Editor::ScriptComponentEditorUtils`
|
||||
|
||||
**类型**: `utility namespace`
|
||||
|
||||
**源文件**: `editor/src/ComponentEditors/ScriptComponentEditorUtils.h`
|
||||
|
||||
**描述**: 为 `ScriptComponentEditor` 提供显示名格式化、场景对象 UUID 查找、字段可编辑性判断和脚本运行时状态提示的内联辅助函数集合。
|
||||
|
||||
## 概述
|
||||
|
||||
`ScriptComponentEditorUtils.h` 不是一个运行时对象,而是一组专门为 [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md) 服务的 helper。它把那些“不是 UI 控件本身,但决定 UI 怎么显示”的规则集中到了一个地方,避免这些判断散落在 editor 主流程里。
|
||||
|
||||
## 当前承担的工作
|
||||
|
||||
### 脚本类显示名
|
||||
|
||||
- `BuildScriptClassDisplayName(descriptor)`
|
||||
- `BuildScriptClassDisplayName(component)`
|
||||
|
||||
当前规则是:
|
||||
|
||||
- 如果程序集名为空,或程序集名为 `GameScripts`,只显示完整类名
|
||||
- 否则显示 `FullName (AssemblyName)`
|
||||
|
||||
这让项目主脚本程序集保持简洁显示,而工具程序集仍能区分来源。
|
||||
|
||||
### 场景对象 UUID 到 Inspector 可选目标的映射
|
||||
|
||||
- `FindGameObjectIdByUuidRecursive(...)`
|
||||
- `FindGameObjectIdByUuid(...)`
|
||||
|
||||
`GameObjectReference` 在脚本字段里存的是 UUID,但 Inspector 引用选择器用的是当前 scene object 的运行时 ID。这组函数负责在场景树里做桥接查找。
|
||||
|
||||
### 字段可编辑性与问题提示
|
||||
|
||||
- `CanEditScriptField(...)`
|
||||
- `CanClearScriptFieldOverride(...)`
|
||||
- `BuildScriptFieldIssueText(...)`
|
||||
|
||||
当前规则里最关键的一点是:
|
||||
|
||||
- 当脚本类状态是 `Available` 时,只有 `declaredInClass == true` 的字段可编辑
|
||||
- 当类缺失或未分配时,遗留 stored 字段仍可继续显示和编辑
|
||||
|
||||
issue 文本当前主要覆盖:
|
||||
|
||||
- `StoredOnly`
|
||||
- `TypeMismatch`
|
||||
|
||||
### 脚本运行时不可用提示
|
||||
|
||||
- `BuildScriptRuntimeUnavailableHint(...)`
|
||||
- `CanReloadScriptRuntime(...)`
|
||||
- `CanRebuildScriptAssemblies(...)`
|
||||
|
||||
这些函数把 `EditorScriptRuntimeStatus` 转成 Inspector 上可直接显示的 hint 与按钮启用条件。
|
||||
|
||||
## 为什么这层有价值
|
||||
|
||||
`ScriptComponentEditor` 同时依赖:
|
||||
|
||||
- 脚本运行时状态
|
||||
- 场景对象层级
|
||||
- 脚本字段问题状态
|
||||
|
||||
把这些判断抽到 utils 后,editor 主类就更聚焦于“如何渲染 UI”和“什么时候写回 ScriptEngine”,而不是维护大量显示策略分支。
|
||||
|
||||
## 测试覆盖
|
||||
|
||||
`tests/Editor/test_script_component_editor_utils.cpp` 当前验证了:
|
||||
|
||||
- 脚本类显示名格式化
|
||||
- 场景层级里的 UUID 查找
|
||||
- 字段可编辑性、可清除性和 issue 文本
|
||||
- runtime unavailable hint 与 reload / rebuild 可用性
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [ScriptComponentEditor](../ScriptComponentEditor/ScriptComponentEditor.md)
|
||||
- [ScriptEngine](../../../Scripting/ScriptEngine/ScriptEngine.md)
|
||||
- [ScriptComponent](../../../Scripting/ScriptComponent/ScriptComponent.md)
|
||||
Reference in New Issue
Block a user