editor: explicit runtime path contract
This commit is contained in:
@@ -12,18 +12,19 @@
|
||||
- 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。
|
||||
- 运行时路径以 `app/Core/Environment/EditorRuntimePaths.h` 为显式契约。workspace、executable、resource、project、capture 根路径由 bootstrap 一次性解析并向下传递;不要在下游重新从 repo root、当前工作目录或文档文件推导运行环境。
|
||||
- 资源路径、图标、shader、截图输出都应走明确服务或 host 接口。不要硬编码从当前工作目录猜路径;手动验证截图不得写回 source tree。
|
||||
|
||||
## 当前情况
|
||||
|
||||
- `editor/AGENTS.md` 本身被 `app/Bootstrap/Application.cpp` 的 `HasEditorRepoMarkers()` 用作仓库根定位标记之一。不要重命名、删除或移动它;如果根定位规则变了,同时更新本文。
|
||||
- `editor/AGENTS.md` 只是协作说明,不是运行时定位标记。`Application::ResolveRuntimePaths()` 当前用稳定工程标记(根 `CMakeLists.txt`、`editor/resources`、`project`)定位开发 workspace,并填充 `EditorRuntimePaths`。
|
||||
- 顶层 `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>` 说法。
|
||||
- 当前应用没有命令行项目选择流程;默认项目根由 `EditorRuntimePaths.projectRoot` 提供,开发 workspace 下通常是 `<workspaceRoot>/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 都在这里串接。
|
||||
- `EditorContext` 是产品级状态聚合点:`EditorSession`、project runtime、scene runtime、selection、command focus、utility window request、shortcut manager 和 host command bridge 都在这里串接;初始化只接收 `EditorRuntimePaths`,不再自行拼接 repo/project/resource 路径。
|
||||
- `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 内维护另一套长期选择真相。
|
||||
@@ -41,6 +42,7 @@
|
||||
- `include/XCEditor/Windowing`:多窗口 workspace 状态、同步计划和 presentation policy。
|
||||
- `src/**`:对应公共头的实现。保持偏纯函数/状态机风格。
|
||||
- `app/Core`:产品级 contracts、session、command focus、selection、panel services、scene/project/viewport/windowing 接口。
|
||||
- `app/Core/Environment`:运行时路径契约。bootstrap 负责解析 `EditorRuntimePaths`,core/composition/features/rendering 只消费显式路径。
|
||||
- `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`:产品面板与场景视图工具。
|
||||
@@ -55,6 +57,7 @@
|
||||
wWinMain
|
||||
-> RunXCEditor
|
||||
-> Application::Initialize / Run
|
||||
-> Application::ResolveRuntimePaths
|
||||
-> EditorContext::Initialize
|
||||
-> BuildEditorApplicationShellAsset
|
||||
-> EditorWindowSystem::BootstrapPrimaryWindow
|
||||
|
||||
@@ -35,11 +35,48 @@ constexpr const wchar_t* kWindowTitle = L"Main Scene * - Main.xx - XCEngine Edit
|
||||
constexpr DWORD kBorderlessWindowStyle = WS_POPUP | WS_THICKFRAME;
|
||||
constexpr int kDefaultSmokeTestDurationSeconds = 12;
|
||||
|
||||
bool HasEditorRepoMarkers(const std::filesystem::path& root) {
|
||||
return std::filesystem::exists(root / "editor" / "AGENTS.md") &&
|
||||
bool HasEditorWorkspaceMarkers(const std::filesystem::path& root) {
|
||||
return std::filesystem::exists(root / "CMakeLists.txt") &&
|
||||
std::filesystem::exists(root / "editor" / "resources") &&
|
||||
std::filesystem::exists(root / "project");
|
||||
}
|
||||
|
||||
std::filesystem::path NormalizePath(const std::filesystem::path& path) {
|
||||
std::error_code errorCode = {};
|
||||
std::filesystem::path normalized =
|
||||
std::filesystem::weakly_canonical(path, errorCode);
|
||||
return errorCode
|
||||
? path.lexically_normal()
|
||||
: normalized.lexically_normal();
|
||||
}
|
||||
|
||||
App::EditorRuntimePaths BuildEditorRuntimePaths(
|
||||
const std::filesystem::path& workspaceRoot,
|
||||
const std::filesystem::path& executableDirectory) {
|
||||
App::EditorRuntimePaths paths = {};
|
||||
paths.workspaceRoot = NormalizePath(workspaceRoot);
|
||||
paths.executableRoot = NormalizePath(executableDirectory);
|
||||
|
||||
const std::filesystem::path sourceTreeResourceRoot =
|
||||
paths.workspaceRoot / "editor" / "resources";
|
||||
const std::filesystem::path packagedResourceRoot =
|
||||
paths.executableRoot / "resources";
|
||||
paths.resourceRoot = std::filesystem::exists(sourceTreeResourceRoot)
|
||||
? NormalizePath(sourceTreeResourceRoot)
|
||||
: NormalizePath(packagedResourceRoot);
|
||||
|
||||
const std::filesystem::path sourceTreeProjectRoot =
|
||||
paths.workspaceRoot / "project";
|
||||
const std::filesystem::path packagedProjectRoot =
|
||||
paths.executableRoot / "project";
|
||||
paths.projectRoot = std::filesystem::exists(sourceTreeProjectRoot)
|
||||
? NormalizePath(sourceTreeProjectRoot)
|
||||
: NormalizePath(packagedProjectRoot);
|
||||
|
||||
paths.captureRoot = NormalizePath(paths.executableRoot / "captures");
|
||||
return paths;
|
||||
}
|
||||
|
||||
void EnableDpiAwareness() {
|
||||
const HMODULE user32 = GetModuleHandleW(L"user32.dll");
|
||||
if (user32 != nullptr) {
|
||||
@@ -118,7 +155,7 @@ namespace XCEngine::UI::Editor {
|
||||
bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
m_hInstance = hInstance;
|
||||
m_resourceService = std::make_unique<Host::Win32EditorResourceService>(m_hInstance);
|
||||
m_repoRoot = ResolveRepoRootPath(m_resourceService->GetExecutableDirectory());
|
||||
m_runtimePaths = ResolveRuntimePaths(m_resourceService->GetExecutableDirectory());
|
||||
EnableDpiAwareness();
|
||||
|
||||
const std::filesystem::path logRoot = m_resourceService->GetExecutableDirectory() / "logs";
|
||||
@@ -126,7 +163,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
SetUnhandledExceptionFilter(&Application::HandleUnhandledException);
|
||||
AppendUIEditorRuntimeTrace("app", "initialize begin");
|
||||
|
||||
if (!m_editorContext->Initialize(m_repoRoot)) {
|
||||
if (!m_editorContext->Initialize(m_runtimePaths)) {
|
||||
AppendUIEditorRuntimeTrace(
|
||||
"app",
|
||||
"shell asset validation failed: " + m_editorContext->GetValidationMessage());
|
||||
@@ -149,8 +186,7 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
hostConfig.windowUserData = this;
|
||||
m_windowHostRuntime = std::make_unique<App::EditorWindowHostRuntime>(
|
||||
hostConfig,
|
||||
m_repoRoot,
|
||||
m_editorContext->GetShellAsset().captureRootPath);
|
||||
m_runtimePaths);
|
||||
m_renderRuntimeFactory =
|
||||
std::make_unique<Host::D3D12EditorWindowRenderRuntimeFactory>();
|
||||
App::EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory = []() {
|
||||
@@ -270,18 +306,13 @@ void Application::Shutdown() {
|
||||
ShutdownUIEditorRuntimeTrace();
|
||||
}
|
||||
|
||||
std::filesystem::path Application::ResolveRepoRootPath(
|
||||
App::EditorRuntimePaths Application::ResolveRuntimePaths(
|
||||
const std::filesystem::path& executableDirectory) {
|
||||
std::error_code errorCode = {};
|
||||
std::filesystem::path current =
|
||||
std::filesystem::weakly_canonical(executableDirectory, errorCode);
|
||||
if (errorCode) {
|
||||
current = executableDirectory.lexically_normal();
|
||||
}
|
||||
std::filesystem::path current = NormalizePath(executableDirectory);
|
||||
|
||||
while (!current.empty()) {
|
||||
if (HasEditorRepoMarkers(current)) {
|
||||
return current.lexically_normal();
|
||||
if (HasEditorWorkspaceMarkers(current)) {
|
||||
return BuildEditorRuntimePaths(current, executableDirectory);
|
||||
}
|
||||
|
||||
const std::filesystem::path parent = current.parent_path();
|
||||
@@ -291,7 +322,7 @@ std::filesystem::path Application::ResolveRepoRootPath(
|
||||
current = parent;
|
||||
}
|
||||
|
||||
return executableDirectory.lexically_normal();
|
||||
return BuildEditorRuntimePaths(executableDirectory, executableDirectory);
|
||||
}
|
||||
|
||||
LONG WINAPI Application::HandleUnhandledException(EXCEPTION_POINTERS* exceptionInfo) {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
@@ -49,13 +51,13 @@ private:
|
||||
bool Initialize(HINSTANCE hInstance, int nCmdShow);
|
||||
void Shutdown();
|
||||
bool RegisterWindowClass();
|
||||
static std::filesystem::path ResolveRepoRootPath(
|
||||
static App::EditorRuntimePaths ResolveRuntimePaths(
|
||||
const std::filesystem::path& executableDirectory);
|
||||
static LONG WINAPI HandleUnhandledException(EXCEPTION_POINTERS* exceptionInfo);
|
||||
|
||||
HINSTANCE m_hInstance = nullptr;
|
||||
ATOM m_windowClassAtom = 0;
|
||||
std::filesystem::path m_repoRoot = {};
|
||||
App::EditorRuntimePaths m_runtimePaths = {};
|
||||
std::chrono::steady_clock::time_point m_smokeTestStartTime = {};
|
||||
std::chrono::milliseconds m_smokeTestDuration = std::chrono::milliseconds::zero();
|
||||
std::unique_ptr<App::EditorContext> m_editorContext = {};
|
||||
|
||||
@@ -52,9 +52,9 @@ UIEditorWorkspacePanelPresentationModel* FindMutablePresentation(
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EditorContext::Initialize(const std::filesystem::path& repoRoot) {
|
||||
bool EditorContext::Initialize(const EditorRuntimePaths& runtimePaths) {
|
||||
AppendUIEditorRuntimeTrace("startup", "EditorContext::Initialize begin");
|
||||
m_shellAsset = BuildEditorApplicationShellAsset(repoRoot);
|
||||
m_shellAsset = BuildEditorApplicationShellAsset(runtimePaths);
|
||||
AppendUIEditorRuntimeTrace("startup", "BuildEditorApplicationShellAsset complete");
|
||||
m_shellValidation = ValidateEditorShellAsset(m_shellAsset);
|
||||
AppendUIEditorRuntimeTrace(
|
||||
@@ -66,13 +66,13 @@ bool EditorContext::Initialize(const std::filesystem::path& repoRoot) {
|
||||
}
|
||||
|
||||
m_session = {};
|
||||
m_session.repoRoot = repoRoot;
|
||||
m_session.projectRoot = (repoRoot / "project").lexically_normal();
|
||||
m_session.workspaceRoot = runtimePaths.workspaceRoot;
|
||||
m_session.projectRoot = runtimePaths.projectRoot;
|
||||
m_commandFocusService = {};
|
||||
m_selectionService = {};
|
||||
m_projectRuntime.Reset();
|
||||
AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize begin");
|
||||
m_projectRuntime.Initialize(repoRoot);
|
||||
m_projectRuntime.Initialize(runtimePaths.projectRoot);
|
||||
AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize end");
|
||||
m_projectRuntime.BindSelectionService(&m_selectionService);
|
||||
m_sceneRuntime.Reset();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Panels/EditorPanelServices.h"
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Windowing/EditorFrameServices.h"
|
||||
#include "Scene/EditorSceneRuntime.h"
|
||||
#include "Project/EditorProjectRuntime.h"
|
||||
@@ -33,7 +34,7 @@ class EditorEditCommandRoute;
|
||||
|
||||
class EditorContext : public EditorFrameServices {
|
||||
public:
|
||||
bool Initialize(const std::filesystem::path& repoRoot);
|
||||
bool Initialize(const EditorRuntimePaths& runtimePaths);
|
||||
void AttachTextMeasurer(const UIEditorTextMeasurer& textMeasurer) override;
|
||||
void SetSystemInteractionHost(System::SystemInteractionService* systemInteractionHost);
|
||||
void BindEditCommandRoutes(
|
||||
|
||||
@@ -510,10 +510,10 @@ UIEditorMenuModel BuildEditorMenuModel() {
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
|
||||
EditorShellAsset BuildEditorApplicationShellAsset(const std::filesystem::path& repoRoot) {
|
||||
EditorShellAsset BuildEditorApplicationShellAsset(const EditorRuntimePaths& runtimePaths) {
|
||||
EditorShellAsset asset = {};
|
||||
asset.screenId = "editor.shell";
|
||||
asset.captureRootPath = (repoRoot / "editor/captures").lexically_normal();
|
||||
asset.captureRootPath = runtimePaths.captureRoot.lexically_normal();
|
||||
asset.panelRegistry = BuildEditorPanelRegistry();
|
||||
asset.workspace = BuildEditorWorkspaceModel(asset.panelRegistry);
|
||||
asset.workspaceSession =
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Windowing/EditorShellVariant.h"
|
||||
|
||||
#include <XCEditor/Shell/UIEditorShellAsset.h>
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
EditorShellAsset BuildEditorApplicationShellAsset(const std::filesystem::path& repoRoot);
|
||||
EditorShellAsset BuildEditorApplicationShellAsset(const EditorRuntimePaths& runtimePaths);
|
||||
|
||||
UIEditorShellInteractionDefinition BuildEditorApplicationShellInteractionDefinition(
|
||||
const EditorShellAsset& asset,
|
||||
|
||||
@@ -18,7 +18,7 @@ EditorShellRuntime::EditorShellRuntime(
|
||||
, m_workspacePanels(std::move(workspacePanels)) {}
|
||||
|
||||
void EditorShellRuntime::Initialize(
|
||||
const std::filesystem::path& repoRoot,
|
||||
const EditorRuntimePaths& runtimePaths,
|
||||
Rendering::Host::UiTextureHost& textureHost,
|
||||
Host::EditorHostResourceService& resourceService,
|
||||
UIEditorTextMeasurer& textMeasurer) {
|
||||
@@ -29,10 +29,10 @@ void EditorShellRuntime::Initialize(
|
||||
}
|
||||
|
||||
m_iconService->Initialize(textureHost, resourceService);
|
||||
m_viewportRuntimeServices->Initialize(repoRoot);
|
||||
m_viewportRuntimeServices->Initialize(runtimePaths);
|
||||
m_workspacePanels.Initialize(
|
||||
EditorWorkspacePanelInitializationContext{
|
||||
.repoRoot = repoRoot,
|
||||
.runtimePaths = runtimePaths,
|
||||
.textMeasurer = textMeasurer,
|
||||
.iconService = *m_iconService,
|
||||
.sceneViewportRuntime = &m_viewportRuntimeServices->GetSceneViewportRuntime(),
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
std::unique_ptr<EditorViewportRuntimeServices> viewportRuntimeServices);
|
||||
|
||||
void Initialize(
|
||||
const std::filesystem::path& repoRoot,
|
||||
const EditorRuntimePaths& runtimePaths,
|
||||
Rendering::Host::UiTextureHost& textureHost,
|
||||
Host::EditorHostResourceService& resourceService,
|
||||
UIEditorTextMeasurer& textMeasurer) override;
|
||||
|
||||
23
editor/app/Core/Environment/EditorRuntimePaths.h
Normal file
23
editor/app/Core/Environment/EditorRuntimePaths.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
struct EditorRuntimePaths {
|
||||
std::filesystem::path workspaceRoot = {};
|
||||
std::filesystem::path executableRoot = {};
|
||||
std::filesystem::path resourceRoot = {};
|
||||
std::filesystem::path projectRoot = {};
|
||||
std::filesystem::path captureRoot = {};
|
||||
|
||||
[[nodiscard]] bool IsComplete() const {
|
||||
return !workspaceRoot.empty() &&
|
||||
!executableRoot.empty() &&
|
||||
!resourceRoot.empty() &&
|
||||
!projectRoot.empty() &&
|
||||
!captureRoot.empty();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -51,7 +51,7 @@ struct EditorConsoleEntry {
|
||||
};
|
||||
|
||||
struct EditorSession {
|
||||
std::filesystem::path repoRoot = {};
|
||||
std::filesystem::path workspaceRoot = {};
|
||||
std::filesystem::path projectRoot = {};
|
||||
std::string activePanelId = {};
|
||||
EditorRuntimeMode runtimeMode = EditorRuntimeMode::Edit;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Scene/SceneViewportRenderRequest.h"
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Viewport/EditorViewportPicking.h"
|
||||
#include "Viewport/EditorViewportTypes.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::Rendering {
|
||||
@@ -33,7 +32,7 @@ class EditorViewportRuntimeServices {
|
||||
public:
|
||||
virtual ~EditorViewportRuntimeServices() = default;
|
||||
|
||||
virtual void Initialize(const std::filesystem::path& repoRoot) = 0;
|
||||
virtual void Initialize(const EditorRuntimePaths& runtimePaths) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void AttachWindowRenderer(Rendering::Host::ViewportRenderHost& windowRenderer) = 0;
|
||||
virtual void DetachWindowRenderer() = 0;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Windowing/EditorFrameServices.h"
|
||||
|
||||
#include <XCEditor/Docking/UIEditorDockHostTransfer.h>
|
||||
@@ -7,7 +8,6 @@
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
virtual ~EditorWorkspaceShellRuntime() = default;
|
||||
|
||||
virtual void Initialize(
|
||||
const std::filesystem::path& repoRoot,
|
||||
const EditorRuntimePaths& runtimePaths,
|
||||
Rendering::Host::UiTextureHost& textureHost,
|
||||
Host::EditorHostResourceService& resourceService,
|
||||
UIEditorTextMeasurer& textMeasurer) = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Assets/EditorIconService.h"
|
||||
#include "Commands/EditorEditCommandRoute.h"
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "State/EditorSession.h"
|
||||
#include "Panels/EditorPanelServices.h"
|
||||
#include "Viewport/EditorViewportRuntimeServices.h"
|
||||
@@ -14,7 +15,6 @@
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -46,7 +46,7 @@ struct EditorWorkspacePanelFrameEvent {
|
||||
};
|
||||
|
||||
struct EditorWorkspacePanelInitializationContext {
|
||||
const std::filesystem::path& repoRoot;
|
||||
const EditorRuntimePaths& runtimePaths;
|
||||
UIEditorTextMeasurer& textMeasurer;
|
||||
EditorIconService& iconService;
|
||||
EditorSceneViewportRuntime* sceneViewportRuntime = nullptr;
|
||||
|
||||
@@ -400,9 +400,9 @@ ProjectPanel::GetPresentedWindowTreeExpansionModel() const {
|
||||
return ResolveUIEditorFilterableTreeHostExpansionModel(m_treeFilterHostFrame);
|
||||
}
|
||||
|
||||
void ProjectPanel::Initialize(const std::filesystem::path& repoRoot) {
|
||||
void ProjectPanel::Initialize(const std::filesystem::path& projectRoot) {
|
||||
m_ownedProjectRuntime = std::make_unique<EditorProjectRuntime>();
|
||||
m_ownedProjectRuntime->Initialize(repoRoot);
|
||||
m_ownedProjectRuntime->Initialize(projectRoot);
|
||||
SyncSelectionsFromRuntime();
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
bool directory = false;
|
||||
};
|
||||
|
||||
void Initialize(const std::filesystem::path& repoRoot);
|
||||
void Initialize(const std::filesystem::path& projectRoot);
|
||||
void SetProjectRuntime(EditorProjectRuntime* projectRuntime);
|
||||
void SetCommandFocusService(EditorCommandFocusService* commandFocusService);
|
||||
void SetSystemInteractionHost(System::SystemInteractionService* systemInteractionHost);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "EditorWindowHostTypes.h"
|
||||
#include "EditorWindowPointerCapture.h"
|
||||
#include "EditorWindowRenderRuntime.h"
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Windowing/EditorWindowGeometry.h"
|
||||
#include "Windowing/EditorWindowTransferRequests.h"
|
||||
#include "Windowing/EditorWindowTypes.h"
|
||||
@@ -12,7 +13,6 @@
|
||||
#include <XCEditor/Windowing/Presentation/EditorWorkspaceWindowProjection.h>
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -40,8 +40,7 @@ class EditorWindowInputFeedbackBinding;
|
||||
class EditorWindowTitleBarBinding;
|
||||
|
||||
struct EditorHostWindowRuntimeInitializationParams {
|
||||
std::filesystem::path repoRoot = {};
|
||||
std::filesystem::path captureRoot = {};
|
||||
EditorRuntimePaths runtimePaths = {};
|
||||
bool autoCaptureOnStartup = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -63,11 +63,9 @@ DWORD ResolveExtendedWindowStyle(const EditorWindowNativeHostPolicy& policy) {
|
||||
|
||||
EditorWindowHostRuntime::EditorWindowHostRuntime(
|
||||
EditorWindowHostConfig hostConfig,
|
||||
std::filesystem::path repoRoot,
|
||||
std::filesystem::path captureRoot)
|
||||
EditorRuntimePaths runtimePaths)
|
||||
: m_hostConfig(hostConfig),
|
||||
m_repoRoot(std::move(repoRoot)),
|
||||
m_captureRoot(std::move(captureRoot)) {}
|
||||
m_runtimePaths(std::move(runtimePaths)) {}
|
||||
|
||||
EditorWindowHostRuntime::~EditorWindowHostRuntime() = default;
|
||||
|
||||
@@ -176,8 +174,7 @@ bool EditorWindowHostRuntime::CreateHostWindow(
|
||||
if (!m_hostCoordinator->InitializeHostWindow(
|
||||
window,
|
||||
EditorHostWindowRuntimeInitializationParams{
|
||||
.repoRoot = m_repoRoot,
|
||||
.captureRoot = m_captureRoot,
|
||||
.runtimePaths = m_runtimePaths,
|
||||
.autoCaptureOnStartup = params.autoCaptureOnStartup,
|
||||
})) {
|
||||
return failWindowInitialization("managed window initialization failed");
|
||||
|
||||
@@ -17,8 +17,7 @@ class EditorWindowHostRuntime final : public EditorWindowHostRuntimeServices {
|
||||
public:
|
||||
EditorWindowHostRuntime(
|
||||
EditorWindowHostConfig hostConfig,
|
||||
std::filesystem::path repoRoot,
|
||||
std::filesystem::path captureRoot);
|
||||
EditorRuntimePaths runtimePaths);
|
||||
~EditorWindowHostRuntime();
|
||||
|
||||
bool CreateHostWindow(
|
||||
@@ -54,8 +53,8 @@ public:
|
||||
return m_hostConfig;
|
||||
}
|
||||
|
||||
const std::filesystem::path& GetRepoRoot() const {
|
||||
return m_repoRoot;
|
||||
const EditorRuntimePaths& GetRuntimePaths() const {
|
||||
return m_runtimePaths;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<EditorWindow>>& GetWindowStorage() {
|
||||
@@ -73,8 +72,7 @@ private:
|
||||
const EditorWindow* FindWindowByIdImpl(std::string_view windowId) const;
|
||||
|
||||
EditorWindowHostConfig m_hostConfig = {};
|
||||
std::filesystem::path m_repoRoot = {};
|
||||
std::filesystem::path m_captureRoot = {};
|
||||
EditorRuntimePaths m_runtimePaths = {};
|
||||
std::vector<std::unique_ptr<EditorWindow>> m_windows = {};
|
||||
EditorWindow* m_pendingCreateWindow = nullptr;
|
||||
EditorWindowHostCoordinator* m_hostCoordinator = nullptr;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
|
||||
#include <filesystem>
|
||||
@@ -25,8 +27,8 @@ inline ::XCEngine::Containers::String NormalizeSceneViewportResourcePath(
|
||||
}
|
||||
|
||||
inline std::filesystem::path GetSceneViewportResourceRootPath(
|
||||
const std::filesystem::path& repoRoot) {
|
||||
return (repoRoot / "editor" / "resources").lexically_normal();
|
||||
const EditorRuntimePaths& runtimePaths) {
|
||||
return runtimePaths.resourceRoot.lexically_normal();
|
||||
}
|
||||
|
||||
inline ::XCEngine::Containers::String BuildSceneViewportResourcePath(
|
||||
@@ -37,9 +39,9 @@ inline ::XCEngine::Containers::String BuildSceneViewportResourcePath(
|
||||
}
|
||||
|
||||
inline SceneViewportShaderPaths BuildSceneViewportShaderPaths(
|
||||
const std::filesystem::path& repoRoot) {
|
||||
const EditorRuntimePaths& runtimePaths) {
|
||||
const std::filesystem::path resourceRoot =
|
||||
GetSceneViewportResourceRootPath(repoRoot);
|
||||
GetSceneViewportResourceRootPath(runtimePaths);
|
||||
|
||||
SceneViewportShaderPaths paths = {};
|
||||
paths.infiniteGridShaderPath = BuildSceneViewportResourcePath(
|
||||
|
||||
@@ -45,8 +45,8 @@ ViewportHostService::ViewportHostService() = default;
|
||||
|
||||
ViewportHostService::~ViewportHostService() = default;
|
||||
|
||||
void ViewportHostService::Initialize(const std::filesystem::path& repoRoot) {
|
||||
m_sceneViewportRuntime.Initialize(BuildSceneViewportShaderPaths(repoRoot));
|
||||
void ViewportHostService::Initialize(const EditorRuntimePaths& runtimePaths) {
|
||||
m_sceneViewportRuntime.Initialize(BuildSceneViewportShaderPaths(runtimePaths));
|
||||
m_placeholderRenderers.clear();
|
||||
for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) {
|
||||
if (panel.presentationKind != UIEditorPanelPresentationKind::ViewportShell) {
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
ViewportHostService();
|
||||
~ViewportHostService();
|
||||
|
||||
void Initialize(const std::filesystem::path& repoRoot) override;
|
||||
void Initialize(const EditorRuntimePaths& runtimePaths) override;
|
||||
void Shutdown() override;
|
||||
void AttachWindowRenderer(Rendering::Host::ViewportRenderHost& windowRenderer) override;
|
||||
void DetachWindowRenderer() override;
|
||||
|
||||
@@ -84,9 +84,9 @@ void EditorProjectRuntime::Reset() {
|
||||
m_pendingSceneOpenPath.reset();
|
||||
}
|
||||
|
||||
bool EditorProjectRuntime::Initialize(const std::filesystem::path& repoRoot) {
|
||||
bool EditorProjectRuntime::Initialize(const std::filesystem::path& projectRoot) {
|
||||
Reset();
|
||||
m_browserModel.Initialize(repoRoot);
|
||||
m_browserModel.Initialize(projectRoot);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
};
|
||||
|
||||
void Reset();
|
||||
bool Initialize(const std::filesystem::path& repoRoot);
|
||||
bool Initialize(const std::filesystem::path& projectRoot);
|
||||
void BindSelectionService(EditorSelectionService* selectionService);
|
||||
void Refresh();
|
||||
|
||||
|
||||
@@ -545,9 +545,9 @@ void ProjectBrowserModel::Reset() {
|
||||
m_currentFolderId.clear();
|
||||
}
|
||||
|
||||
void ProjectBrowserModel::Initialize(const std::filesystem::path& repoRoot) {
|
||||
void ProjectBrowserModel::Initialize(const std::filesystem::path& projectRoot) {
|
||||
Reset();
|
||||
m_assetsRootPath = (repoRoot / "project/Assets").lexically_normal();
|
||||
m_assetsRootPath = (projectRoot / "Assets").lexically_normal();
|
||||
TraceProjectBrowser("ProjectBrowserModel::Initialize assetsRoot=" + PathToUtf8String(m_assetsRootPath));
|
||||
std::error_code errorCode = {};
|
||||
if (!std::filesystem::exists(m_assetsRootPath, errorCode)) {
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
ProjectBrowserModel& operator=(ProjectBrowserModel&&) noexcept = default;
|
||||
|
||||
void Reset();
|
||||
void Initialize(const std::filesystem::path& repoRoot);
|
||||
void Initialize(const std::filesystem::path& projectRoot);
|
||||
void Refresh();
|
||||
|
||||
bool Empty() const;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Windowing/EditorWindowTransferRequests.h"
|
||||
#include "Windowing/EditorFrameServices.h"
|
||||
#include "EditorWindowContentBindings.h"
|
||||
|
||||
#include <XCEditor/Foundation/UIEditorTextMeasurement.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -61,7 +61,7 @@ struct EditorWindowContentCapabilities {
|
||||
};
|
||||
|
||||
struct EditorWindowContentInitializationContext {
|
||||
const std::filesystem::path& repoRoot;
|
||||
const EditorRuntimePaths& runtimePaths;
|
||||
EditorFrameServices& frameServices;
|
||||
Rendering::Host::UiTextureHost& textureHost;
|
||||
Host::EditorHostResourceService& resourceService;
|
||||
|
||||
@@ -115,7 +115,7 @@ void EditorWorkspaceWindowContentController::Initialize(
|
||||
return;
|
||||
}
|
||||
m_shellRuntime->Initialize(
|
||||
context.repoRoot,
|
||||
context.runtimePaths,
|
||||
context.textureHost,
|
||||
context.resourceService,
|
||||
context.textMeasurer);
|
||||
|
||||
@@ -313,8 +313,7 @@ bool EditorWindowInstance::InitializeRuntime(
|
||||
.widthPixels = runtimeSurface.widthPixels,
|
||||
.heightPixels = runtimeSurface.heightPixels,
|
||||
},
|
||||
params.repoRoot,
|
||||
params.captureRoot,
|
||||
params.runtimePaths,
|
||||
params.autoCaptureOnStartup);
|
||||
if (initialized) {
|
||||
MarkRunning();
|
||||
|
||||
@@ -142,8 +142,7 @@ const ::XCEngine::UI::UITextureHandle& EditorWindowRuntimeController::GetTitleBa
|
||||
|
||||
bool EditorWindowRuntimeController::Initialize(
|
||||
const Rendering::Host::EditorWindowRenderRuntimeInitializeParams& renderParams,
|
||||
const std::filesystem::path& repoRoot,
|
||||
const std::filesystem::path& captureRoot,
|
||||
const EditorRuntimePaths& runtimePaths,
|
||||
bool autoCaptureOnStartup) {
|
||||
if (m_renderRuntime == nullptr) {
|
||||
LogRuntimeTrace("app", "window initialize failed: render runtime is null");
|
||||
@@ -167,7 +166,7 @@ bool EditorWindowRuntimeController::Initialize(
|
||||
m_frameServices,
|
||||
m_renderRuntime->GetTextMeasurer());
|
||||
m_contentController->Initialize(EditorWindowContentInitializationContext{
|
||||
.repoRoot = repoRoot,
|
||||
.runtimePaths = runtimePaths,
|
||||
.frameServices = m_frameServices,
|
||||
.textureHost = m_renderRuntime->GetTextureHost(),
|
||||
.resourceService = m_resourceService,
|
||||
@@ -200,7 +199,7 @@ bool EditorWindowRuntimeController::Initialize(
|
||||
m_ready = true;
|
||||
|
||||
m_screenshotController.Initialize(
|
||||
captureRoot,
|
||||
runtimePaths.captureRoot,
|
||||
m_resourceService.GetExecutableDirectory());
|
||||
if (autoCaptureOnStartup) {
|
||||
m_screenshotController.RequestCapture("startup");
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#endif
|
||||
|
||||
#include "Content/EditorWindowContentController.h"
|
||||
#include "Environment/EditorRuntimePaths.h"
|
||||
#include "Runtime/EditorWindowScreenshotController.h"
|
||||
#include "Windowing/EditorFrameServices.h"
|
||||
|
||||
@@ -14,7 +15,6 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -69,8 +69,7 @@ public:
|
||||
|
||||
bool Initialize(
|
||||
const Rendering::Host::EditorWindowRenderRuntimeInitializeParams& renderParams,
|
||||
const std::filesystem::path& repoRoot,
|
||||
const std::filesystem::path& captureRoot,
|
||||
const EditorRuntimePaths& runtimePaths,
|
||||
bool autoCaptureOnStartup);
|
||||
void Shutdown();
|
||||
void ResetInteractionState();
|
||||
|
||||
@@ -12,13 +12,11 @@ namespace {
|
||||
std::filesystem::path ResolveBuildCaptureRoot(
|
||||
const std::filesystem::path& requestedCaptureRoot,
|
||||
const std::filesystem::path& executableDirectory) {
|
||||
std::filesystem::path captureRoot = executableDirectory / "captures";
|
||||
const std::filesystem::path scenarioPath = requestedCaptureRoot.parent_path().filename();
|
||||
if (!scenarioPath.empty() && scenarioPath != "captures") {
|
||||
captureRoot /= scenarioPath;
|
||||
if (!requestedCaptureRoot.empty()) {
|
||||
return requestedCaptureRoot.lexically_normal();
|
||||
}
|
||||
|
||||
return captureRoot.lexically_normal();
|
||||
return (executableDirectory / "captures").lexically_normal();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -65,7 +65,7 @@ TEST(EditorProjectRuntimeTests, NavigateToFolderClearsCurrentProjectSelection) {
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scenes/Main.xc"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scenes"));
|
||||
ASSERT_TRUE(runtime.SetSelection("Assets/Scenes/Main.xc"));
|
||||
|
||||
@@ -80,7 +80,7 @@ TEST(EditorProjectRuntimeTests, OpenSceneItemQueuesSceneOpenRequest) {
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scenes/Main.xc"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scenes"));
|
||||
|
||||
ASSERT_TRUE(runtime.OpenItem("Assets/Scenes/Main.xc"));
|
||||
@@ -97,7 +97,7 @@ TEST(EditorProjectRuntimeTests, ResolveCommandTargetsFollowRuntimeSelectionAndCu
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scenes/Main.xc"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scenes"));
|
||||
ASSERT_TRUE(runtime.SetSelection("Assets/Scenes/Main.xc"));
|
||||
|
||||
@@ -134,7 +134,7 @@ TEST(EditorProjectRuntimeTests, ResolveEditCommandTargetMarksAssetsRootAndIgnore
|
||||
TemporaryRepo repo = {};
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
|
||||
const std::optional<EditorProjectRuntime::EditCommandTarget> rootTarget =
|
||||
runtime.ResolveEditCommandTarget();
|
||||
@@ -153,7 +153,7 @@ TEST(EditorProjectRuntimeTests, RenameSelectedItemRemapsSelectionAndDeleteClears
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scripts/Player.cs.meta"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scripts"));
|
||||
ASSERT_TRUE(runtime.SetSelection("Assets/Scripts/Player.cs"));
|
||||
|
||||
@@ -176,7 +176,7 @@ TEST(EditorProjectRuntimeTests, BoundSelectionServiceBecomesTheSingleProjectSele
|
||||
|
||||
EditorSelectionService selectionService = {};
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
runtime.BindSelectionService(&selectionService);
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scripts"));
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ 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::EditorRuntimePaths;
|
||||
using XCEngine::UI::Editor::App::FindEditorProductPanel;
|
||||
using XCEngine::UI::Editor::App::GetEditorProductPanels;
|
||||
using XCEngine::UI::Editor::App::kGamePanelId;
|
||||
@@ -56,8 +57,18 @@ UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) {
|
||||
return binding;
|
||||
}
|
||||
|
||||
EditorRuntimePaths TestRuntimePaths() {
|
||||
return EditorRuntimePaths{
|
||||
.workspaceRoot = ".",
|
||||
.executableRoot = ".",
|
||||
.resourceRoot = "editor/resources",
|
||||
.projectRoot = "project",
|
||||
.captureRoot = "captures",
|
||||
};
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, DefaultShellAssetPassesValidation) {
|
||||
const auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
const auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
|
||||
const auto validation = ValidateEditorShellAsset(shellAsset);
|
||||
EXPECT_TRUE(validation.IsValid()) << validation.message;
|
||||
@@ -79,7 +90,7 @@ TEST(EditorShellAssetValidationTest, ProductManifestDeclaresPanelRuntimeAndViewp
|
||||
<< validation.message;
|
||||
ASSERT_TRUE(validation.IsValid());
|
||||
|
||||
const auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
const auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
ASSERT_EQ(shellAsset.panelRegistry.panels.size(), GetEditorProductPanels().size());
|
||||
|
||||
const auto* gamePanel = FindEditorProductPanel(kGamePanelId);
|
||||
@@ -92,7 +103,7 @@ TEST(EditorShellAssetValidationTest, ProductManifestDeclaresPanelRuntimeAndViewp
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspacePanelMissingFromRegistry) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
|
||||
auto* documentPanel =
|
||||
const_cast<XCEngine::UI::Editor::UIEditorPanelDescriptor*>(
|
||||
@@ -108,7 +119,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspacePanelMissingFromR
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspaceTitleDriftFromRegistry) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
auto* scenePanel = FindWorkspacePanelNode(shellAsset.workspace.root, "scene");
|
||||
ASSERT_NE(scenePanel, nullptr);
|
||||
shellAsset.workspace.activePanelId = scenePanel->panel.panelId;
|
||||
@@ -119,7 +130,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspaceTitleDriftFromReg
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidWorkspaceSessionState) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
ASSERT_FALSE(shellAsset.workspaceSession.panelStates.empty());
|
||||
shellAsset.workspaceSession.panelStates.front().open = false;
|
||||
|
||||
@@ -128,7 +139,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidWorkspaceSessionSta
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationMissingFromRegistry) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
ASSERT_EQ(
|
||||
shellAsset.shellDefinition.workspacePresentations.size(),
|
||||
shellAsset.panelRegistry.panels.size());
|
||||
@@ -142,7 +153,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationMissingFr
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsDuplicateShellPresentationPanelId) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
ASSERT_EQ(
|
||||
shellAsset.shellDefinition.workspacePresentations.size(),
|
||||
shellAsset.panelRegistry.panels.size());
|
||||
@@ -156,7 +167,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsDuplicateShellPresentation
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsMissingRequiredShellPresentation) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
shellAsset.shellDefinition.workspacePresentations.clear();
|
||||
|
||||
const auto validation = ValidateEditorShellAsset(shellAsset);
|
||||
@@ -166,7 +177,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsMissingRequiredShellPresen
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShellMenuModel) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
shellAsset.shellDefinition.menuModel.menus = {
|
||||
{
|
||||
"window",
|
||||
@@ -191,7 +202,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShellMenuModel) {
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShortcutConfiguration) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
shellAsset.shortcutAsset.bindings.push_back(
|
||||
MakeBinding("missing.command", KeyCode::R));
|
||||
|
||||
@@ -202,7 +213,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShortcutConfigurati
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationKindMismatch) {
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(".");
|
||||
auto shellAsset = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
ASSERT_EQ(
|
||||
shellAsset.shellDefinition.workspacePresentations.size(),
|
||||
shellAsset.panelRegistry.panels.size());
|
||||
|
||||
@@ -67,7 +67,7 @@ TEST(ProjectBrowserModelTests, ReparentFolderMovesFolderMetaAndRemapsCurrentFold
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/B.meta"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
|
||||
ASSERT_TRUE(model.NavigateToFolder("Assets/A/Child"));
|
||||
|
||||
@@ -89,7 +89,7 @@ TEST(ProjectBrowserModelTests, MoveFolderToRootMovesFolderMetaAndRemapsCurrentFo
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Parent/Nested.meta"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
|
||||
ASSERT_TRUE(model.NavigateToFolder("Assets/Parent/Nested"));
|
||||
|
||||
@@ -110,7 +110,7 @@ TEST(ProjectBrowserModelTests, CreateFolderCreatesUniqueDirectoryUnderTargetFold
|
||||
ASSERT_TRUE(repo.CreateDirectory("project/Assets/Scenes/New Folder"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
|
||||
std::string createdFolderId = {};
|
||||
ASSERT_TRUE(model.CreateFolder("Assets/Scenes", "New Folder", &createdFolderId));
|
||||
@@ -125,7 +125,7 @@ TEST(ProjectBrowserModelTests, CreateMaterialCreatesUniqueMaterialFileAndExposes
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Materials/New Material.mat"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
|
||||
std::string createdItemId = {};
|
||||
ASSERT_TRUE(model.CreateMaterial("Assets/Materials", "New Material", &createdItemId));
|
||||
@@ -151,7 +151,7 @@ TEST(ProjectBrowserModelTests, CanMoveItemToFolderRejectsDescendantFolderTargets
|
||||
ASSERT_TRUE(repo.CreateDirectory("project/Assets/FolderB"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
|
||||
EXPECT_FALSE(model.CanMoveItemToFolder("Assets/FolderA", "Assets/FolderA/Nested"));
|
||||
EXPECT_TRUE(model.CanMoveItemToFolder("Assets/FolderA", "Assets/FolderB"));
|
||||
@@ -165,7 +165,7 @@ TEST(ProjectBrowserModelTests, MoveItemToFolderMovesFileMetaAndRefreshesCurrentL
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scripts/Player.cs.meta"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
ASSERT_TRUE(model.NavigateToFolder("Assets/Scripts"));
|
||||
|
||||
std::string movedItemId = {};
|
||||
@@ -192,7 +192,7 @@ TEST(ProjectBrowserModelTests, RenameFilePreservesExtensionAndUpdatesCurrentList
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scenes/Main.xc.meta"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
ASSERT_TRUE(model.NavigateToFolder("Assets/Scenes"));
|
||||
|
||||
std::string renamedItemId = {};
|
||||
@@ -218,7 +218,7 @@ TEST(ProjectBrowserModelTests, DeleteFolderRemovesMetaAndFallsBackCurrentFolder)
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Parent/Nested.meta"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
ASSERT_TRUE(model.NavigateToFolder("Assets/Parent/Nested"));
|
||||
|
||||
ASSERT_TRUE(model.DeleteItem("Assets/Parent"));
|
||||
@@ -236,7 +236,7 @@ TEST(ProjectBrowserModelTests, AssetEntriesExposeKindAndOpenCapability) {
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/readme.txt"));
|
||||
|
||||
ProjectBrowserModel model = {};
|
||||
model.Initialize(repo.Root());
|
||||
model.Initialize(repo.Root() / "project");
|
||||
|
||||
const ProjectBrowserModel::AssetEntry* sceneEntry =
|
||||
model.FindAssetEntry("Assets/mesh.fbx");
|
||||
|
||||
@@ -144,7 +144,7 @@ TEST(ProjectPanelTests, CreateFolderCommandCreatesDirectoryAndQueuesRename) {
|
||||
TemporaryRepo repo = {};
|
||||
|
||||
ProjectPanel panel = {};
|
||||
panel.Initialize(repo.Root());
|
||||
panel.Initialize(repo.Root() / "project");
|
||||
|
||||
const UIEditorHostCommandEvaluationResult evaluation =
|
||||
panel.EvaluateAssetCommand("assets.create_folder");
|
||||
@@ -169,7 +169,7 @@ TEST(ProjectPanelTests, CreateMaterialCommandCreatesFileAndQueuesRename) {
|
||||
TemporaryRepo repo = {};
|
||||
|
||||
ProjectPanel panel = {};
|
||||
panel.Initialize(repo.Root());
|
||||
panel.Initialize(repo.Root() / "project");
|
||||
|
||||
const UIEditorHostCommandEvaluationResult evaluation =
|
||||
panel.EvaluateAssetCommand("assets.create_material");
|
||||
@@ -192,7 +192,7 @@ TEST(ProjectPanelTests, BackgroundContextMenuCreateFolderUsesCurrentFolder) {
|
||||
TemporaryRepo repo = {};
|
||||
|
||||
ProjectPanel panel = {};
|
||||
panel.Initialize(repo.Root());
|
||||
panel.Initialize(repo.Root() / "project");
|
||||
|
||||
const UIEditorHostedPanelDispatchEntry dispatchEntry = MakeProjectDispatchEntry();
|
||||
panel.Update(
|
||||
@@ -219,7 +219,7 @@ TEST(ProjectPanelTests, FolderContextMenuCreateFolderUsesFolderTarget) {
|
||||
ASSERT_TRUE(std::filesystem::create_directories(repo.Root() / "project/Assets/FolderA"));
|
||||
|
||||
ProjectPanel panel = {};
|
||||
panel.Initialize(repo.Root());
|
||||
panel.Initialize(repo.Root() / "project");
|
||||
|
||||
const UIEditorHostedPanelDispatchEntry dispatchEntry = MakeProjectDispatchEntry();
|
||||
panel.Update(
|
||||
@@ -239,7 +239,7 @@ TEST(ProjectPanelTests, InjectedRuntimeSelectionDrivesRenameWithoutPanelSelectio
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scripts/Player.cs.meta"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
|
||||
ProjectPanel panel = {};
|
||||
panel.SetProjectRuntime(&runtime);
|
||||
@@ -263,7 +263,7 @@ TEST(ProjectPanelTests, InjectedRuntimeCurrentFolderDrivesRenameFallbackWithoutT
|
||||
ASSERT_TRUE(repo.CreateDirectory("project/Assets/FolderA"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
|
||||
ProjectPanel panel = {};
|
||||
panel.SetProjectRuntime(&runtime);
|
||||
@@ -283,7 +283,7 @@ TEST(ProjectPanelTests, IconServiceCanBeConfiguredBeforeRuntimeInitialization) {
|
||||
FakeIconService icons = {};
|
||||
|
||||
panel.SetIconService(&icons);
|
||||
panel.Initialize(repo.Root());
|
||||
panel.Initialize(repo.Root() / "project");
|
||||
|
||||
const UIEditorHostCommandEvaluationResult evaluation =
|
||||
panel.EvaluateAssetCommand("assets.create_folder");
|
||||
@@ -296,7 +296,7 @@ TEST(ProjectPanelTests, CopyPathCommandUsesInjectedSystemHost) {
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scripts/Player.cs"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scripts"));
|
||||
ASSERT_TRUE(runtime.SetSelection("Assets/Scripts/Player.cs"));
|
||||
|
||||
@@ -322,7 +322,7 @@ TEST(ProjectPanelTests, ShowInExplorerCommandUsesInjectedSystemHost) {
|
||||
ASSERT_TRUE(repo.WriteFile("project/Assets/Scripts/Player.cs"));
|
||||
|
||||
EditorProjectRuntime runtime = {};
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root()));
|
||||
ASSERT_TRUE(runtime.Initialize(repo.Root() / "project"));
|
||||
ASSERT_TRUE(runtime.NavigateToFolder("Assets/Scripts"));
|
||||
ASSERT_TRUE(runtime.SetSelection("Assets/Scripts/Player.cs"));
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace {
|
||||
|
||||
using XCEngine::Input::KeyCode;
|
||||
using XCEngine::UI::Editor::App::BuildEditorApplicationShellAsset;
|
||||
using XCEngine::UI::Editor::App::EditorRuntimePaths;
|
||||
using XCEngine::UI::Editor::BuildStructuredEditorShellBinding;
|
||||
using XCEngine::UI::Editor::BuildStructuredEditorShellServices;
|
||||
using XCEngine::UI::Editor::ResolveUIEditorShellInteractionModel;
|
||||
@@ -40,6 +41,17 @@ std::filesystem::path RepoRootPath() {
|
||||
return std::filesystem::path(root).lexically_normal();
|
||||
}
|
||||
|
||||
EditorRuntimePaths TestRuntimePaths() {
|
||||
const std::filesystem::path root = RepoRootPath();
|
||||
return EditorRuntimePaths{
|
||||
.workspaceRoot = root,
|
||||
.executableRoot = root,
|
||||
.resourceRoot = root / "editor" / "resources",
|
||||
.projectRoot = root / "project",
|
||||
.captureRoot = root / "build" / "editor" / "captures",
|
||||
};
|
||||
}
|
||||
|
||||
bool PanelRegistriesMatch(
|
||||
const XCEngine::UI::Editor::UIEditorPanelRegistry& lhs,
|
||||
const XCEngine::UI::Editor::UIEditorPanelRegistry& rhs) {
|
||||
@@ -126,7 +138,7 @@ UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) {
|
||||
} // namespace
|
||||
|
||||
TEST(EditorUIStructuredShellTest, StructuredEditorShellDoesNotRequireRepositoryXCUIDocument) {
|
||||
const auto shell = BuildEditorApplicationShellAsset(RepoRootPath());
|
||||
const auto shell = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
const auto binding = BuildStructuredEditorShellBinding(shell);
|
||||
|
||||
ASSERT_TRUE(binding.IsValid()) << binding.assetValidation.message;
|
||||
@@ -136,7 +148,7 @@ TEST(EditorUIStructuredShellTest, StructuredEditorShellDoesNotRequireRepositoryX
|
||||
}
|
||||
|
||||
TEST(EditorUIStructuredShellTest, StructuredShellBindingUsesEditorShellAssetAsSingleSource) {
|
||||
auto shell = BuildEditorApplicationShellAsset(RepoRootPath());
|
||||
auto shell = BuildEditorApplicationShellAsset(TestRuntimePaths());
|
||||
shell.shortcutAsset.commandRegistry.commands = {
|
||||
{
|
||||
"workspace.reset_layout",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::Editor::App::BuildEditorApplicationShellAsset;
|
||||
using XCEngine::UI::Editor::App::EditorRuntimePaths;
|
||||
using XCEngine::UI::Editor::FindUIEditorPanelDescriptor;
|
||||
using XCEngine::UI::Editor::UIEditorPanelDescriptor;
|
||||
using XCEngine::UI::Editor::UIEditorPanelRegistry;
|
||||
@@ -14,8 +15,15 @@ using XCEngine::UI::Editor::UIEditorPanelRegistryValidationCode;
|
||||
using XCEngine::UI::Editor::ValidateUIEditorPanelRegistry;
|
||||
|
||||
TEST(UIEditorPanelRegistryTest, DefaultRegistryContainsShellDescriptors) {
|
||||
const EditorRuntimePaths paths{
|
||||
.workspaceRoot = ".",
|
||||
.executableRoot = ".",
|
||||
.resourceRoot = "editor/resources",
|
||||
.projectRoot = "project",
|
||||
.captureRoot = "captures",
|
||||
};
|
||||
const UIEditorPanelRegistry registry =
|
||||
BuildEditorApplicationShellAsset(".").panelRegistry;
|
||||
BuildEditorApplicationShellAsset(paths).panelRegistry;
|
||||
|
||||
ASSERT_EQ(registry.panels.size(), 6u);
|
||||
const UIEditorPanelDescriptor* descriptor =
|
||||
|
||||
Reference in New Issue
Block a user