editor: centralize product panel manifest
This commit is contained in:
7
AGENT.md
7
AGENT.md
@@ -61,7 +61,7 @@
|
||||
- `engine/` 构建静态库 `XCEngine`;`editor/` 构建 `XCUIEditor` 静态库和 `XCEditor` 可执行目标。
|
||||
- `editor/` 目前继续保留为当前正式编辑器、行为对照和视觉基线来源。
|
||||
- 启用 `XCENGINE_BUILD_XCUI_EDITOR_APP` 时,`XCEditor` 输出 `build/editor/<Config>/XCEngine.exe`。
|
||||
- editor 默认把仓库内的 `project/` 识别为工程根目录,也支持 `--project <path>` 覆盖。
|
||||
- 当前 XCUI editor 把仓库内的 `project/` 固定识别为工程根目录;不要再沿用旧的 `--project <path>` 说法。
|
||||
- 当前工程真实使用 `Assets/ + .meta + Library/` 的项目布局;`project/Library/` 是当前 workflow 的一部分,不是可随手忽略的垃圾目录。
|
||||
- Mono 运行时与 editor 的脚本类发现都从 `<project>/Library/ScriptAssemblies/` 加载程序集。
|
||||
- 当前根目录里没有 `native` 占位项;更新目录树或迁移旧文档时,不要继续传播这条过期事实。
|
||||
@@ -108,13 +108,14 @@
|
||||
### 3.3 Editor
|
||||
|
||||
- editor 仍然是 `D3D12` 宿主应用。
|
||||
- Scene/Game viewport 已通过引擎 `Rendering + RHI` 输出离屏纹理,再由 editor 宿主接入 ImGui。
|
||||
- Scene/Game viewport 已通过引擎 `Rendering + RHI` 输出离屏纹理,再由 editor 宿主接入 XCUI shell。
|
||||
- 当前 Scene View 主链已经显式拆成:
|
||||
- `SceneViewportChrome`
|
||||
- `SceneViewportInteractionFrame`
|
||||
- `SceneViewportNavigation`
|
||||
- `SceneViewportTransformGizmoCoordinator`
|
||||
- `ViewportHostService`
|
||||
- 当前 editor 产品装配的单一事实源是 `editor/app/Core/Product/EditorProductManifest.*`。panel 声明、action route、runtime owner、viewport renderer owner 先看这里,再看派生出来的 shell / menu / runtime / viewport 注册。
|
||||
- `editor/src/Viewport/` 当前稳定存在的关键入口包括:
|
||||
- `SceneViewportCameraController`
|
||||
- `SceneViewportChrome`
|
||||
@@ -149,9 +150,11 @@
|
||||
- `editor/` 是当前 XCUI 编辑器主线;不要再把 `new_editor/` 当成当前 checkout 的构建入口。
|
||||
- 当前宿主分层是:
|
||||
- `XCUIEditor`
|
||||
- `XCEditorCore`
|
||||
- `XCEditor`(可选应用壳,输出 `XCEngine.exe`)
|
||||
- 共享 UI core、runtime screen host 与 widget 基础能力主要沉淀在 `engine/include/XCEngine/UI/` 与 `engine/src/UI/`;`editor/` 负责 XCUI editor 壳、宿主与产品装配。
|
||||
- `tests/UI/` 是当前 XCUI `Core / Editor / Runtime` 三层的唯一正式基础层验证入口;`editor/` 不承担测试堆场职责。
|
||||
- 当前 `game` panel 已在产品 manifest 中显式声明为 placeholder viewport:它会显示未实现状态,但没有正式 Game runtime / renderer owner,不要把它当作已完成能力。
|
||||
|
||||
### 3.5 Scripting
|
||||
|
||||
|
||||
123
editor/AGENTS.md
Normal file
123
editor/AGENTS.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# XCEditor Agent Guide
|
||||
|
||||
本文面向以后在 `editor/` 下工作的 coding agent / 开发者。若本文与代码、`CMakeLists.txt` 或测试目标冲突,以代码为准,并在同一次改动中更新本文。
|
||||
|
||||
## 长期目标
|
||||
|
||||
- `editor/` 是当前 XCUI 编辑器主线,不是旧 `mvs/editor` 或 `new_editor` 入口。长期方向是把可复用 UI shell 与产品编辑器装配继续分离清楚。
|
||||
- `XCUIEditor` 保持为平台无关、后端无关的静态库。公共头在 `include/XCEditor/**`,实现主要在 `src/**`;不要放入 Win32、D3D12、DXGI、产品面板状态或工程 runtime 事实。
|
||||
- `XCEditorCore` 承担产品编辑器核心。代码主要在 `app/Core`、`app/Composition`、`app/Features`、`app/Services`、`app/Windowing`,并通过 `XCEditorCoreRendering` 对象库接入 `app/Rendering`;它不直接拥有 Win32 消息循环。
|
||||
- `XCEditor` 是可选 Win32 + D3D12 应用壳。代码在 `app/Bootstrap`、`app/Host/Win32`、`app/Host/D3D12`,负责窗口、DPI、消息分发、swapchain、UI 纹理和截图。
|
||||
- 产品装配应以 `app/Core/Product/EditorProductManifest.*` 为单一事实源。正式 panel 集、action route、runtime owner、viewport renderer owner 先在 manifest 中声明,再派生 shell / menu / command / runtime / viewport 注册。
|
||||
- UI widget / shell / workspace 代码优先保持 model/state/request/frame/result 风格。新增行为要能被 `tests/UI/Editor/unit` 以纯状态方式测试。
|
||||
- scene/project 的用户操作应通过 runtime 或 command route 进入,不要在 draw/append 阶段直接改 scene 或文件系统。
|
||||
- scene 渲染私有逻辑不要塞进 panel 或 shell。新增渲染能力时先判断它属于 engine `Rendering/RHI`、editor viewport pass bundle,还是 UI overlay。
|
||||
- 资源路径、图标、shader、截图输出都应走明确服务或 host 接口。不要硬编码从当前工作目录猜路径;手动验证截图不得写回 source tree。
|
||||
|
||||
## 当前情况
|
||||
|
||||
- `editor/AGENTS.md` 本身被 `app/Bootstrap/Application.cpp` 的 `HasEditorRepoMarkers()` 用作仓库根定位标记之一。不要重命名、删除或移动它;如果根定位规则变了,同时更新本文。
|
||||
- 顶层 `CMakeLists.txt` 默认启用 `XCENGINE_BUILD_XCUI_EDITOR_CORE` 和 `XCENGINE_BUILD_XCUI_EDITOR_APP`。构建 core/app 时必须启用 `XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT`。
|
||||
- `XCEditor` 目标输出名是 `XCEngine.exe`,Debug 产物位于 `build/editor/Debug/XCEngine.exe`。
|
||||
- 当前应用没有命令行项目选择流程;`EditorContext` 将项目根固定为 `<repoRoot>/project`,不要沿用旧文档里的 `--project <path>` 说法。
|
||||
- 当前面板 ID 为 `hierarchy`、`scene`、`game`、`inspector`、`console`、`project`,由 `EditorProductManifest.*` 声明并驱动注册。
|
||||
- 当前 `game` panel 是 viewport shell,但 renderer owner 是 placeholder,不是正式 Game runtime。它会显示 `Game view runtime is not implemented`,不要假设 Game view 已完整实现。
|
||||
- 当前 command 事实:`file.exit` 已绑定退出;`edit.*` 通过 active route 分发到 hierarchy/project/scene/inspector;`assets.*` 通过 project route 分发;多数 `file.*`、`run.*`、`scripts.*`、`help.about` 仍只是菜单/命令 surface,未拥有完整 host owner。
|
||||
- `EditorContext` 是产品级状态聚合点:`EditorSession`、project runtime、scene runtime、selection、command focus、utility window request、shortcut manager 和 host command bridge 都在这里串接。
|
||||
- `UIEditorWorkspaceController` 管单窗口 workspace model/session;`EditorWindowSystem` 管多窗口 workspace set。跨窗口 detach、close、update 必须经过 synchronization plan。
|
||||
- `EditorWorkspacePanelRuntimeSet` 托管产品面板生命周期。新增 panel 时先改 `EditorProductManifest.*`,再按需要调整 `BuildEditorWorkspaceModel()` 的默认布局和测试;不要再手工在 registry / menu / runtime set / viewport host 多处分别补定义。
|
||||
- `EditorSelectionService` 是 hierarchy/project/inspector/scene viewport 之间的选择同步核心。不要在单个 panel 内维护另一套长期选择真相。
|
||||
- `EditorProjectRuntime` 包装 `ProjectBrowserModel`,负责 project tree/grid、选择、文件操作、scene asset open request。文件系统改动后要刷新并 revalidate selection。
|
||||
- `EditorSceneRuntime` 负责 startup scene、editor scene camera、hierarchy selection、component list、transform edit history 和 scene tool state。
|
||||
|
||||
当前目录地图:
|
||||
|
||||
- `include/XCEditor/Foundation`:命令注册/分发、快捷键、主题、文本测量接口、runtime trace。
|
||||
- `include/XCEditor/Fields`:bool、number、text、enum、asset、object、color、vector、property grid 等编辑字段的绘制模型与交互。
|
||||
- `include/XCEditor/Collections`:list/tree/tab/scroll/inline rename/drag-drop 等集合控件。
|
||||
- `include/XCEditor/Docking`、`include/XCEditor/Workspace`:dock host、workspace tree、layout persistence、splitter correction、panel detach/transfer。
|
||||
- `include/XCEditor/Shell`:menu/toolbar/status/workspace 的 shell 组合与交互入口。
|
||||
- `include/XCEditor/Viewport`:viewport slot/shell/input bridge。这里是通用 UI viewport 容器,不是 scene renderer。
|
||||
- `include/XCEditor/Windowing`:多窗口 workspace 状态、同步计划和 presentation policy。
|
||||
- `src/**`:对应公共头的实现。保持偏纯函数/状态机风格。
|
||||
- `app/Core`:产品级 contracts、session、command focus、selection、panel services、scene/project/viewport/windowing 接口。
|
||||
- `app/Core/Product`:产品 manifest。这里定义正式 panel 集、route 归属、runtime owner 和 viewport renderer owner。
|
||||
- `app/Composition`:装配编辑器 shell。`EditorContext` 拥有 session、project runtime、scene runtime、selection、command bridge;`EditorShellRuntime` 驱动 shell interaction、hosted panels 和 viewport runtime。
|
||||
- `app/Features`:产品面板与场景视图工具。
|
||||
- `app/Rendering`:编辑器 viewport、icon、object-id picking、grid/outline/helper pass 相关服务。渲染执行仍走 engine `Rendering + RHI`。
|
||||
- `app/Host`:宿主接口实现。Win32/D3D12 细节只能待在这里或 rendering host 实现里。
|
||||
- `app/Windowing`:窗口实例、内容控制器、生命周期协调器、workspace 多窗口同步、截图和 frame orchestration。
|
||||
- `resources/Icons`、`resources/shaders/scene-viewport`:内置图标和 scene viewport shader。资源路径由 app/rendering 层解析。
|
||||
|
||||
当前启动和帧流程:
|
||||
|
||||
```text
|
||||
wWinMain
|
||||
-> RunXCEditor
|
||||
-> Application::Initialize / Run
|
||||
-> EditorContext::Initialize
|
||||
-> BuildEditorApplicationShellAsset
|
||||
-> EditorWindowSystem::BootstrapPrimaryWindow
|
||||
-> EditorWindowManager::CreateWorkspaceWindow
|
||||
-> EditorWindowRuntimeController
|
||||
-> EditorWorkspaceWindowContentController
|
||||
-> EditorShellRuntime::Update / Append / RenderRequestedViewports
|
||||
```
|
||||
|
||||
- `Application::Run()` pump Win32 message,更新 `ResourceManager::UpdateAsyncLoads()`,然后让 `EditorWindowManager::RenderAllWindows()` 驱动所有窗口。
|
||||
- `EditorWindowRuntimeController::BeginFrame()` 从 D3D12 runtime 取 `RenderContext`,content controller 先更新 shell 与 hosted panels,再 present UI draw data。
|
||||
- `EditorShellInteractionEngine::Update()` 先 `BeginFrame()` 清空 viewport 请求,再运行 `UpdateUIEditorShellInteraction()`,最后把每个 viewport shell 的尺寸提交给 `EditorViewportRuntimeServices::RequestViewport()`。
|
||||
- `EditorShellRuntime::RenderRequestedViewports()` 在 UI shell 更新后调用 viewport runtime,把 scene viewport 渲染进离屏纹理,再由 shell frame 展示。
|
||||
|
||||
当前 Scene Viewport 分层:
|
||||
|
||||
- `src/Viewport` 和 `include/XCEditor/Viewport`:viewport slot/shell/input bridge,只处理 UI 容器和输入桥。
|
||||
- `app/Features/Scene/SceneViewportController.*`:场景视图产品交互,处理 Q/W/E/R 工具切换、F 聚焦、鼠标导航、scene icon picking、transform gizmo。
|
||||
- `app/Core/Scene/EditorSceneRuntime.*`:scene selection、editor camera、transform undo/redo、component mutation 和 scene render request 的事实来源。
|
||||
- `app/Rendering/Viewport/ViewportHostService.*`:离屏 viewport 资源管理和 renderer 注册。
|
||||
- `app/Rendering/Viewport/SceneViewportRenderService.*`:调用 engine `SceneRenderer`,插入 grid、selection outline、selected helpers、object-id 等 editor pass。
|
||||
- object-id picking 依赖有效 object-id surface 和 frame serial,修改时要覆盖 `test_viewport_object_id_picker.cpp` 及相关 viewport render plan 测试。
|
||||
|
||||
当前构建和验证入口:
|
||||
|
||||
```powershell
|
||||
cmake --build build --config Debug --target XCUIEditor
|
||||
cmake --build build --config Debug --target XCEditorCore
|
||||
cmake --build build --config Debug --target XCEditor
|
||||
cmake --build build --config Debug --target editor_ui_tests
|
||||
cmake --build build --config Debug --target editor_app_core_tests
|
||||
cmake --build build --config Debug --target editor_app_feature_tests
|
||||
cmake --build build --config Debug --target editor_windowing_phase1_tests
|
||||
cmake --build build --config Debug --target editor_ui_smoke_targets
|
||||
cmake --build build --config Debug --target editor_ui_manual_validation_scenarios
|
||||
```
|
||||
|
||||
```powershell
|
||||
ctest --test-dir build -C Debug -R "editor|xceditor" --output-on-failure
|
||||
```
|
||||
|
||||
- `XCUIEditor` widget/shell/workspace 改动:跑 `editor_ui_tests`、`editor_windowing_phase1_tests`。
|
||||
- `app/Core`、project/scene/session/command 改动:跑 `editor_app_core_tests`。
|
||||
- scene viewport、project panel、window input routing 改动:跑 `editor_app_feature_tests`。
|
||||
- Win32/D3D12 host 或启动流程改动:跑 `editor_ui_smoke_targets`,必要时跑 `xceditor_smoke`。
|
||||
- 手动 UI 场景:跑 `editor_ui_manual_validation_scenarios`。
|
||||
|
||||
有用环境变量:
|
||||
|
||||
- `XCUIEDITOR_SMOKE_TEST=1`:启用应用自退出 smoke 模式。
|
||||
- `XCUIEDITOR_SMOKE_TEST_FRAME_LIMIT=<n>`:smoke 模式帧数上限。
|
||||
- `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=<n>`:smoke 模式持续时间。
|
||||
- `XCUI_AUTO_CAPTURE_ON_STARTUP=1`:启动后自动截图。
|
||||
|
||||
## 过去执行
|
||||
|
||||
- 已把 editor 产品装配的 panel 声明收敛到 `app/Core/Product/EditorProductManifest.*`,并让 panel registry、View > Panels 菜单、panel 激活命令、action route、workspace runtime set 和 viewport renderer 注册从 manifest 派生。
|
||||
- 已把 `game` panel 明确标成 placeholder viewport,而不是隐式共享 scene renderer 或假装 Game runtime 已完成。
|
||||
- 已新增 manifest validation 测试,确保产品 manifest 能声明 panel runtime owner 和 viewport renderer owner,并覆盖 `game` placeholder 的预期状态。
|
||||
- 已更新根 `AGENT.md` 和本文件,去掉旧 `--project` 说法,记录当前 XCUI editor、`XCEditorCore` 分层和 product manifest 规则。
|
||||
- 本次改动验证过:
|
||||
- `cmake --build build --config Debug --target XCEditor`
|
||||
- `cmake --build build --config Debug --target editor_app_core_tests`
|
||||
- `.\build\tests\UI\Editor\unit\Debug\editor_app_core_tests.exe --gtest_filter=EditorShellAssetValidationTest.*`
|
||||
- `cmake --build build --config Debug --target editor_app_feature_tests`
|
||||
- `.\build\tests\UI\Editor\unit\Debug\editor_app_feature_tests.exe`
|
||||
@@ -215,6 +215,7 @@ set(XCUI_EDITOR_HOST_RENDERING_SOURCES
|
||||
|
||||
if(XCENGINE_BUILD_XCUI_EDITOR_CORE)
|
||||
set(XCUI_EDITOR_APP_CORE_CONTRACT_SOURCES
|
||||
app/Core/Product/EditorProductManifest.cpp
|
||||
app/Core/UtilityWindows/EditorUtilityWindowRegistry.cpp
|
||||
app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.cpp
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include "Panels/EditorPanelIds.h"
|
||||
#include "Product/EditorProductManifest.h"
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
#include "Assets/EditorIconService.h"
|
||||
|
||||
@@ -134,6 +135,10 @@ UIEditorCommandDescriptor BuildWorkspaceCommand(
|
||||
return command;
|
||||
}
|
||||
|
||||
std::string BuildPanelActivationCommandId(std::string_view panelId) {
|
||||
return std::string("view.activate_") + std::string(panelId);
|
||||
}
|
||||
|
||||
UIShortcutBinding BuildBinding(
|
||||
std::string commandId,
|
||||
std::int32_t keyCode,
|
||||
@@ -187,38 +192,16 @@ UIEditorCommandRegistry BuildEditorCommandRegistry() {
|
||||
BuildWorkspaceCommand(
|
||||
"view.reset_layout",
|
||||
"Reset Layout",
|
||||
UIEditorWorkspaceCommandKind::ResetWorkspace),
|
||||
BuildWorkspaceCommand(
|
||||
"view.activate_hierarchy",
|
||||
"Hierarchy",
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(kHierarchyPanelId)),
|
||||
BuildWorkspaceCommand(
|
||||
"view.activate_scene",
|
||||
"Scene",
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(kScenePanelId)),
|
||||
BuildWorkspaceCommand(
|
||||
"view.activate_game",
|
||||
"Game",
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(kGamePanelId)),
|
||||
BuildWorkspaceCommand(
|
||||
"view.activate_inspector",
|
||||
"Inspector",
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(kInspectorPanelId)),
|
||||
BuildWorkspaceCommand(
|
||||
"view.activate_console",
|
||||
"Console",
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(kConsolePanelId)),
|
||||
BuildWorkspaceCommand(
|
||||
"view.activate_project",
|
||||
"Project",
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(kProjectPanelId))
|
||||
UIEditorWorkspaceCommandKind::ResetWorkspace)
|
||||
};
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
registry.commands.push_back(
|
||||
BuildWorkspaceCommand(
|
||||
BuildPanelActivationCommandId(panel.panelId),
|
||||
std::string(panel.defaultTitle),
|
||||
UIEditorWorkspaceCommandKind::ActivatePanel,
|
||||
std::string(panel.panelId)));
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
@@ -250,39 +233,22 @@ namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
UIEditorPanelDescriptor BuildHostedContentPanelDescriptor(
|
||||
std::string_view panelId,
|
||||
std::string_view title,
|
||||
bool placeholder,
|
||||
bool canHide,
|
||||
bool canClose) {
|
||||
UIEditorPanelDescriptor BuildProductPanelDescriptor(
|
||||
const EditorProductPanelDescriptor& productPanel) {
|
||||
UIEditorPanelDescriptor descriptor = {};
|
||||
descriptor.panelId = std::string(panelId);
|
||||
descriptor.defaultTitle = std::string(title);
|
||||
descriptor.presentationKind = UIEditorPanelPresentationKind::HostedContent;
|
||||
descriptor.placeholder = placeholder;
|
||||
descriptor.canHide = canHide;
|
||||
descriptor.canClose = canClose;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
UIEditorPanelDescriptor BuildViewportPanelDescriptor(
|
||||
std::string_view panelId,
|
||||
std::string_view title,
|
||||
bool canHide,
|
||||
bool canClose,
|
||||
bool showTopBar,
|
||||
bool showBottomBar) {
|
||||
UIEditorPanelDescriptor descriptor = {};
|
||||
descriptor.panelId = std::string(panelId);
|
||||
descriptor.defaultTitle = std::string(title);
|
||||
descriptor.presentationKind = UIEditorPanelPresentationKind::ViewportShell;
|
||||
descriptor.placeholder = false;
|
||||
descriptor.canHide = canHide;
|
||||
descriptor.canClose = canClose;
|
||||
descriptor.viewportShellSpec.chrome.title = descriptor.defaultTitle;
|
||||
descriptor.viewportShellSpec.chrome.showTopBar = showTopBar;
|
||||
descriptor.viewportShellSpec.chrome.showBottomBar = showBottomBar;
|
||||
descriptor.panelId = std::string(productPanel.panelId);
|
||||
descriptor.defaultTitle = std::string(productPanel.defaultTitle);
|
||||
descriptor.presentationKind = productPanel.presentationKind;
|
||||
descriptor.placeholder = productPanel.placeholder;
|
||||
descriptor.canHide = productPanel.canHide;
|
||||
descriptor.canClose = productPanel.canClose;
|
||||
if (descriptor.presentationKind == UIEditorPanelPresentationKind::ViewportShell) {
|
||||
descriptor.viewportShellSpec.chrome.title = descriptor.defaultTitle;
|
||||
descriptor.viewportShellSpec.chrome.showTopBar =
|
||||
productPanel.showViewportTopBar;
|
||||
descriptor.viewportShellSpec.chrome.showBottomBar =
|
||||
productPanel.showViewportBottomBar;
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@@ -303,14 +269,9 @@ const UIEditorPanelDescriptor& RequirePanelDescriptor(
|
||||
|
||||
UIEditorPanelRegistry BuildEditorPanelRegistry() {
|
||||
UIEditorPanelRegistry registry = {};
|
||||
registry.panels = {
|
||||
BuildHostedContentPanelDescriptor(kHierarchyPanelId, kHierarchyPanelTitle, true, false, false),
|
||||
BuildViewportPanelDescriptor(kScenePanelId, kScenePanelTitle, false, false, false, false),
|
||||
BuildViewportPanelDescriptor(kGamePanelId, kGamePanelTitle, false, false, false, false),
|
||||
BuildHostedContentPanelDescriptor(kInspectorPanelId, kInspectorPanelTitle, true, false, false),
|
||||
BuildHostedContentPanelDescriptor(kConsolePanelId, kConsolePanelTitle, true, false, false),
|
||||
BuildHostedContentPanelDescriptor(kProjectPanelId, kProjectPanelTitle, false, false, false)
|
||||
};
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
registry.panels.push_back(BuildProductPanelDescriptor(panel));
|
||||
}
|
||||
return registry;
|
||||
}
|
||||
|
||||
@@ -498,30 +459,20 @@ UIEditorMenuModel BuildEditorMenuModel() {
|
||||
BuildCommandItem("scripts-rebuild", "Rebuild Script Assemblies", "scripts.rebuild")
|
||||
};
|
||||
|
||||
UIEditorMenuCheckedStateBinding hierarchyActive = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(kHierarchyPanelId)
|
||||
};
|
||||
UIEditorMenuCheckedStateBinding sceneActive = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(kScenePanelId)
|
||||
};
|
||||
UIEditorMenuCheckedStateBinding gameActive = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(kGamePanelId)
|
||||
};
|
||||
UIEditorMenuCheckedStateBinding inspectorActive = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(kInspectorPanelId)
|
||||
};
|
||||
UIEditorMenuCheckedStateBinding consoleActive = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(kConsolePanelId)
|
||||
};
|
||||
UIEditorMenuCheckedStateBinding projectActive = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(kProjectPanelId)
|
||||
};
|
||||
std::vector<UIEditorMenuItemDescriptor> panelMenuItems = {};
|
||||
panelMenuItems.reserve(GetEditorProductPanels().size());
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
UIEditorMenuCheckedStateBinding activeBinding = {
|
||||
UIEditorMenuCheckedStateSource::PanelActive,
|
||||
std::string(panel.panelId)
|
||||
};
|
||||
panelMenuItems.push_back(
|
||||
BuildCommandItem(
|
||||
std::string("view-panel-") + std::string(panel.panelId),
|
||||
std::string(panel.defaultTitle),
|
||||
BuildPanelActivationCommandId(panel.panelId),
|
||||
std::move(activeBinding)));
|
||||
}
|
||||
|
||||
UIEditorMenuDescriptor viewMenu = {};
|
||||
viewMenu.menuId = "view";
|
||||
@@ -532,14 +483,7 @@ UIEditorMenuModel BuildEditorMenuModel() {
|
||||
BuildSubmenuItem(
|
||||
"view-panels",
|
||||
"Panels",
|
||||
{
|
||||
BuildCommandItem("view-panel-hierarchy", "Hierarchy", "view.activate_hierarchy", hierarchyActive),
|
||||
BuildCommandItem("view-panel-scene", "Scene", "view.activate_scene", sceneActive),
|
||||
BuildCommandItem("view-panel-game", "Game", "view.activate_game", gameActive),
|
||||
BuildCommandItem("view-panel-inspector", "Inspector", "view.activate_inspector", inspectorActive),
|
||||
BuildCommandItem("view-panel-console", "Console", "view.activate_console", consoleActive),
|
||||
BuildCommandItem("view-panel-project", "Project", "view.activate_project", projectActive)
|
||||
})
|
||||
std::move(panelMenuItems))
|
||||
};
|
||||
|
||||
UIEditorMenuDescriptor helpMenu = {};
|
||||
|
||||
@@ -18,8 +18,6 @@ inline constexpr std::string_view kInspectorPanelTitle = "Inspector";
|
||||
inline constexpr std::string_view kConsolePanelTitle = "Console";
|
||||
inline constexpr std::string_view kProjectPanelTitle = "Project";
|
||||
|
||||
[[nodiscard]] constexpr bool IsEditorViewportPanelId(std::string_view panelId) {
|
||||
return panelId == kScenePanelId || panelId == kGamePanelId;
|
||||
}
|
||||
[[nodiscard]] bool IsEditorViewportPanelId(std::string_view panelId);
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
201
editor/app/Core/Product/EditorProductManifest.cpp
Normal file
201
editor/app/Core/Product/EditorProductManifest.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "Product/EditorProductManifest.h"
|
||||
|
||||
#include "Panels/EditorPanelIds.h"
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<EditorProductPanelDescriptor, 6> kEditorProductPanels = {
|
||||
EditorProductPanelDescriptor{
|
||||
kHierarchyPanelId,
|
||||
kHierarchyPanelTitle,
|
||||
UIEditorPanelPresentationKind::HostedContent,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
EditorActionRoute::Hierarchy,
|
||||
EditorProductPanelRuntimeKind::Hierarchy,
|
||||
EditorProductViewportRendererKind::None,
|
||||
{} },
|
||||
EditorProductPanelDescriptor{
|
||||
kScenePanelId,
|
||||
kScenePanelTitle,
|
||||
UIEditorPanelPresentationKind::ViewportShell,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
EditorActionRoute::Scene,
|
||||
EditorProductPanelRuntimeKind::Scene,
|
||||
EditorProductViewportRendererKind::Scene,
|
||||
{} },
|
||||
EditorProductPanelDescriptor{
|
||||
kGamePanelId,
|
||||
kGamePanelTitle,
|
||||
UIEditorPanelPresentationKind::ViewportShell,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
EditorActionRoute::Game,
|
||||
EditorProductPanelRuntimeKind::None,
|
||||
EditorProductViewportRendererKind::Placeholder,
|
||||
"Game view runtime is not implemented" },
|
||||
EditorProductPanelDescriptor{
|
||||
kInspectorPanelId,
|
||||
kInspectorPanelTitle,
|
||||
UIEditorPanelPresentationKind::HostedContent,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
EditorActionRoute::Inspector,
|
||||
EditorProductPanelRuntimeKind::Inspector,
|
||||
EditorProductViewportRendererKind::None,
|
||||
{} },
|
||||
EditorProductPanelDescriptor{
|
||||
kConsolePanelId,
|
||||
kConsolePanelTitle,
|
||||
UIEditorPanelPresentationKind::HostedContent,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
EditorActionRoute::Console,
|
||||
EditorProductPanelRuntimeKind::Console,
|
||||
EditorProductViewportRendererKind::None,
|
||||
{} },
|
||||
EditorProductPanelDescriptor{
|
||||
kProjectPanelId,
|
||||
kProjectPanelTitle,
|
||||
UIEditorPanelPresentationKind::HostedContent,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
EditorActionRoute::Project,
|
||||
EditorProductPanelRuntimeKind::Project,
|
||||
EditorProductViewportRendererKind::None,
|
||||
{} },
|
||||
};
|
||||
|
||||
EditorProductManifestValidationResult BuildManifestError(
|
||||
EditorProductManifestValidationCode code,
|
||||
std::string_view message) {
|
||||
EditorProductManifestValidationResult result = {};
|
||||
result.code = code;
|
||||
result.message = std::string(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::span<const EditorProductPanelDescriptor> GetEditorProductPanels() {
|
||||
return kEditorProductPanels;
|
||||
}
|
||||
|
||||
const EditorProductPanelDescriptor* FindEditorProductPanel(
|
||||
std::string_view panelId) {
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
if (panel.panelId == panelId) {
|
||||
return &panel;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsEditorProductViewportPanel(std::string_view panelId) {
|
||||
const EditorProductPanelDescriptor* panel = FindEditorProductPanel(panelId);
|
||||
return panel != nullptr &&
|
||||
panel->presentationKind == UIEditorPanelPresentationKind::ViewportShell;
|
||||
}
|
||||
|
||||
EditorProductManifestValidationResult ValidateEditorProductManifest() {
|
||||
const std::span<const EditorProductPanelDescriptor> panels =
|
||||
GetEditorProductPanels();
|
||||
for (std::size_t index = 0u; index < panels.size(); ++index) {
|
||||
const EditorProductPanelDescriptor& panel = panels[index];
|
||||
if (panel.panelId.empty()) {
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::EmptyPanelId,
|
||||
"Editor product manifest contains a panel with an empty id.");
|
||||
}
|
||||
if (panel.defaultTitle.empty()) {
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::EmptyPanelTitle,
|
||||
"Editor product manifest contains a panel with an empty title.");
|
||||
}
|
||||
|
||||
for (std::size_t other = index + 1u; other < panels.size(); ++other) {
|
||||
if (panels[other].panelId == panel.panelId) {
|
||||
std::ostringstream message = {};
|
||||
message << "Editor product manifest duplicates panel id '"
|
||||
<< panel.panelId << "'.";
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::DuplicatePanelId,
|
||||
message.str());
|
||||
}
|
||||
}
|
||||
|
||||
if (panel.presentationKind == UIEditorPanelPresentationKind::HostedContent &&
|
||||
panel.runtimeKind == EditorProductPanelRuntimeKind::None) {
|
||||
std::ostringstream message = {};
|
||||
message << "Hosted panel '" << panel.panelId
|
||||
<< "' has no runtime owner.";
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::HostedPanelMissingRuntime,
|
||||
message.str());
|
||||
}
|
||||
|
||||
if (panel.presentationKind == UIEditorPanelPresentationKind::ViewportShell &&
|
||||
panel.viewportRendererKind == EditorProductViewportRendererKind::None) {
|
||||
std::ostringstream message = {};
|
||||
message << "Viewport panel '" << panel.panelId
|
||||
<< "' has no viewport renderer owner.";
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::ViewportPanelMissingRenderer,
|
||||
message.str());
|
||||
}
|
||||
|
||||
if (panel.presentationKind != UIEditorPanelPresentationKind::ViewportShell &&
|
||||
panel.viewportRendererKind != EditorProductViewportRendererKind::None) {
|
||||
std::ostringstream message = {};
|
||||
message << "Non-viewport panel '" << panel.panelId
|
||||
<< "' declares a viewport renderer.";
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::NonViewportPanelHasRenderer,
|
||||
message.str());
|
||||
}
|
||||
|
||||
if (panel.viewportRendererKind ==
|
||||
EditorProductViewportRendererKind::Placeholder &&
|
||||
panel.viewportPlaceholderStatus.empty()) {
|
||||
std::ostringstream message = {};
|
||||
message << "Placeholder viewport panel '" << panel.panelId
|
||||
<< "' has no status text.";
|
||||
return BuildManifestError(
|
||||
EditorProductManifestValidationCode::PlaceholderViewportMissingStatus,
|
||||
message.str());
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsEditorViewportPanelId(std::string_view panelId) {
|
||||
return IsEditorProductViewportPanel(panelId);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
77
editor/app/Core/Product/EditorProductManifest.h
Normal file
77
editor/app/Core/Product/EditorProductManifest.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include "State/EditorSession.h"
|
||||
|
||||
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
enum class EditorProductPanelRuntimeKind : std::uint8_t {
|
||||
None = 0,
|
||||
Console,
|
||||
Hierarchy,
|
||||
Inspector,
|
||||
Project,
|
||||
Scene
|
||||
};
|
||||
|
||||
enum class EditorProductViewportRendererKind : std::uint8_t {
|
||||
None = 0,
|
||||
Scene,
|
||||
Placeholder
|
||||
};
|
||||
|
||||
struct EditorProductPanelDescriptor {
|
||||
std::string_view panelId = {};
|
||||
std::string_view defaultTitle = {};
|
||||
UIEditorPanelPresentationKind presentationKind =
|
||||
UIEditorPanelPresentationKind::Placeholder;
|
||||
bool placeholder = true;
|
||||
bool canHide = true;
|
||||
bool canClose = true;
|
||||
bool showViewportTopBar = false;
|
||||
bool showViewportBottomBar = false;
|
||||
EditorActionRoute actionRoute = EditorActionRoute::None;
|
||||
EditorProductPanelRuntimeKind runtimeKind =
|
||||
EditorProductPanelRuntimeKind::None;
|
||||
EditorProductViewportRendererKind viewportRendererKind =
|
||||
EditorProductViewportRendererKind::None;
|
||||
std::string_view viewportPlaceholderStatus = {};
|
||||
};
|
||||
|
||||
enum class EditorProductManifestValidationCode : std::uint8_t {
|
||||
None = 0,
|
||||
EmptyPanelId,
|
||||
EmptyPanelTitle,
|
||||
DuplicatePanelId,
|
||||
HostedPanelMissingRuntime,
|
||||
ViewportPanelMissingRenderer,
|
||||
NonViewportPanelHasRenderer,
|
||||
PlaceholderViewportMissingStatus
|
||||
};
|
||||
|
||||
struct EditorProductManifestValidationResult {
|
||||
EditorProductManifestValidationCode code =
|
||||
EditorProductManifestValidationCode::None;
|
||||
std::string message = {};
|
||||
|
||||
[[nodiscard]] bool IsValid() const {
|
||||
return code == EditorProductManifestValidationCode::None;
|
||||
}
|
||||
};
|
||||
|
||||
std::span<const EditorProductPanelDescriptor> GetEditorProductPanels();
|
||||
|
||||
const EditorProductPanelDescriptor* FindEditorProductPanel(
|
||||
std::string_view panelId);
|
||||
|
||||
bool IsEditorProductViewportPanel(std::string_view panelId);
|
||||
|
||||
EditorProductManifestValidationResult ValidateEditorProductManifest();
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "State/EditorSession.h"
|
||||
|
||||
#include "Panels/EditorPanelIds.h"
|
||||
#include "Product/EditorProductManifest.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
|
||||
@@ -52,23 +52,10 @@ std::string_view GetEditorSelectionKindName(EditorSelectionKind kind) {
|
||||
}
|
||||
|
||||
EditorActionRoute ResolveEditorActionRoute(std::string_view panelId) {
|
||||
if (panelId == kHierarchyPanelId) {
|
||||
return EditorActionRoute::Hierarchy;
|
||||
}
|
||||
if (panelId == kProjectPanelId) {
|
||||
return EditorActionRoute::Project;
|
||||
}
|
||||
if (panelId == kInspectorPanelId) {
|
||||
return EditorActionRoute::Inspector;
|
||||
}
|
||||
if (panelId == kConsolePanelId) {
|
||||
return EditorActionRoute::Console;
|
||||
}
|
||||
if (panelId == kScenePanelId) {
|
||||
return EditorActionRoute::Scene;
|
||||
}
|
||||
if (panelId == kGamePanelId) {
|
||||
return EditorActionRoute::Game;
|
||||
if (const EditorProductPanelDescriptor* panel =
|
||||
FindEditorProductPanel(panelId);
|
||||
panel != nullptr) {
|
||||
return panel->actionRoute;
|
||||
}
|
||||
return EditorActionRoute::None;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Project/ProjectPanel.h"
|
||||
#include "Scene/SceneEditCommandRoute.h"
|
||||
#include "Scene/SceneViewportFeature.h"
|
||||
#include "Product/EditorProductManifest.h"
|
||||
|
||||
#include <XCEditor/Panels/UIEditorHostedPanelDispatch.h>
|
||||
|
||||
@@ -449,15 +450,36 @@ private:
|
||||
SceneEditCommandRoute m_commandRoute = {};
|
||||
};
|
||||
|
||||
std::unique_ptr<EditorWorkspacePanel> CreateWorkspacePanelRuntime(
|
||||
const EditorProductPanelDescriptor& panel) {
|
||||
switch (panel.runtimeKind) {
|
||||
case EditorProductPanelRuntimeKind::Console:
|
||||
return std::make_unique<ConsoleWorkspacePanel>();
|
||||
case EditorProductPanelRuntimeKind::Hierarchy:
|
||||
return std::make_unique<HierarchyWorkspacePanel>();
|
||||
case EditorProductPanelRuntimeKind::Inspector:
|
||||
return std::make_unique<InspectorWorkspacePanel>();
|
||||
case EditorProductPanelRuntimeKind::Project:
|
||||
return std::make_unique<ProjectWorkspacePanel>();
|
||||
case EditorProductPanelRuntimeKind::Scene:
|
||||
return std::make_unique<SceneWorkspacePanel>();
|
||||
case EditorProductPanelRuntimeKind::None:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet() {
|
||||
EditorWorkspacePanelRuntimeSet panels = {};
|
||||
panels.AddPanel(std::make_unique<ConsoleWorkspacePanel>());
|
||||
panels.AddPanel(std::make_unique<HierarchyWorkspacePanel>());
|
||||
panels.AddPanel(std::make_unique<InspectorWorkspacePanel>());
|
||||
panels.AddPanel(std::make_unique<ProjectWorkspacePanel>());
|
||||
panels.AddPanel(std::make_unique<SceneWorkspacePanel>());
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
if (std::unique_ptr<EditorWorkspacePanel> runtime =
|
||||
CreateWorkspacePanelRuntime(panel);
|
||||
runtime != nullptr) {
|
||||
panels.AddPanel(std::move(runtime));
|
||||
}
|
||||
}
|
||||
return panels;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ViewportHostService.h"
|
||||
|
||||
#include "Panels/EditorPanelIds.h"
|
||||
#include "Product/EditorProductManifest.h"
|
||||
#include "Viewport/SceneViewportResourcePaths.h"
|
||||
#include "ViewportRenderHost.h"
|
||||
|
||||
@@ -14,6 +15,30 @@ namespace {
|
||||
|
||||
using ::XCEngine::RHI::ResourceStates;
|
||||
|
||||
class PlaceholderViewportContentRenderer final : public IViewportContentRenderer {
|
||||
public:
|
||||
explicit PlaceholderViewportContentRenderer(std::string_view statusText)
|
||||
: m_statusText(statusText) {}
|
||||
|
||||
ViewportRenderResult Render(
|
||||
ViewportRenderTargets&,
|
||||
::XCEngine::RHI::RHIDevice&,
|
||||
const ::XCEngine::Rendering::RenderContext&) override {
|
||||
ViewportRenderResult result = {};
|
||||
result.rendered = false;
|
||||
result.requiresFallbackClear = true;
|
||||
result.statusText = m_statusText;
|
||||
result.fallbackClearR = 0.07f;
|
||||
result.fallbackClearG = 0.08f;
|
||||
result.fallbackClearB = 0.10f;
|
||||
result.fallbackClearA = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_statusText = {};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ViewportHostService::ViewportHostService() = default;
|
||||
@@ -22,10 +47,33 @@ ViewportHostService::~ViewportHostService() = default;
|
||||
|
||||
void ViewportHostService::Initialize(const std::filesystem::path& repoRoot) {
|
||||
m_sceneViewportRuntime.Initialize(BuildSceneViewportShaderPaths(repoRoot));
|
||||
SetContentRenderer(
|
||||
kScenePanelId,
|
||||
&m_sceneViewportRuntime,
|
||||
SceneViewportRenderService::GetViewportResourceRequirements());
|
||||
m_placeholderRenderers.clear();
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
if (panel.presentationKind != UIEditorPanelPresentationKind::ViewportShell) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (panel.viewportRendererKind) {
|
||||
case EditorProductViewportRendererKind::Scene:
|
||||
SetContentRenderer(
|
||||
panel.panelId,
|
||||
&m_sceneViewportRuntime,
|
||||
SceneViewportRenderService::GetViewportResourceRequirements());
|
||||
break;
|
||||
case EditorProductViewportRendererKind::Placeholder: {
|
||||
auto placeholder =
|
||||
std::make_unique<PlaceholderViewportContentRenderer>(
|
||||
panel.viewportPlaceholderStatus);
|
||||
SetContentRenderer(panel.panelId, placeholder.get(), {});
|
||||
m_placeholderRenderers.push_back(std::move(placeholder));
|
||||
break;
|
||||
}
|
||||
case EditorProductViewportRendererKind::None:
|
||||
default:
|
||||
SetContentRenderer(panel.panelId, nullptr, {});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportHostService::AttachWindowRenderer(
|
||||
@@ -60,7 +108,12 @@ void ViewportHostService::SetContentRenderer(
|
||||
}
|
||||
|
||||
void ViewportHostService::Shutdown() {
|
||||
SetContentRenderer(kScenePanelId, nullptr, {});
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
if (panel.presentationKind == UIEditorPanelPresentationKind::ViewportShell) {
|
||||
SetContentRenderer(panel.panelId, nullptr, {});
|
||||
}
|
||||
}
|
||||
m_placeholderRenderers.clear();
|
||||
m_sceneViewportRuntime.Shutdown();
|
||||
for (auto& [viewportId, entry] : m_entries) {
|
||||
DestroyViewportEntry(entry);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
@@ -82,6 +83,7 @@ private:
|
||||
bool m_surfacePresentationEnabled = false;
|
||||
std::unordered_map<std::string, ViewportEntry> m_entries = {};
|
||||
std::vector<std::vector<ViewportRenderTargets>> m_retiredTargetsBySlot = {};
|
||||
std::vector<std::unique_ptr<IViewportContentRenderer>> m_placeholderRenderers = {};
|
||||
SceneViewportRenderService m_sceneViewportRuntime = {};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "EditorShellAssetBuilder.h"
|
||||
#include "Panels/EditorPanelIds.h"
|
||||
#include "Product/EditorProductManifest.h"
|
||||
|
||||
#include <XCEditor/Shell/UIEditorShellAsset.h>
|
||||
|
||||
@@ -10,6 +12,13 @@ namespace {
|
||||
|
||||
using XCEngine::Input::KeyCode;
|
||||
using XCEngine::UI::Editor::App::BuildEditorApplicationShellAsset;
|
||||
using XCEngine::UI::Editor::App::EditorProductManifestValidationCode;
|
||||
using XCEngine::UI::Editor::App::EditorProductPanelRuntimeKind;
|
||||
using XCEngine::UI::Editor::App::EditorProductViewportRendererKind;
|
||||
using XCEngine::UI::Editor::App::FindEditorProductPanel;
|
||||
using XCEngine::UI::Editor::App::GetEditorProductPanels;
|
||||
using XCEngine::UI::Editor::App::kGamePanelId;
|
||||
using XCEngine::UI::Editor::App::ValidateEditorProductManifest;
|
||||
using XCEngine::UI::Editor::EditorShellAssetValidationCode;
|
||||
using XCEngine::UI::Editor::FindUIEditorPanelDescriptor;
|
||||
using XCEngine::UI::Editor::UIEditorCommandPanelSource;
|
||||
@@ -64,6 +73,24 @@ TEST(EditorShellAssetValidationTest, DefaultShellAssetPassesValidation) {
|
||||
shellAsset.panelRegistry.panels.front().presentationKind);
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ProductManifestDeclaresPanelRuntimeAndViewportOwners) {
|
||||
const auto validation = ValidateEditorProductManifest();
|
||||
EXPECT_EQ(validation.code, EditorProductManifestValidationCode::None)
|
||||
<< validation.message;
|
||||
ASSERT_TRUE(validation.IsValid());
|
||||
|
||||
const auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
ASSERT_EQ(shellAsset.panelRegistry.panels.size(), GetEditorProductPanels().size());
|
||||
|
||||
const auto* gamePanel = FindEditorProductPanel(kGamePanelId);
|
||||
ASSERT_NE(gamePanel, nullptr);
|
||||
EXPECT_EQ(gamePanel->runtimeKind, EditorProductPanelRuntimeKind::None);
|
||||
EXPECT_EQ(
|
||||
gamePanel->viewportRendererKind,
|
||||
EditorProductViewportRendererKind::Placeholder);
|
||||
EXPECT_FALSE(gamePanel->viewportPlaceholderStatus.empty());
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspacePanelMissingFromRegistry) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user