Refactor editor windowing and update renderer regression
This commit is contained in:
@@ -1,93 +0,0 @@
|
||||
# Rendering SRP / URP Closeout Plan
|
||||
|
||||
更新日期: `2026-04-26`
|
||||
|
||||
状态: `Completed, Phase 1 closeout done`
|
||||
|
||||
## 1. 长期目标
|
||||
|
||||
目标不是继续堆更多 URP 外形,而是把默认渲染主线真正收口为:
|
||||
|
||||
`GraphicsSettings.renderPipelineAsset -> UniversalRenderPipelineAsset -> ScriptableRendererData -> UniversalRenderer -> ScriptableRendererFeature/Pass -> ScriptableRenderPipelineHost -> native backend contract -> RenderGraph / RHI`
|
||||
|
||||
收口后的职责边界:
|
||||
|
||||
- managed URP 负责 camera policy、renderer 选择、stage planning、feature/pass 编排、final color 策略
|
||||
- native Rendering 负责 scene extraction、scene draw、fullscreen graph execution、RenderGraph/RHI
|
||||
- `BuiltinForwardPipeline` 或其后继只保留 backend 语义,不继续承载产品层策略
|
||||
|
||||
## 2. 阶段路线
|
||||
|
||||
### Phase 1: Renderer-backed URP v1 收口
|
||||
|
||||
- 让默认 `UniversalRenderPipelineAsset -> UniversalRenderer` 成为唯一上层主线
|
||||
- 先收口 ownership 和 contract,不在本阶段做 backend 大拆分
|
||||
|
||||
### Phase 2: Native backend contract 抽取
|
||||
|
||||
- 把默认 native backend 从产品层语义里剥离出来
|
||||
- 让 host/bridge 面向稳定 backend contract,而不是面向 builtin forward 产品实现
|
||||
|
||||
### Phase 3: Feature ownership 迁移
|
||||
|
||||
- 把 feature 的启用、排序、插入点统一回收到 managed `ScriptableRendererFeature`
|
||||
|
||||
### Phase 4: Asset / Editor / Runtime 产品化
|
||||
|
||||
- 稳定 renderer data、camera override、runtime invalidation、editor 消费路径
|
||||
|
||||
### Phase 5: 收尾与清理
|
||||
|
||||
- 删除临时兼容层,统一命名和文档
|
||||
|
||||
## 3. Phase 1 已完成进展
|
||||
|
||||
- 已补齐长期目标文档和 Rendering 模块目标描述
|
||||
- 已把 managed backend 解析从“隐式默认 fallback”改成“显式策略”
|
||||
- 已新增 `ManagedPipelineRendererAssetPolicy`
|
||||
- 已让 `ManagedScriptableRenderPipelineAsset` 先读显式 renderer asset,再按 policy 解析 default native backend
|
||||
- 已让 `MonoManagedRenderPipelineAssetRuntime::GetPipelineRendererAsset()` 不再直接偷偷创建默认 native backend asset
|
||||
- 已让 Mono runtime 显式返回 `DefaultNativeBackend` policy
|
||||
- 已把 `UniversalRenderer` 的 `PostProcess` 变成显式 stage 分支
|
||||
- 已补 `UniversalPostProcessBlock.EnqueueRenderPasses()` 作为显式入口
|
||||
- 已把 `RuntimeStateHashUtility` 调整为跨程序集可见
|
||||
- 已把 `ScriptableRendererFeature.CreateInstance()` 调整为可供外部受控复用
|
||||
- 已更新一批 scripting 侧测试预期,使默认 URP/managed pipeline case 面向“显式 default backend policy”
|
||||
- 已完成 editor 编译与 12 秒启动冒烟
|
||||
- `XCUIEditorApp` 编译通过
|
||||
- `build/editor/Debug/XCEngine.exe` 12 秒启动烟测通过
|
||||
|
||||
## 4. Phase 1 验收结果
|
||||
|
||||
- 已修掉 `managed/GameScripts/RenderPipelineApiProbe.cs` 中挡住本阶段验证的 managed API 问题
|
||||
- `scripting_tests` 已恢复为可用基线,并通过 9 条聚焦的 SRP/URP 回归用例
|
||||
- 默认 backend 来源、default renderer fallback、feature 注入、renderer invalidation 等关键主路径已被 focused regression tests 覆盖
|
||||
- editor 侧验证已补齐,`XCUIEditorApp` 编译与 `build/editor/Debug/XCEngine.exe` 12 秒启动烟测均通过
|
||||
- `rendering_unit_tests` 仍存在与本次收口无关的既有编译断点,但不再阻塞 Phase 1 收口验收
|
||||
|
||||
## 5. 当前 subplan
|
||||
|
||||
本轮 subplan 已完成,已从执行列表清空。
|
||||
|
||||
### 5.1 已完成验收项
|
||||
|
||||
- 默认 backend 来源已变成显式可追踪的 policy
|
||||
- `PostProcess` ownership 已稳定挂到 `UniversalRenderer`
|
||||
- `scripting_tests` 已能稳定覆盖本阶段相关用例
|
||||
- 默认 URP 主路径已具备 focused regression tests
|
||||
- Phase 1 未再引入新的隐式 fallback 语义
|
||||
|
||||
## 6. 下一阶段建议
|
||||
|
||||
下一阶段可以进入 Phase 2,但不要重新把产品层策略带回 native:
|
||||
|
||||
1. 抽取更稳定的 native backend contract,继续收窄 host/bridge 对具体 builtin forward 实现的依赖
|
||||
2. 继续把 feature 的启用、排序、插入点留在 managed `ScriptableRendererFeature / UniversalRenderer` 主线
|
||||
3. 在当前 focused regression baseline 之上继续稳住 renderer invalidation、asset authoring 和 editor/runtime 消费路径
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 本阶段不直接重命名 `BuiltinForwardPipeline`
|
||||
- 不在本阶段完成 backend-only 最终类层次
|
||||
- 不引入 deferred、clustered lighting、完整 volume framework 等新能力
|
||||
- 不把 editor tooling 路径强行并进默认 URP 产品主线
|
||||
@@ -1,141 +1 @@
|
||||
# 渲染模块长期目标与下一阶段执行计划
|
||||
|
||||
更新时间: `2026-04-26`
|
||||
|
||||
状态: `Active`
|
||||
|
||||
执行闸门:
|
||||
|
||||
- 每完成一个阶段,先编译并启动 `editor`。
|
||||
- 对 `editor` 执行不少于 `12s` 的冒烟测试,确认进程可启动、主循环可持续运行且无立即崩溃。
|
||||
- 只有在该阶段编译和 `12s` 冒烟测试都通过后,才允许执行该阶段的 `commit` 和 `push`。
|
||||
|
||||
说明: 本文基于当前仓库中的实际代码重新审核后编写,用于替代已经失效的 `rendering_srp_urp_closeout_plan.md`。本计划只按现有实现说话,不沿用旧文档中的“已收口完成”结论。
|
||||
|
||||
## 1. 基于代码的审核结论
|
||||
|
||||
- 当前默认渲染主线是
|
||||
`SceneRenderer -> SceneRenderRequestPlanner -> RenderPipelineHost -> CameraFramePlanBuilder -> CameraRenderer -> ExecuteCameraFrameRenderGraphPlan`,不是“managed URP 直接驱动 native backend”的单线结构。
|
||||
- 当前默认顶层 pipeline 已经是 `ScriptableRenderPipelineHost`。在没有显式 preferred asset 时,`RenderPipelineFactory` 会优先尝试 configured managed pipeline asset,失败后回落到 host asset;host 的默认 backend 是 `BuiltinForwardPipelineAsset`。
|
||||
- managed SRP 不是空壳。当前已经具备 `ScriptableRenderPipelinePlanningContext`、`RenderSceneSetupContext`、`DirectionalShadowExecutionContext`、`ScriptableRenderContext`,能够参与 request/frame plan、scene setup、shadow policy、scene draw 和 fullscreen stage 录制。
|
||||
- 但 managed/backend contract 还没有真正完成。`MonoManagedRenderPipelineAssetRuntime::GetPipelineRendererAsset()` 当前不会返回 shared backend asset,`GetPipelineRendererAssetPolicy()` 在 managed asset 可用时固定返回 `DefaultNativeBackend`。也就是说,现阶段 managed renderer 仍统一依赖默认 native scene backend。
|
||||
- `RenderGraph` 的 managed 接口目前是 v1。它能创建 transient texture、记录 scene phase / injection point / native feature pass、按 desc 调 `DrawRenderers`,也能提交 raster pass;但 raster pass execution 仍只覆盖 `ColorScale` / `ShaderVector` / `FinalColor` 三类 fullscreen execution,且 flush 逻辑只对 fullscreen sequence stage 生效。
|
||||
- `UniversalRenderPipelineAsset` / `RendererBackedRenderPipelineAsset` 已支持 `rendererDataList`、`defaultRendererIndex`、`UniversalAdditionalCameraData.rendererIndex`,但 `UniversalPostProcessBlock` 仍为空实现,默认后处理主要依赖 feature 注入和 `FinalOutput` 内置 pass。
|
||||
- `BuiltinForwardPipeline` 仍同时承担 fallback pipeline、默认 `SceneDrawBackend`、默认 native feature host 三个角色,是当前渲染模块最大的耦合点。
|
||||
- editor tooling 仍是独立层。`CameraRenderer` 顶层安装 `BuiltinObjectIdPass`,而 scene viewport 的 grid / selection / helper pass 仍在 editor 专用路径,不应并入本阶段 SRP / URP 主线计划。
|
||||
|
||||
## 2. 长期目标
|
||||
|
||||
- 稳定总主线:
|
||||
`SceneRenderer -> SceneRenderRequestPlanner -> CameraFramePlanBuilder -> CameraRenderer -> ScriptableRenderPipelineHost -> managed stage recorder / native scene draw backend -> RenderGraph / RHI`
|
||||
- 稳定职责边界:
|
||||
managed 负责 camera / request / frame plan、renderer 选择、feature / pass 排序、fullscreen stage 组合、scene setup、shadow policy;native 负责 scene extraction、culling、scene draw backend、资源生命周期、RenderGraph 编译执行、RHI。
|
||||
- 让 `ScriptableRenderPipelineHost` 成为唯一可信的 SRP 边界,而不是继续把产品层策略散落在 `BuiltinForwardPipeline` 和 editor / runtime 兼容层里。
|
||||
- 把 `BuiltinForwardPipeline` 收敛为清晰的 fallback / backend 角色,避免它继续同时承担“默认产品主线”和“可复用 native backend”两套语义。
|
||||
- 让 `rendererDataList`、`rendererIndex`、feature invalidation、bridge generation refresh、asset runtime versioning 变成稳定 contract,而不是靠隐式 fallback 维持行为。
|
||||
- 如果未来真的需要多个 native backend asset,必须通过明确定义的 backend asset contract 接入,而不是继续复用当前未完成的 placeholder API。
|
||||
- 明确 runtime 主渲染与 editor viewport / tooling 的共用层和专用层,避免后续计划再次混写。
|
||||
|
||||
## 3. 当前约束
|
||||
|
||||
- 下一阶段不做纯 managed scene draw backend,不承诺把 Gaussian Splat / Volumetric 改写为纯 C# feature。
|
||||
- 下一阶段不新增 deferred、clustered lighting、完整 volume framework 等新能力。
|
||||
- 下一阶段不改 editor viewport render loop,也不把 grid / selection / helper pass 并入 runtime 主线。
|
||||
- 下一阶段不把 managed `RenderGraph` 包装误写成“通用图形 / 计算图 API”;当前它只是 SRP v1 录制接口。
|
||||
|
||||
## 4. 下一阶段执行计划
|
||||
|
||||
### 4.1 先把 backend contract 讲真
|
||||
|
||||
状态: `Completed` (`2026-04-26`)
|
||||
|
||||
目标: 把“当前唯一真实可用的是 `DefaultNativeBackend`”写进代码和测试,不再保留会误导人的伪完成语义。
|
||||
|
||||
执行:
|
||||
|
||||
- 重新审核 `ManagedRenderPipelineAssetRuntime`、`MonoManagedRenderPipelineAssetRuntime`、`RenderPipelineFactory`、`ScriptableRenderPipelineHost` 的 backend 选择路径。
|
||||
- 对未实现的 explicit backend asset 分支做二选一收口:要么真正实现 shared backend asset 解析,要么明确删除 / 禁用该分支并补充注释与测试。
|
||||
- 把 `DefaultNativeBackend` 作为当前正式 contract 固化下来,避免再出现“文档已完成、代码仍默认回落”的错位。
|
||||
|
||||
完成标志:
|
||||
|
||||
- 代码、测试、文档对 backend 选择语义一致。
|
||||
- 不再有“managed renderer 已支持 explicit backend asset”这一错误暗示。
|
||||
|
||||
### 4.2 从 `BuiltinForwardPipeline` 中抽出可复用的 native scene backend
|
||||
|
||||
状态: `Completed` (`2026-04-26`)
|
||||
|
||||
目标: 减轻 `BuiltinForwardPipeline` 的三重职责,让 host fallback 和 scene draw backend 可以独立演进。
|
||||
|
||||
执行:
|
||||
|
||||
- 从 `BuiltinForwardSceneSetup`、`BuiltinForwardPipeline`、`NativeSceneRecorder` 梳理出稳定的 `SceneDrawBackend` 组装层。
|
||||
- 把 `ShadowCaster`、`DepthOnly`、main scene feature host 的 ownership 拆清楚,避免 host / backend / fallback wrapper 重复配置。
|
||||
- 统一 fallback path 和 managed recorder path 对默认 native feature 的消费方式,减少双份默认注册逻辑的漂移风险。
|
||||
|
||||
完成标志:
|
||||
|
||||
- fallback pipeline 仍可独立工作。
|
||||
- managed stage recorder 与 fallback path 共用同一套默认 native backend 组装逻辑。
|
||||
|
||||
### 4.3 固化 managed SRP v1 能力边界
|
||||
|
||||
状态: `Completed` (`2026-04-26`)
|
||||
|
||||
目标: 把现有 managed API 从“隐式能力集合”收敛成明确的 v1 contract,便于后续扩展而不是继续误判能力上限。
|
||||
|
||||
执行:
|
||||
|
||||
- 整理 `ScriptableRenderContext`、`ScriptableRenderPipelinePlanningContext`、`RenderSceneSetupContext`、`DirectionalShadowExecutionContext` 的能力矩阵。
|
||||
- 对“fullscreen raster-only”“仅 fullscreen sequence stage flush managed raster pass”“scene draw 仍依赖 native backend”等限制补齐断言、测试和文档。
|
||||
- 补齐 `UniversalPostProcessBlock` 当前的真实语义说明:默认后处理并未在 block 内完成,仍主要依赖 feature / pass 注入与 `FinalOutput` 内置 pass。
|
||||
|
||||
完成标志:
|
||||
|
||||
- managed 侧能做什么、不能做什么有明确边界。
|
||||
- 后续计划不再把现有 API 误写成完整 URP runtime 或通用 RenderGraph API。
|
||||
|
||||
### 4.4 稳住 renderer authoring / invalidation / bridge refresh
|
||||
|
||||
状态: `Completed` (`2026-04-26`)
|
||||
|
||||
目标: 把当前已经存在但还容易漂移的 authoring 路径变成可回归验证的稳定面。
|
||||
|
||||
执行:
|
||||
|
||||
- 围绕 `rendererDataList`、`defaultRendererIndex`、`UniversalAdditionalCameraData.rendererIndex`、feature `SetDirty` / invalidation、bridge generation refresh 增加 focused tests。
|
||||
- 验证 default asset selection、explicit managed asset selection、bridge rebinding 这三条路径在 frame plan 和实际渲染行为上保持一致。
|
||||
- 明确 renderer 选择、feature 变更和 runtime resource version 之间的刷新链路,避免再次回退到隐式缓存。
|
||||
|
||||
完成标志:
|
||||
|
||||
- renderer 切换、feature 改动、bridge 切换都有回归保护。
|
||||
- `CameraFramePlanBuilder` 和 `CameraRenderer` 对 managed path 的依赖关系稳定可追踪。
|
||||
|
||||
## 5. 下一阶段完成后的验收口径
|
||||
|
||||
- `ScriptableRenderPipelineHost` 的角色描述不再含混。
|
||||
- `DefaultNativeBackend` 与 explicit backend asset 的语义只保留一种真实状态。
|
||||
- `BuiltinForwardPipeline` 不再是继续扩张的“总包类”。
|
||||
- managed SRP v1 的能力边界和限制有文档、有测试、有 fail-fast。
|
||||
- renderer authoring / invalidation / bridge refresh 不再依赖人工记忆。
|
||||
|
||||
## 6. 审核依据
|
||||
|
||||
- `engine/src/Rendering/Execution/SceneRenderer.cpp`
|
||||
- `engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp`
|
||||
- `engine/src/Rendering/Planning/CameraFramePlanBuilder.cpp`
|
||||
- `engine/src/Rendering/Execution/CameraRenderer.cpp`
|
||||
- `engine/src/Rendering/Internal/RenderPipelineFactory.cpp`
|
||||
- `engine/src/Rendering/Pipelines/ScriptableRenderPipelineHost.cpp`
|
||||
- `engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp`
|
||||
- `engine/src/Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.cpp`
|
||||
- `engine/src/Scripting/Mono/MonoScriptRuntime.cpp`
|
||||
- `managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs`
|
||||
- `managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelinePlanningContext.cs`
|
||||
- `managed/XCEngine.ScriptCore/Rendering/Core/RenderSceneSetupContext.cs`
|
||||
- `managed/XCEngine.ScriptCore/Rendering/Graph/RenderGraphRasterPassBuilder.cs`
|
||||
- `managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderPipelineAsset.cs`
|
||||
- `managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs`
|
||||
- `managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs`
|
||||
|
||||
@@ -260,6 +260,10 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
|
||||
set(XCUI_EDITOR_APP_WINDOWING_SOURCES
|
||||
app/Composition/EditorWindowWorkspaceStore.cpp
|
||||
app/Windowing/EditorWindowManager.cpp
|
||||
app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.cpp
|
||||
app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp
|
||||
app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp
|
||||
app/Windowing/Content/EditorWindowContentFactory.cpp
|
||||
app/Windowing/Content/EditorUtilityWindowContentController.cpp
|
||||
app/Windowing/Content/EditorWorkspaceWindowContentController.cpp
|
||||
@@ -279,11 +283,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP)
|
||||
app/Platform/Win32/Runtime/EditorWindowRuntimeController.cpp
|
||||
app/Platform/Win32/System/Win32SystemInteractionHost.cpp
|
||||
app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp
|
||||
app/Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.cpp
|
||||
app/Platform/Win32/Windowing/EditorWindowManager.cpp
|
||||
app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.cpp
|
||||
app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp
|
||||
app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp
|
||||
)
|
||||
|
||||
set(XCUI_EDITOR_APP_CORE_SOURCES
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
#include "Bootstrap/EditorResources.h"
|
||||
#include "System/SystemInteractionService.h"
|
||||
#include "Composition/EditorContext.h"
|
||||
#include "Windowing/EditorWindowManager.h"
|
||||
#include "Windowing/System/EditorWindowSystem.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowManager.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Platform/Win32/System/Win32SystemInteractionHost.h"
|
||||
#include "Support/EnvironmentFlags.h"
|
||||
#include "Support/ExecutablePath.h"
|
||||
@@ -137,15 +136,11 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (App::EditorWindow* primaryWindow = m_windowManager->FindPrimaryWindow();
|
||||
primaryWindow != nullptr &&
|
||||
primaryWindow->GetHwnd() != nullptr) {
|
||||
PostMessageW(primaryWindow->GetHwnd(), WM_CLOSE, 0, 0);
|
||||
}
|
||||
m_windowManager->RequestPrimaryWindowClose();
|
||||
});
|
||||
m_editorContext->SetReadyStatus();
|
||||
|
||||
App::EditorWindowManager::CreateParams createParams = {};
|
||||
App::EditorWindowCreateParams createParams = {};
|
||||
createParams.windowId = "main";
|
||||
createParams.title = kWindowTitle;
|
||||
createParams.category = App::EditorWindowCategory::Workspace;
|
||||
@@ -304,11 +299,7 @@ int Application::Run(HINSTANCE hInstance, int nCmdShow) {
|
||||
"smoke",
|
||||
"auto-exit requested after duration/frame limit");
|
||||
m_smokeTestCloseRequested = true;
|
||||
if (App::EditorWindow* primaryWindow = m_windowManager->FindPrimaryWindow();
|
||||
primaryWindow != nullptr &&
|
||||
primaryWindow->GetHwnd() != nullptr) {
|
||||
PostMessageW(primaryWindow->GetHwnd(), WM_CLOSE, 0, 0);
|
||||
} else {
|
||||
if (!m_windowManager->RequestPrimaryWindowClose()) {
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,20 @@ namespace XCEngine::UI::Editor::App {
|
||||
using namespace EditorWindowSupport;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
|
||||
POINT EditorWindow::ToNativePoint(const EditorWindowScreenPoint& screenPoint) {
|
||||
return POINT{
|
||||
static_cast<LONG>(screenPoint.x),
|
||||
static_cast<LONG>(screenPoint.y),
|
||||
};
|
||||
}
|
||||
|
||||
EditorWindowScreenPoint EditorWindow::FromNativePoint(const POINT& screenPoint) {
|
||||
EditorWindowScreenPoint point = {};
|
||||
point.x = screenPoint.x;
|
||||
point.y = screenPoint.y;
|
||||
return point;
|
||||
}
|
||||
|
||||
EditorWindow::EditorWindow(
|
||||
std::string windowId,
|
||||
std::wstring title,
|
||||
@@ -135,6 +149,11 @@ bool EditorWindow::IsDestroyed() const {
|
||||
return m_session->IsDestroyed();
|
||||
}
|
||||
|
||||
bool EditorWindow::HasLiveHostWindow() const {
|
||||
const HWND hwnd = m_session->GetHwnd();
|
||||
return hwnd != nullptr && IsWindow(hwnd);
|
||||
}
|
||||
|
||||
bool EditorWindow::IsRenderReady() const {
|
||||
return m_runtime->IsReady();
|
||||
}
|
||||
@@ -204,6 +223,12 @@ void EditorWindow::SetTitle(std::wstring title) {
|
||||
m_session->SetTitle(std::move(title));
|
||||
}
|
||||
|
||||
void EditorWindow::ApplyHostWindowTitle() {
|
||||
if (HasLiveHostWindow()) {
|
||||
SetWindowTextW(m_session->GetHwnd(), GetTitle().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorWindow::RefreshWorkspaceProjection(EditorWorkspaceWindowProjection projection) {
|
||||
EditorWindowWorkspaceBinding* workspaceBinding = m_runtime->TryGetWorkspaceBinding();
|
||||
assert(workspaceBinding != nullptr);
|
||||
@@ -343,8 +368,9 @@ UIPoint EditorWindow::ConvertClientPixelsToDips(LONG x, LONG y) const {
|
||||
PixelsToDips(static_cast<float>(y)));
|
||||
}
|
||||
|
||||
UIPoint EditorWindow::ConvertScreenPixelsToClientDips(const POINT& screenPoint) const {
|
||||
POINT clientPoint = screenPoint;
|
||||
UIPoint EditorWindow::ConvertScreenPixelsToClientDips(
|
||||
const EditorWindowScreenPoint& screenPoint) const {
|
||||
POINT clientPoint = ToNativePoint(screenPoint);
|
||||
if (const HWND hwnd = m_session->GetHwnd();
|
||||
hwnd != nullptr) {
|
||||
ScreenToClient(hwnd, &clientPoint);
|
||||
@@ -363,8 +389,8 @@ UIPoint EditorWindow::ConvertScreenPixelsToClientDips(const POINT& screenPoint)
|
||||
bool EditorWindow::TryResolveDockTabDragHotspot(
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const POINT& screenPoint,
|
||||
POINT& outHotspot) const {
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
EditorWindowScreenPoint& outHotspot) const {
|
||||
const EditorWindowDockHostBinding* dockHostBinding = m_runtime->TryGetDockHostBinding();
|
||||
if (dockHostBinding == nullptr) {
|
||||
outHotspot = {};
|
||||
@@ -383,13 +409,13 @@ bool EditorWindow::TryResolveDockTabDragHotspot(
|
||||
}
|
||||
|
||||
const float dpiScale = GetDpiScale();
|
||||
outHotspot.x = static_cast<LONG>(std::lround(hotspotDips.x * dpiScale));
|
||||
outHotspot.y = static_cast<LONG>(std::lround(hotspotDips.y * dpiScale));
|
||||
outHotspot.x = static_cast<std::int32_t>(std::lround(hotspotDips.x * dpiScale));
|
||||
outHotspot.y = static_cast<std::int32_t>(std::lround(hotspotDips.y * dpiScale));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorWindow::TryResolveDockTabDropTarget(
|
||||
const POINT& screenPoint,
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
UIEditorDockHostTabDropTarget& outTarget) const {
|
||||
const EditorWindowDockHostBinding* dockHostBinding = m_runtime->TryGetDockHostBinding();
|
||||
if (dockHostBinding == nullptr) {
|
||||
@@ -402,6 +428,57 @@ bool EditorWindow::TryResolveDockTabDropTarget(
|
||||
return outTarget.valid;
|
||||
}
|
||||
|
||||
bool EditorWindow::TryGetHostScreenRect(EditorWindowScreenRect& outRect) const {
|
||||
outRect = {};
|
||||
|
||||
RECT nativeRect = {};
|
||||
if (!HasLiveHostWindow() || !GetWindowRect(m_session->GetHwnd(), &nativeRect)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outRect.left = nativeRect.left;
|
||||
outRect.top = nativeRect.top;
|
||||
outRect.right = nativeRect.right;
|
||||
outRect.bottom = nativeRect.bottom;
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorWindow::SetHostScreenPosition(const EditorWindowScreenPoint& screenPoint) {
|
||||
if (!HasLiveHostWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetWindowPos(
|
||||
m_session->GetHwnd(),
|
||||
nullptr,
|
||||
screenPoint.x,
|
||||
screenPoint.y,
|
||||
0,
|
||||
0,
|
||||
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
void EditorWindow::FocusHostWindow() {
|
||||
if (!HasLiveHostWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShowWindow(m_session->GetHwnd(), SW_RESTORE);
|
||||
SetForegroundWindow(m_session->GetHwnd());
|
||||
}
|
||||
|
||||
void EditorWindow::PostCloseToHost() {
|
||||
if (HasLiveHostWindow()) {
|
||||
PostMessageW(m_session->GetHwnd(), WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorWindow::DestroyHostWindow() {
|
||||
if (HasLiveHostWindow()) {
|
||||
DestroyWindow(m_session->GetHwnd());
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorWindow::OnResize(UINT width, UINT height) {
|
||||
const bool matchedPresentedPrediction =
|
||||
m_chromeController->ConsumePresentedPredictedClientPixelSizeMatch(width, height);
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "Platform/Win32/Windowing/EditorWindowPointerCapture.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowSession.h"
|
||||
#include "Windowing/Host/EditorWindowHostInterfaces.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -64,7 +64,7 @@ class EditorWindowWorkspaceCoordinator;
|
||||
class EditorWindowSession;
|
||||
struct EditorWorkspaceWindowProjection;
|
||||
|
||||
class EditorWindow {
|
||||
class EditorWindow final : public EditorHostWindow {
|
||||
public:
|
||||
EditorWindow(
|
||||
std::string windowId,
|
||||
@@ -80,25 +80,27 @@ public:
|
||||
EditorWindow(EditorWindow&&) = delete;
|
||||
EditorWindow& operator=(EditorWindow&&) = delete;
|
||||
|
||||
std::string_view GetWindowId() const;
|
||||
std::string_view GetWindowId() const override;
|
||||
HWND GetHwnd() const;
|
||||
bool HasHwnd() const;
|
||||
EditorWindowCategory GetCategory() const;
|
||||
const EditorWindowChromePolicy& GetChromePolicy() const;
|
||||
EditorWindowLifecycleState GetLifecycleState() const;
|
||||
bool IsPrimary() const;
|
||||
bool IsWorkspaceWindow() const;
|
||||
bool IsUtilityWindow() const;
|
||||
bool IsClosing() const;
|
||||
bool IsDestroyed() const;
|
||||
const std::wstring& GetTitle() const;
|
||||
EditorWindowLifecycleState GetLifecycleState() const override;
|
||||
bool IsPrimary() const override;
|
||||
bool IsWorkspaceWindow() const override;
|
||||
bool IsUtilityWindow() const override;
|
||||
bool IsClosing() const override;
|
||||
bool IsDestroyed() const override;
|
||||
bool HasLiveHostWindow() const override;
|
||||
const std::wstring& GetTitle() const override;
|
||||
std::string_view GetCachedTitleText() const;
|
||||
const UIEditorWorkspaceController* TryGetWorkspaceController() const;
|
||||
const EditorWorkspaceWindowProjection* TryGetWorkspaceProjection() const;
|
||||
const UIEditorWorkspaceController* TryGetWorkspaceController() const override;
|
||||
const EditorWorkspaceWindowProjection* TryGetWorkspaceProjection() const override;
|
||||
const UIEditorWorkspaceController& GetWorkspaceController() const;
|
||||
EditorWindowDockHostBinding* TryGetDockHostBinding();
|
||||
const EditorWindowDockHostBinding* TryGetDockHostBinding() const;
|
||||
::XCEngine::UI::UIPoint ConvertScreenPixelsToClientDips(const POINT& screenPoint) const;
|
||||
EditorWindowDockHostBinding* TryGetDockHostBinding() override;
|
||||
const EditorWindowDockHostBinding* TryGetDockHostBinding() const override;
|
||||
::XCEngine::UI::UIPoint ConvertScreenPixelsToClientDips(
|
||||
const EditorWindowScreenPoint& screenPoint) const override;
|
||||
|
||||
private:
|
||||
friend class EditorWindowChromeController;
|
||||
@@ -108,33 +110,39 @@ private:
|
||||
friend class EditorWindowLifecycleCoordinator;
|
||||
friend class EditorWindowWorkspaceCoordinator;
|
||||
|
||||
bool IsRenderReady() const;
|
||||
bool IsRenderReady() const override;
|
||||
bool TryResolveDockTabDragHotspot(
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const POINT& screenPoint,
|
||||
POINT& outHotspot) const;
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
EditorWindowScreenPoint& outHotspot) const override;
|
||||
bool TryResolveDockTabDropTarget(
|
||||
const POINT& screenPoint,
|
||||
UIEditorDockHostTabDropTarget& outTarget) const;
|
||||
void InvalidateHostWindow() const;
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
UIEditorDockHostTabDropTarget& outTarget) const override;
|
||||
void InvalidateHostWindow() const override;
|
||||
|
||||
void AttachHwnd(HWND hwnd);
|
||||
void MarkInitializing();
|
||||
void MarkRunning();
|
||||
void MarkDestroyed();
|
||||
void MarkClosing();
|
||||
void SetPrimary(bool primary);
|
||||
void SetTitle(std::wstring title);
|
||||
void RefreshWorkspaceProjection(EditorWorkspaceWindowProjection projection);
|
||||
void MarkDestroyed() override;
|
||||
void MarkClosing() override;
|
||||
void SetPrimary(bool primary) override;
|
||||
void SetTitle(std::wstring title) override;
|
||||
void ApplyHostWindowTitle() override;
|
||||
void RefreshWorkspaceProjection(EditorWorkspaceWindowProjection projection) override;
|
||||
|
||||
bool Initialize(
|
||||
const std::filesystem::path& repoRoot,
|
||||
EditorContext& editorContext,
|
||||
const std::filesystem::path& captureRoot,
|
||||
bool autoCaptureOnStartup);
|
||||
void Shutdown();
|
||||
void ResetInteractionState();
|
||||
void Shutdown() override;
|
||||
void ResetInteractionState() override;
|
||||
bool TryGetHostScreenRect(EditorWindowScreenRect& outRect) const override;
|
||||
void SetHostScreenPosition(const EditorWindowScreenPoint& screenPoint) override;
|
||||
void FocusHostWindow() override;
|
||||
void PostCloseToHost() override;
|
||||
void DestroyHostWindow() override;
|
||||
|
||||
EditorWindowFrameTransferRequests RenderFrame(
|
||||
EditorContext& editorContext,
|
||||
@@ -156,8 +164,8 @@ private:
|
||||
bool ApplyCurrentCursor() const;
|
||||
bool HasInteractiveCaptureState() const;
|
||||
bool OwnsPointerCapture(EditorWindowPointerCaptureOwner owner) const;
|
||||
void AcquirePointerCapture(EditorWindowPointerCaptureOwner owner);
|
||||
void ReleasePointerCapture(EditorWindowPointerCaptureOwner owner);
|
||||
void AcquirePointerCapture(EditorWindowPointerCaptureOwner owner) override;
|
||||
void ReleasePointerCapture(EditorWindowPointerCaptureOwner owner) override;
|
||||
void ForceReleasePointerCapture();
|
||||
void TryStartImmediateShellPointerCapture(LPARAM lParam);
|
||||
|
||||
@@ -179,6 +187,8 @@ private:
|
||||
::XCEngine::UI::UIRect ResolveWorkspaceBounds(
|
||||
float clientWidthDips,
|
||||
float clientHeightDips) const;
|
||||
static POINT ToNativePoint(const EditorWindowScreenPoint& screenPoint);
|
||||
static EditorWindowScreenPoint FromNativePoint(const POINT& screenPoint);
|
||||
EditorWindowFrameTransferRequests RenderRuntimeFrame(
|
||||
EditorContext& editorContext,
|
||||
bool globalTabDragActive,
|
||||
|
||||
19
editor/app/Platform/Win32/Windowing/EditorWindowHostConfig.h
Normal file
19
editor/app/Platform/Win32/Windowing/EditorWindowHostConfig.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
struct EditorWindowHostConfig {
|
||||
HINSTANCE hInstance = nullptr;
|
||||
const wchar_t* windowClassName = L"";
|
||||
DWORD windowStyle = 0;
|
||||
const wchar_t* primaryWindowTitle = L"";
|
||||
void* windowUserData = nullptr;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -3,13 +3,15 @@
|
||||
#include "Bootstrap/EditorResources.h"
|
||||
#include "Composition/EditorContext.h"
|
||||
#include "Platform/Win32/Chrome/EditorWindowChromeController.h"
|
||||
#include "Platform/Win32/Windowing/EditorFloatingWindowPlacement.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Windowing/Content/EditorWindowContentFactory.h"
|
||||
#include "Windowing/Content/EditorWindowContentController.h"
|
||||
#include "Platform/Win32/Runtime/EditorWindowFrameDriver.h"
|
||||
#include "Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorUtilityWindowCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h"
|
||||
#include "Windowing/System/EditorWindowSystem.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
|
||||
@@ -61,17 +63,19 @@ EditorWindowHostRuntime::EditorWindowHostRuntime(
|
||||
EditorWindowHostConfig hostConfig,
|
||||
std::filesystem::path repoRoot,
|
||||
EditorContext& editorContext,
|
||||
EditorWindowSystem& windowSystem,
|
||||
EditorWindowContentFactory& contentFactory)
|
||||
: m_hostConfig(hostConfig),
|
||||
m_repoRoot(std::move(repoRoot)),
|
||||
m_editorContext(editorContext),
|
||||
m_windowSystem(windowSystem),
|
||||
m_contentFactory(contentFactory) {}
|
||||
|
||||
EditorWindowHostRuntime::~EditorWindowHostRuntime() = default;
|
||||
|
||||
EditorWindow* EditorWindowHostRuntime::CreateEditorWindow(
|
||||
std::unique_ptr<EditorWindowContentController> contentController,
|
||||
const CreateParams& params) {
|
||||
const EditorWindowCreateParams& params) {
|
||||
if (contentController == nullptr) {
|
||||
LogRuntimeTrace("window", "window creation failed: content controller is null");
|
||||
return nullptr;
|
||||
@@ -113,14 +117,24 @@ EditorWindow* EditorWindowHostRuntime::CreateEditorWindow(
|
||||
m_pendingCreateWindow = rawWindow;
|
||||
const DWORD windowStyle = params.nativeStylePolicy.useHostWindowStyle
|
||||
? m_hostConfig.windowStyle
|
||||
: params.nativeStylePolicy.windowStyle;
|
||||
: static_cast<DWORD>(params.nativeStylePolicy.windowStyle);
|
||||
const DWORD extendedWindowStyle =
|
||||
params.nativeStylePolicy.extendedWindowStyle != 0u
|
||||
? static_cast<DWORD>(params.nativeStylePolicy.extendedWindowStyle)
|
||||
: WS_EX_APPWINDOW;
|
||||
const int initialX = params.initialX == kEditorWindowDefaultPosition
|
||||
? CW_USEDEFAULT
|
||||
: params.initialX;
|
||||
const int initialY = params.initialY == kEditorWindowDefaultPosition
|
||||
? CW_USEDEFAULT
|
||||
: params.initialY;
|
||||
const HWND hwnd = CreateWindowExW(
|
||||
params.nativeStylePolicy.extendedWindowStyle,
|
||||
extendedWindowStyle,
|
||||
m_hostConfig.windowClassName,
|
||||
rawWindow->GetTitle().c_str(),
|
||||
windowStyle,
|
||||
params.initialX,
|
||||
params.initialY,
|
||||
initialX,
|
||||
initialY,
|
||||
params.initialWidth,
|
||||
params.initialHeight,
|
||||
nullptr,
|
||||
@@ -181,17 +195,18 @@ EditorWindow* EditorWindowHostRuntime::CreateEditorWindow(
|
||||
|
||||
EditorWindow* EditorWindowHostRuntime::CreateWorkspaceWindow(
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
const CreateParams& params) {
|
||||
const EditorWindowCreateParams& params) {
|
||||
return CreateEditorWindow(
|
||||
m_contentFactory.CreateWorkspaceContentController(
|
||||
params.windowId,
|
||||
std::move(workspaceController)),
|
||||
std::move(workspaceController),
|
||||
m_windowSystem),
|
||||
params);
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowHostRuntime::CreateUtilityWindow(
|
||||
const EditorUtilityWindowDescriptor& descriptor,
|
||||
const CreateParams& params) {
|
||||
const EditorWindowCreateParams& params) {
|
||||
return CreateEditorWindow(
|
||||
m_contentFactory.CreateUtilityContentController(descriptor),
|
||||
params);
|
||||
@@ -215,6 +230,111 @@ bool EditorWindowHostRuntime::HasWindows() const {
|
||||
return !m_windows.empty();
|
||||
}
|
||||
|
||||
std::vector<EditorHostWindow*> EditorWindowHostRuntime::GetWindows() {
|
||||
std::vector<EditorHostWindow*> windows = {};
|
||||
windows.reserve(m_windows.size());
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
|
||||
if (window != nullptr) {
|
||||
windows.push_back(window.get());
|
||||
}
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
std::vector<const EditorHostWindow*> EditorWindowHostRuntime::GetWindows() const {
|
||||
std::vector<const EditorHostWindow*> windows = {};
|
||||
windows.reserve(m_windows.size());
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
|
||||
if (window != nullptr) {
|
||||
windows.push_back(window.get());
|
||||
}
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
std::wstring_view EditorWindowHostRuntime::GetPrimaryWindowTitle() const {
|
||||
return m_hostConfig.primaryWindowTitle != nullptr
|
||||
? std::wstring_view(m_hostConfig.primaryWindowTitle)
|
||||
: std::wstring_view{};
|
||||
}
|
||||
|
||||
bool EditorWindowHostRuntime::TryGetCursorScreenPoint(
|
||||
EditorWindowScreenPoint& outPoint) const {
|
||||
POINT nativePoint = {};
|
||||
if (!GetCursorPos(&nativePoint)) {
|
||||
outPoint = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
outPoint.x = nativePoint.x;
|
||||
outPoint.y = nativePoint.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
EditorWindowScreenRect EditorWindowHostRuntime::ResolveFloatingPlacement(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
int preferredWidth,
|
||||
int preferredHeight) const {
|
||||
POINT nativePoint = {};
|
||||
nativePoint.x = screenPoint.x;
|
||||
nativePoint.y = screenPoint.y;
|
||||
const RECT nativeRect = BuildEditorFloatingWindowRect(
|
||||
nativePoint,
|
||||
preferredWidth,
|
||||
preferredHeight);
|
||||
EditorWindowScreenRect rect = {};
|
||||
rect.left = nativeRect.left;
|
||||
rect.top = nativeRect.top;
|
||||
rect.right = nativeRect.right;
|
||||
rect.bottom = nativeRect.bottom;
|
||||
return rect;
|
||||
}
|
||||
|
||||
EditorHostWindow* EditorWindowHostRuntime::FindWindowFromScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint) {
|
||||
POINT nativePoint = {};
|
||||
nativePoint.x = screenPoint.x;
|
||||
nativePoint.y = screenPoint.y;
|
||||
const HWND hitWindow = WindowFromPoint(nativePoint);
|
||||
if (hitWindow == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return FindWindow(GetAncestor(hitWindow, GA_ROOT));
|
||||
}
|
||||
|
||||
const EditorHostWindow* EditorWindowHostRuntime::FindWindowFromScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint) const {
|
||||
return const_cast<EditorWindowHostRuntime*>(this)->FindWindowFromScreenPoint(screenPoint);
|
||||
}
|
||||
|
||||
void EditorWindowHostRuntime::ReapDestroyedWindows() {
|
||||
for (auto it = m_windows.begin(); it != m_windows.end();) {
|
||||
EditorWindow* const window = it->get();
|
||||
if (window == nullptr || !window->IsDestroyed()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_pendingCreateWindow == window) {
|
||||
m_pendingCreateWindow = nullptr;
|
||||
}
|
||||
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ReapDestroyedWindows erase windowId='" + std::string(window->GetWindowId()) +
|
||||
"' hostBefore=" + DescribeHostWindows(m_windows));
|
||||
it = m_windows.erase(it);
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ReapDestroyedWindows erase end hostAfter=" + DescribeHostWindows(m_windows));
|
||||
}
|
||||
}
|
||||
|
||||
std::string EditorWindowHostRuntime::DescribeWindows() const {
|
||||
return DescribeHostWindows(m_windows);
|
||||
}
|
||||
|
||||
void EditorWindowHostRuntime::RenderAllWindows(
|
||||
bool globalTabDragActive,
|
||||
EditorWindowWorkspaceCoordinator& workspaceCoordinator,
|
||||
@@ -229,7 +349,7 @@ void EditorWindowHostRuntime::RenderAllWindows(
|
||||
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_windows) {
|
||||
if (window == nullptr ||
|
||||
window->GetHwnd() == nullptr ||
|
||||
!window->HasLiveHostWindow() ||
|
||||
window->GetLifecycleState() != EditorWindowLifecycleState::Running) {
|
||||
continue;
|
||||
}
|
||||
@@ -257,7 +377,7 @@ void EditorWindowHostRuntime::RenderAllWindows(
|
||||
|
||||
for (WindowFrameTransferBatch& batch : transferBatches) {
|
||||
if (batch.sourceWindow == nullptr ||
|
||||
batch.sourceWindow->GetHwnd() == nullptr ||
|
||||
!batch.sourceWindow->HasLiveHostWindow() ||
|
||||
batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) {
|
||||
continue;
|
||||
}
|
||||
@@ -289,7 +409,7 @@ const EditorWindow* EditorWindowHostRuntime::FindWindow(HWND hwnd) const {
|
||||
return const_cast<EditorWindowHostRuntime*>(this)->FindWindow(hwnd);
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowHostRuntime::FindWindow(std::string_view windowId) {
|
||||
EditorWindow* EditorWindowHostRuntime::FindWindowByIdImpl(std::string_view windowId) {
|
||||
if (windowId.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -303,8 +423,16 @@ EditorWindow* EditorWindowHostRuntime::FindWindow(std::string_view windowId) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const EditorWindow* EditorWindowHostRuntime::FindWindow(std::string_view windowId) const {
|
||||
return const_cast<EditorWindowHostRuntime*>(this)->FindWindow(windowId);
|
||||
const EditorWindow* EditorWindowHostRuntime::FindWindowByIdImpl(std::string_view windowId) const {
|
||||
return const_cast<EditorWindowHostRuntime*>(this)->FindWindowByIdImpl(windowId);
|
||||
}
|
||||
|
||||
EditorHostWindow* EditorWindowHostRuntime::FindWindowById(std::string_view windowId) {
|
||||
return FindWindowByIdImpl(windowId);
|
||||
}
|
||||
|
||||
const EditorHostWindow* EditorWindowHostRuntime::FindWindowById(std::string_view windowId) const {
|
||||
return FindWindowByIdImpl(windowId);
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowHostRuntime::FindPrimaryWindow() {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Platform/Win32/Windowing/EditorWindowManager.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostConfig.h"
|
||||
#include "Windowing/Host/EditorWindowHostInterfaces.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
|
||||
@@ -16,41 +18,55 @@ class EditorWindow;
|
||||
class EditorWindowContentController;
|
||||
class EditorWindowContentFactory;
|
||||
class EditorWindowLifecycleCoordinator;
|
||||
class EditorWindowSystem;
|
||||
class EditorUtilityWindowCoordinator;
|
||||
class EditorWindowWorkspaceCoordinator;
|
||||
struct EditorUtilityWindowDescriptor;
|
||||
|
||||
class EditorWindowHostRuntime final {
|
||||
class EditorWindowHostRuntime final : public EditorWindowHost {
|
||||
public:
|
||||
using CreateParams = EditorWindowManager::CreateParams;
|
||||
|
||||
EditorWindowHostRuntime(
|
||||
EditorWindowHostConfig hostConfig,
|
||||
std::filesystem::path repoRoot,
|
||||
EditorContext& editorContext,
|
||||
EditorWindowSystem& windowSystem,
|
||||
EditorWindowContentFactory& contentFactory);
|
||||
~EditorWindowHostRuntime();
|
||||
|
||||
EditorWindow* CreateEditorWindow(
|
||||
std::unique_ptr<EditorWindowContentController> contentController,
|
||||
const CreateParams& params);
|
||||
const EditorWindowCreateParams& params);
|
||||
EditorWindow* CreateWorkspaceWindow(
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
const CreateParams& params);
|
||||
const EditorWindowCreateParams& params) override;
|
||||
EditorWindow* CreateUtilityWindow(
|
||||
const EditorUtilityWindowDescriptor& descriptor,
|
||||
const CreateParams& params);
|
||||
const EditorWindowCreateParams& params) override;
|
||||
void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator);
|
||||
void HandlePendingNativeWindowCreated(HWND hwnd);
|
||||
|
||||
EditorWindow* FindWindow(HWND hwnd);
|
||||
const EditorWindow* FindWindow(HWND hwnd) const;
|
||||
EditorWindow* FindWindow(std::string_view windowId);
|
||||
const EditorWindow* FindWindow(std::string_view windowId) const;
|
||||
EditorHostWindow* FindWindowById(std::string_view windowId) override;
|
||||
const EditorHostWindow* FindWindowById(std::string_view windowId) const override;
|
||||
EditorWindow* FindPrimaryWindow();
|
||||
const EditorWindow* FindPrimaryWindow() const;
|
||||
|
||||
bool HasWindows() const;
|
||||
std::vector<EditorHostWindow*> GetWindows() override;
|
||||
std::vector<const EditorHostWindow*> GetWindows() const override;
|
||||
std::wstring_view GetPrimaryWindowTitle() const override;
|
||||
bool TryGetCursorScreenPoint(EditorWindowScreenPoint& outPoint) const override;
|
||||
EditorWindowScreenRect ResolveFloatingPlacement(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
int preferredWidth,
|
||||
int preferredHeight) const override;
|
||||
EditorHostWindow* FindWindowFromScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint) override;
|
||||
const EditorHostWindow* FindWindowFromScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint) const override;
|
||||
void ReapDestroyedWindows() override;
|
||||
std::string DescribeWindows() const override;
|
||||
void RenderAllWindows(
|
||||
bool globalTabDragActive,
|
||||
EditorWindowWorkspaceCoordinator& workspaceCoordinator,
|
||||
@@ -72,22 +88,24 @@ public:
|
||||
return m_repoRoot;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<EditorWindow>>& GetWindows() {
|
||||
std::vector<std::unique_ptr<EditorWindow>>& GetWindowStorage() {
|
||||
return m_windows;
|
||||
}
|
||||
|
||||
const std::vector<std::unique_ptr<EditorWindow>>& GetWindows() const {
|
||||
const std::vector<std::unique_ptr<EditorWindow>>& GetWindowStorage() const {
|
||||
return m_windows;
|
||||
}
|
||||
|
||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const override;
|
||||
|
||||
private:
|
||||
friend class EditorWindowLifecycleCoordinator;
|
||||
EditorWindow* FindWindowByIdImpl(std::string_view windowId);
|
||||
const EditorWindow* FindWindowByIdImpl(std::string_view windowId) const;
|
||||
|
||||
EditorWindowHostConfig m_hostConfig = {};
|
||||
std::filesystem::path m_repoRoot = {};
|
||||
EditorContext& m_editorContext;
|
||||
EditorWindowSystem& m_windowSystem;
|
||||
EditorWindowContentFactory& m_contentFactory;
|
||||
std::vector<std::unique_ptr<EditorWindow>> m_windows = {};
|
||||
EditorWindow* m_pendingCreateWindow = nullptr;
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#include "Platform/Win32/Runtime/EditorWindowRuntimeController.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowPointerCapture.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostRuntime.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorUtilityWindowCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
@@ -23,38 +23,6 @@ namespace {
|
||||
constexpr UINT kMessageNcUaDrawCaption = 0x00AEu;
|
||||
constexpr UINT kMessageNcUaDrawFrame = 0x00AFu;
|
||||
|
||||
std::string DescribeHwnd(HWND hwnd) {
|
||||
std::ostringstream stream = {};
|
||||
stream << "0x" << std::hex << std::uppercase
|
||||
<< reinterpret_cast<std::uintptr_t>(hwnd);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string DescribeHostWindows(const EditorWindowHostRuntime& hostRuntime) {
|
||||
std::ostringstream stream = {};
|
||||
const auto& windows = hostRuntime.GetWindows();
|
||||
stream << "count=" << windows.size() << " [";
|
||||
bool first = true;
|
||||
for (const std::unique_ptr<EditorWindow>& window : windows) {
|
||||
if (!first) {
|
||||
stream << ", ";
|
||||
}
|
||||
first = false;
|
||||
if (window == nullptr) {
|
||||
stream << "<null>";
|
||||
continue;
|
||||
}
|
||||
|
||||
stream << window->GetWindowId()
|
||||
<< "{hwnd=" << DescribeHwnd(window->GetHwnd())
|
||||
<< ",primary=" << (window->IsPrimary() ? '1' : '0')
|
||||
<< ",state=" << GetEditorWindowLifecycleStateName(window->GetLifecycleState())
|
||||
<< '}';
|
||||
}
|
||||
stream << ']';
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct EditorWindowMessageDispatcher::DispatchContext {
|
||||
@@ -185,7 +153,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowPointerMessage(
|
||||
if (CanRouteEditorWindowGlobalTabDragPointerMessages(
|
||||
inputController.GetPointerCaptureOwner(),
|
||||
context.workspaceCoordinator.OwnsActiveGlobalTabDrag(context.window.GetWindowId())) &&
|
||||
context.workspaceCoordinator.HandleGlobalTabDragPointerMove(context.hwnd)) {
|
||||
context.workspaceCoordinator.HandleGlobalTabDragPointerMove(context.window)) {
|
||||
outResult = 0;
|
||||
return true;
|
||||
}
|
||||
@@ -274,7 +242,7 @@ bool EditorWindowMessageDispatcher::TryDispatchWindowPointerMessage(
|
||||
if (CanRouteEditorWindowGlobalTabDragPointerMessages(
|
||||
inputController.GetPointerCaptureOwner(),
|
||||
context.workspaceCoordinator.OwnsActiveGlobalTabDrag(context.window.GetWindowId())) &&
|
||||
context.workspaceCoordinator.HandleGlobalTabDragPointerButtonUp(context.hwnd)) {
|
||||
context.workspaceCoordinator.HandleGlobalTabDragPointerButtonUp(context.window)) {
|
||||
outResult = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,42 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
enum class EditorWindowPointerCaptureOwner : std::uint8_t {
|
||||
None = 0,
|
||||
Shell,
|
||||
HostedContent,
|
||||
BorderlessResize,
|
||||
BorderlessChrome,
|
||||
GlobalTabDrag,
|
||||
};
|
||||
|
||||
constexpr bool CanRouteEditorWindowGlobalTabDragPointerMessages(
|
||||
EditorWindowPointerCaptureOwner owner,
|
||||
bool ownsActiveGlobalTabDrag) {
|
||||
return ownsActiveGlobalTabDrag &&
|
||||
owner == EditorWindowPointerCaptureOwner::GlobalTabDrag;
|
||||
}
|
||||
|
||||
constexpr bool CanRouteEditorWindowBorderlessResizePointerMessages(
|
||||
EditorWindowPointerCaptureOwner owner) {
|
||||
return owner == EditorWindowPointerCaptureOwner::BorderlessResize;
|
||||
}
|
||||
|
||||
constexpr bool CanRouteEditorWindowBorderlessChromePointerMessages(
|
||||
EditorWindowPointerCaptureOwner owner) {
|
||||
return owner == EditorWindowPointerCaptureOwner::BorderlessChrome;
|
||||
}
|
||||
|
||||
constexpr bool CanConsumeEditorWindowChromeHover(
|
||||
EditorWindowPointerCaptureOwner owner,
|
||||
bool shellInteractiveCaptureActive,
|
||||
bool hostedContentCaptureActive) {
|
||||
return owner == EditorWindowPointerCaptureOwner::None &&
|
||||
!shellInteractiveCaptureActive &&
|
||||
!hostedContentCaptureActive;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
#include "Windowing/Host/EditorWindowPointerCapture.h"
|
||||
|
||||
@@ -4,73 +4,14 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "Windowing/Host/EditorWindowTypes.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
enum class EditorWindowLifecycleState : std::uint8_t {
|
||||
PendingNativeCreate = 0,
|
||||
NativeAttached,
|
||||
Initializing,
|
||||
Running,
|
||||
Closing,
|
||||
Destroyed,
|
||||
};
|
||||
|
||||
enum class EditorWindowCategory : std::uint8_t {
|
||||
Workspace = 0,
|
||||
Utility,
|
||||
};
|
||||
|
||||
inline std::string_view GetEditorWindowCategoryName(
|
||||
EditorWindowCategory category) {
|
||||
switch (category) {
|
||||
case EditorWindowCategory::Workspace:
|
||||
return "Workspace";
|
||||
case EditorWindowCategory::Utility:
|
||||
return "Utility";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
struct EditorWindowChromePolicy {
|
||||
bool allowDetachedTitleBarTabStrip = true;
|
||||
bool showFrameStats = true;
|
||||
bool showTopmostButton = false;
|
||||
bool topmostByDefault = false;
|
||||
};
|
||||
|
||||
struct EditorWindowNativeStylePolicy {
|
||||
DWORD extendedWindowStyle = WS_EX_APPWINDOW;
|
||||
DWORD windowStyle = 0;
|
||||
bool useHostWindowStyle = true;
|
||||
};
|
||||
|
||||
inline std::string_view GetEditorWindowLifecycleStateName(
|
||||
EditorWindowLifecycleState state) {
|
||||
switch (state) {
|
||||
case EditorWindowLifecycleState::PendingNativeCreate:
|
||||
return "PendingNativeCreate";
|
||||
case EditorWindowLifecycleState::NativeAttached:
|
||||
return "NativeAttached";
|
||||
case EditorWindowLifecycleState::Initializing:
|
||||
return "Initializing";
|
||||
case EditorWindowLifecycleState::Running:
|
||||
return "Running";
|
||||
case EditorWindowLifecycleState::Closing:
|
||||
return "Closing";
|
||||
case EditorWindowLifecycleState::Destroyed:
|
||||
return "Destroyed";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
struct EditorWindowWindowState {
|
||||
HWND hwnd = nullptr;
|
||||
std::string windowId = {};
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
#include "Features/ColorPicker/ColorPickerPanel.h"
|
||||
#include "Features/Inspector/AddComponentPanel.h"
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "UtilityWindows/EditorUtilityWindowKind.h"
|
||||
#include "UtilityWindows/EditorUtilityWindowPanel.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowState.h"
|
||||
#include "Windowing/Host/EditorWindowTypes.h"
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Windowing/Content/EditorUtilityWindowContentController.h"
|
||||
#include "Windowing/Content/EditorWorkspaceWindowContentController.h"
|
||||
#include "Windowing/System/EditorWindowSystem.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -13,10 +14,12 @@ class DefaultEditorWindowContentFactory final : public EditorWindowContentFactor
|
||||
public:
|
||||
std::unique_ptr<EditorWindowContentController> CreateWorkspaceContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController) const override {
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
EditorWindowSystem& windowSystem) const override {
|
||||
return CreateEditorWorkspaceWindowContentController(
|
||||
windowId,
|
||||
std::move(workspaceController));
|
||||
std::move(workspaceController),
|
||||
windowSystem);
|
||||
}
|
||||
|
||||
std::unique_ptr<EditorWindowContentController> CreateUtilityContentController(
|
||||
|
||||
@@ -12,6 +12,7 @@ class UIEditorWorkspaceController;
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorWindowContentController;
|
||||
class EditorWindowSystem;
|
||||
struct EditorUtilityWindowDescriptor;
|
||||
|
||||
class EditorWindowContentFactory {
|
||||
@@ -20,7 +21,8 @@ public:
|
||||
|
||||
virtual std::unique_ptr<EditorWindowContentController> CreateWorkspaceContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController) const = 0;
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
EditorWindowSystem& windowSystem) const = 0;
|
||||
virtual std::unique_ptr<EditorWindowContentController> CreateUtilityContentController(
|
||||
const EditorUtilityWindowDescriptor& descriptor) const = 0;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "Windowing/Content/EditorWorkspaceWindowContentController.h"
|
||||
|
||||
#include "Windowing/System/EditorWindowPresentationPolicy.h"
|
||||
#include "Windowing/System/EditorWindowSystem.h"
|
||||
|
||||
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
|
||||
#include <XCEditor/Workspace/UIEditorDetachedWindowPolicy.h>
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceLayoutPersistence.h>
|
||||
|
||||
@@ -49,10 +53,12 @@ EditorWorkspaceWindowProjection BuildWorkspaceProjectionFromController(
|
||||
|
||||
EditorWorkspaceWindowContentController::EditorWorkspaceWindowContentController(
|
||||
std::string windowId,
|
||||
UIEditorWorkspaceController workspaceController)
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
EditorWindowSystem& windowSystem)
|
||||
: m_windowId(std::move(windowId)),
|
||||
m_workspaceController(std::move(workspaceController)) {
|
||||
m_projection = BuildWorkspaceProjectionFromController(m_workspaceController, m_windowId);
|
||||
m_workspaceController(std::move(workspaceController)),
|
||||
m_windowSystem(windowSystem) {
|
||||
RefreshProjectionFromWorkspaceController();
|
||||
}
|
||||
|
||||
EditorWorkspaceWindowContentController::~EditorWorkspaceWindowContentController() = default;
|
||||
@@ -125,6 +131,27 @@ void EditorWorkspaceWindowContentController::RefreshWorkspaceProjection(
|
||||
m_projection = std::move(projection);
|
||||
}
|
||||
|
||||
void EditorWorkspaceWindowContentController::RefreshProjectionFromWorkspaceController(bool primary) {
|
||||
const std::wstring preservedWindowTitle = m_projection.windowTitle;
|
||||
m_projection = BuildWorkspaceProjectionFromController(m_workspaceController, m_windowId);
|
||||
if (!primary) {
|
||||
m_projection.windowTitle = ResolveEditorWindowPresentationTitle(
|
||||
std::wstring_view{},
|
||||
m_workspaceController.GetPanelRegistry(),
|
||||
m_projection.windowState,
|
||||
false);
|
||||
} else if (!preservedWindowTitle.empty()) {
|
||||
m_projection.windowTitle = preservedWindowTitle;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorWorkspaceWindowContentController::RestoreWorkspaceControllerFromProjection() {
|
||||
m_workspaceController = UIEditorWorkspaceController(
|
||||
m_workspaceController.GetPanelRegistry(),
|
||||
m_projection.windowState.workspace,
|
||||
m_projection.windowState.session);
|
||||
}
|
||||
|
||||
void EditorWorkspaceWindowContentController::Initialize(
|
||||
const EditorWindowContentInitializationContext& context) {
|
||||
m_shellRuntime.Initialize(context.repoRoot, context.textureHost, context.textMeasurer);
|
||||
@@ -165,14 +192,19 @@ EditorWindowFrameTransferRequests EditorWorkspaceWindowContentController::Update
|
||||
m_workspaceController.GetWorkspace(),
|
||||
m_workspaceController.GetSession());
|
||||
if (!AreUIEditorWorkspaceLayoutSnapshotsEquivalent(beforeSnapshot, afterSnapshot)) {
|
||||
transferRequests.workspace.workspaceMutation = EditorWindowWorkspaceMutationRequest{
|
||||
.windowState =
|
||||
UIEditorWindowWorkspaceState{
|
||||
.windowId = m_windowId,
|
||||
.workspace = m_workspaceController.GetWorkspace(),
|
||||
.session = m_workspaceController.GetSession(),
|
||||
},
|
||||
};
|
||||
std::string error = {};
|
||||
if (!m_windowSystem.CommitLiveWindowMutation(
|
||||
m_windowId,
|
||||
m_workspaceController,
|
||||
error)) {
|
||||
AppendUIEditorRuntimeTrace(
|
||||
"window",
|
||||
"workspace direct commit rejected for window '" +
|
||||
m_windowId + "': " + error);
|
||||
RestoreWorkspaceControllerFromProjection();
|
||||
} else {
|
||||
RefreshProjectionFromWorkspaceController(context.primary);
|
||||
}
|
||||
}
|
||||
return transferRequests;
|
||||
}
|
||||
@@ -263,10 +295,12 @@ std::string EditorWorkspaceWindowContentController::ResolveDetachedWindowTitleTe
|
||||
|
||||
std::unique_ptr<EditorWindowContentController> CreateEditorWorkspaceWindowContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController) {
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
EditorWindowSystem& windowSystem) {
|
||||
return std::make_unique<EditorWorkspaceWindowContentController>(
|
||||
std::string(windowId),
|
||||
std::move(workspaceController));
|
||||
std::move(workspaceController),
|
||||
windowSystem);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorWindowSystem;
|
||||
|
||||
class EditorWorkspaceWindowContentController final
|
||||
: public EditorWindowContentController
|
||||
, public EditorWindowWorkspaceBinding
|
||||
@@ -19,7 +21,8 @@ class EditorWorkspaceWindowContentController final
|
||||
public:
|
||||
EditorWorkspaceWindowContentController(
|
||||
std::string windowId,
|
||||
UIEditorWorkspaceController workspaceController);
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
EditorWindowSystem& windowSystem);
|
||||
~EditorWorkspaceWindowContentController() override;
|
||||
|
||||
EditorWindowContentCapabilities GetCapabilities() const override;
|
||||
@@ -72,8 +75,12 @@ public:
|
||||
std::string_view fallbackWindowTitle) const override;
|
||||
|
||||
private:
|
||||
void RefreshProjectionFromWorkspaceController(bool primary = false);
|
||||
void RestoreWorkspaceControllerFromProjection();
|
||||
|
||||
std::string m_windowId = {};
|
||||
UIEditorWorkspaceController m_workspaceController = {};
|
||||
EditorWindowSystem& m_windowSystem;
|
||||
EditorWorkspaceWindowProjection m_projection = {};
|
||||
EditorShellRuntime m_shellRuntime = {};
|
||||
EditorWindowFrameOrchestrator m_frameOrchestrator = {};
|
||||
@@ -81,6 +88,7 @@ private:
|
||||
|
||||
std::unique_ptr<EditorWindowContentController> CreateEditorWorkspaceWindowContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController);
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
EditorWindowSystem& windowSystem);
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
#include "Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorUtilityWindowCoordinator.h"
|
||||
|
||||
#include "UtilityWindows/EditorUtilityWindowRegistry.h"
|
||||
#include "Platform/Win32/Windowing/EditorFloatingWindowPlacement.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostRuntime.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -12,29 +9,22 @@ namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsLiveWindow(const EditorWindow* window) {
|
||||
bool IsLiveWindow(const EditorHostWindow* window) {
|
||||
return window != nullptr &&
|
||||
window->GetHwnd() != nullptr &&
|
||||
window->HasLiveHostWindow() &&
|
||||
window->GetLifecycleState() == EditorWindowLifecycleState::Running;
|
||||
}
|
||||
|
||||
LONG ResolveOuterDimension(float value, LONG fallback) {
|
||||
int ResolveOuterDimension(float value, int fallback) {
|
||||
return value > 0.0f
|
||||
? static_cast<LONG>(std::lround(value))
|
||||
? static_cast<int>(std::lround(value))
|
||||
: fallback;
|
||||
}
|
||||
|
||||
POINT ToNativePoint(const EditorWindowScreenPoint& point) {
|
||||
return POINT{
|
||||
static_cast<LONG>(point.x),
|
||||
static_cast<LONG>(point.y),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EditorUtilityWindowCoordinator::EditorUtilityWindowCoordinator(
|
||||
EditorWindowHostRuntime& hostRuntime)
|
||||
EditorWindowHost& hostRuntime)
|
||||
: m_hostRuntime(hostRuntime) {}
|
||||
|
||||
EditorUtilityWindowCoordinator::~EditorUtilityWindowCoordinator() = default;
|
||||
@@ -45,7 +35,7 @@ void EditorUtilityWindowCoordinator::BindLifecycleCoordinator(
|
||||
}
|
||||
|
||||
void EditorUtilityWindowCoordinator::HandleWindowFrameTransferRequests(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowFrameTransferRequests& transferRequests) {
|
||||
if (transferRequests.utility.openUtilityWindow.has_value() &&
|
||||
transferRequests.utility.openUtilityWindow->IsValid()) {
|
||||
@@ -56,7 +46,7 @@ void EditorUtilityWindowCoordinator::HandleWindowFrameTransferRequests(
|
||||
}
|
||||
|
||||
bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowOpenUtilityWindowRequest& request) {
|
||||
if (!IsLiveWindow(&sourceWindow)) {
|
||||
LogRuntimeTrace(
|
||||
@@ -73,13 +63,13 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
}
|
||||
|
||||
if (descriptor->reusePolicy == EditorUtilityWindowReusePolicy::SingleInstance) {
|
||||
if (EditorWindow* existingWindow = m_hostRuntime.FindWindow(descriptor->windowId);
|
||||
if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId);
|
||||
existingWindow != nullptr && existingWindow->IsDestroyed() &&
|
||||
m_lifecycleCoordinator != nullptr) {
|
||||
m_lifecycleCoordinator->ReapDestroyedWindows();
|
||||
}
|
||||
|
||||
if (EditorWindow* existingWindow = m_hostRuntime.FindWindow(descriptor->windowId);
|
||||
if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId);
|
||||
IsLiveWindow(existingWindow)) {
|
||||
if (!existingWindow->IsUtilityWindow()) {
|
||||
LogRuntimeTrace(
|
||||
@@ -87,14 +77,14 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
"open utility window request rejected: existing window id is not utility");
|
||||
return false;
|
||||
}
|
||||
FocusWindow(*existingWindow);
|
||||
existingWindow->FocusHostWindow();
|
||||
LogRuntimeTrace(
|
||||
"utility",
|
||||
"reused utility window '" + std::string(descriptor->windowId) + "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (EditorWindow* existingWindow = m_hostRuntime.FindWindow(descriptor->windowId);
|
||||
if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId);
|
||||
existingWindow != nullptr) {
|
||||
LogRuntimeTrace(
|
||||
"utility",
|
||||
@@ -103,7 +93,7 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
}
|
||||
}
|
||||
|
||||
EditorWindowHostRuntime::CreateParams createParams = {};
|
||||
EditorWindowCreateParams createParams = {};
|
||||
createParams.windowId = std::string(descriptor->windowId);
|
||||
createParams.title = descriptor->title;
|
||||
createParams.category = EditorWindowCategory::Utility;
|
||||
@@ -117,39 +107,30 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest(
|
||||
descriptor->preferredOuterSize.height,
|
||||
createParams.initialHeight);
|
||||
if (request.useCursorPlacement) {
|
||||
const RECT rect = BuildEditorFloatingWindowRect(
|
||||
ToNativePoint(request.screenPoint),
|
||||
const EditorWindowScreenRect rect = m_hostRuntime.ResolveFloatingPlacement(
|
||||
request.screenPoint,
|
||||
createParams.initialWidth,
|
||||
createParams.initialHeight);
|
||||
createParams.initialX = rect.left;
|
||||
createParams.initialY = rect.top;
|
||||
createParams.initialWidth = rect.right - rect.left;
|
||||
createParams.initialHeight = rect.bottom - rect.top;
|
||||
createParams.initialWidth = rect.Width();
|
||||
createParams.initialHeight = rect.Height();
|
||||
}
|
||||
|
||||
EditorWindow* utilityWindow =
|
||||
EditorHostWindow* utilityWindow =
|
||||
m_hostRuntime.CreateUtilityWindow(*descriptor, createParams);
|
||||
if (utilityWindow == nullptr) {
|
||||
LogRuntimeTrace("utility", "failed to create utility window");
|
||||
return false;
|
||||
}
|
||||
|
||||
FocusWindow(*utilityWindow);
|
||||
utilityWindow->FocusHostWindow();
|
||||
LogRuntimeTrace(
|
||||
"utility",
|
||||
"opened utility window '" + std::string(descriptor->windowId) + "'");
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorUtilityWindowCoordinator::FocusWindow(EditorWindow& window) const {
|
||||
if (!IsLiveWindow(&window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShowWindow(window.GetHwnd(), SW_RESTORE);
|
||||
SetForegroundWindow(window.GetHwnd());
|
||||
}
|
||||
|
||||
void EditorUtilityWindowCoordinator::LogRuntimeTrace(
|
||||
std::string_view channel,
|
||||
std::string_view message) const {
|
||||
@@ -1,33 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "Windowing/Frame/EditorWindowTransferRequests.h"
|
||||
#include "Windowing/Host/EditorWindowHostInterfaces.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorWindow;
|
||||
class EditorWindowHostRuntime;
|
||||
class EditorWindowLifecycleCoordinator;
|
||||
|
||||
class EditorUtilityWindowCoordinator final {
|
||||
public:
|
||||
explicit EditorUtilityWindowCoordinator(EditorWindowHostRuntime& hostRuntime);
|
||||
explicit EditorUtilityWindowCoordinator(EditorWindowHost& hostRuntime);
|
||||
~EditorUtilityWindowCoordinator();
|
||||
|
||||
void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator);
|
||||
void HandleWindowFrameTransferRequests(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowFrameTransferRequests& transferRequests);
|
||||
|
||||
private:
|
||||
bool TryProcessOpenUtilityWindowRequest(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowOpenUtilityWindowRequest& request);
|
||||
void FocusWindow(EditorWindow& window) const;
|
||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
||||
|
||||
EditorWindowHostRuntime& m_hostRuntime;
|
||||
EditorWindowHost& m_hostRuntime;
|
||||
EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr;
|
||||
};
|
||||
|
||||
@@ -1,71 +1,29 @@
|
||||
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h"
|
||||
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostRuntime.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string DescribeHwnd(HWND hwnd) {
|
||||
std::ostringstream stream = {};
|
||||
stream << "0x" << std::hex << std::uppercase
|
||||
<< reinterpret_cast<std::uintptr_t>(hwnd);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string DescribeHostWindows(
|
||||
const std::vector<std::unique_ptr<EditorWindow>>& windows) {
|
||||
std::ostringstream stream = {};
|
||||
stream << "count=" << windows.size() << " [";
|
||||
bool first = true;
|
||||
for (const std::unique_ptr<EditorWindow>& window : windows) {
|
||||
if (!first) {
|
||||
stream << ", ";
|
||||
}
|
||||
first = false;
|
||||
if (window == nullptr) {
|
||||
stream << "<null>";
|
||||
continue;
|
||||
}
|
||||
|
||||
stream << window->GetWindowId()
|
||||
<< "{hwnd=" << DescribeHwnd(window->GetHwnd())
|
||||
<< ",primary=" << (window->IsPrimary() ? '1' : '0')
|
||||
<< ",state=" << GetEditorWindowLifecycleStateName(window->GetLifecycleState())
|
||||
<< '}';
|
||||
}
|
||||
stream << ']';
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EditorWindowLifecycleCoordinator::EditorWindowLifecycleCoordinator(
|
||||
EditorWindowHostRuntime& hostRuntime,
|
||||
EditorWindowHost& hostRuntime,
|
||||
EditorWindowWorkspaceCoordinator& workspaceCoordinator)
|
||||
: m_hostRuntime(hostRuntime),
|
||||
m_workspaceCoordinator(workspaceCoordinator) {}
|
||||
|
||||
EditorWindowLifecycleCoordinator::~EditorWindowLifecycleCoordinator() = default;
|
||||
|
||||
void EditorWindowLifecycleCoordinator::PostCloseRequest(EditorWindow& window) {
|
||||
void EditorWindowLifecycleCoordinator::PostCloseRequest(EditorHostWindow& window) {
|
||||
if (window.IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HWND hwnd = window.GetHwnd();
|
||||
if (!window.IsClosing()) {
|
||||
window.MarkClosing();
|
||||
}
|
||||
|
||||
if (hwnd == nullptr || !IsWindow(hwnd)) {
|
||||
if (!window.HasLiveHostWindow()) {
|
||||
ShutdownRuntimeIfNeeded(window);
|
||||
window.MarkDestroyed();
|
||||
return;
|
||||
@@ -74,27 +32,24 @@ void EditorWindowLifecycleCoordinator::PostCloseRequest(EditorWindow& window) {
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"PostCloseRequest windowId='" + std::string(window.GetWindowId()) +
|
||||
"' hwnd=" + DescribeHwnd(hwnd) +
|
||||
" primary=" + (window.IsPrimary() ? "1" : "0") +
|
||||
" host=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
PostMessageW(hwnd, WM_CLOSE, 0, 0);
|
||||
" host=" + m_hostRuntime.DescribeWindows());
|
||||
window.PostCloseToHost();
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorWindow& window) {
|
||||
void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorHostWindow& window) {
|
||||
if (window.IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HWND hwnd = window.GetHwnd();
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ExecuteCloseRequest begin windowId='" + std::string(window.GetWindowId()) +
|
||||
"' hwnd=" + DescribeHwnd(hwnd) +
|
||||
" primary=" + (window.IsPrimary() ? "1" : "0") +
|
||||
" lifecycleBefore=" +
|
||||
std::string(GetEditorWindowLifecycleStateName(window.GetLifecycleState())) +
|
||||
" workspace=" + m_workspaceCoordinator.DescribeWindowSet() +
|
||||
" host=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
" host=" + m_hostRuntime.DescribeWindows());
|
||||
|
||||
if (!window.IsClosing()) {
|
||||
window.MarkClosing();
|
||||
@@ -102,8 +57,8 @@ void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorWindow& window)
|
||||
|
||||
ShutdownRuntimeIfNeeded(window);
|
||||
|
||||
if (hwnd != nullptr && IsWindow(hwnd)) {
|
||||
DestroyWindow(hwnd);
|
||||
if (window.HasLiveHostWindow()) {
|
||||
window.DestroyHostWindow();
|
||||
} else {
|
||||
window.MarkDestroyed();
|
||||
}
|
||||
@@ -111,10 +66,10 @@ void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorWindow& window)
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ExecuteCloseRequest end windowId='" + std::string(window.GetWindowId()) +
|
||||
"' host=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
"' host=" + m_hostRuntime.DescribeWindows());
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorWindow& window) {
|
||||
void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorHostWindow& window) {
|
||||
if (window.IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
@@ -124,11 +79,10 @@ void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorWindow&
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"HandleNativeWindowDestroyed begin windowId='" + std::string(window.GetWindowId()) +
|
||||
"' hwnd=" + DescribeHwnd(window.GetHwnd()) +
|
||||
" destroyedPrimary=" + (destroyedPrimary ? "1" : "0") +
|
||||
" localPrimary=" + (window.IsPrimary() ? "1" : "0") +
|
||||
" workspaceBefore=" + m_workspaceCoordinator.DescribeWindowSet() +
|
||||
" hostBefore=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
" hostBefore=" + m_hostRuntime.DescribeWindows());
|
||||
|
||||
if (m_workspaceCoordinator.OwnsActiveGlobalTabDrag(window.GetWindowId())) {
|
||||
m_workspaceCoordinator.EndGlobalTabDragSession();
|
||||
@@ -141,19 +95,20 @@ void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorWindow&
|
||||
}
|
||||
|
||||
if (destroyedPrimary) {
|
||||
std::vector<EditorWindow*> closeTargets = {};
|
||||
closeTargets.reserve(m_hostRuntime.GetWindows().size());
|
||||
for (const std::unique_ptr<EditorWindow>& otherWindow : m_hostRuntime.GetWindows()) {
|
||||
std::vector<EditorHostWindow*> closeTargets = {};
|
||||
const std::vector<EditorHostWindow*> windows = m_hostRuntime.GetWindows();
|
||||
closeTargets.reserve(windows.size());
|
||||
for (EditorHostWindow* otherWindow : windows) {
|
||||
if (otherWindow == nullptr ||
|
||||
otherWindow.get() == &window ||
|
||||
otherWindow->GetHwnd() == nullptr ||
|
||||
otherWindow == &window ||
|
||||
!otherWindow->HasLiveHostWindow() ||
|
||||
otherWindow->IsClosing()) {
|
||||
continue;
|
||||
}
|
||||
closeTargets.push_back(otherWindow.get());
|
||||
closeTargets.push_back(otherWindow);
|
||||
}
|
||||
|
||||
for (EditorWindow* closeTarget : closeTargets) {
|
||||
for (EditorHostWindow* closeTarget : closeTargets) {
|
||||
if (closeTarget != nullptr) {
|
||||
PostCloseRequest(*closeTarget);
|
||||
}
|
||||
@@ -164,24 +119,22 @@ void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorWindow&
|
||||
"window-close",
|
||||
"HandleNativeWindowDestroyed end windowId='" + std::string(window.GetWindowId()) +
|
||||
"' workspaceAfter=" + m_workspaceCoordinator.DescribeWindowSet() +
|
||||
" hostAfter=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
" hostAfter=" + m_hostRuntime.DescribeWindows());
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::AbortUnregisteredWindow(EditorWindow& window) {
|
||||
const HWND hwnd = window.GetHwnd();
|
||||
void EditorWindowLifecycleCoordinator::AbortUnregisteredWindow(EditorHostWindow& window) {
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"AbortUnregisteredWindow begin windowId='" + std::string(window.GetWindowId()) +
|
||||
"' hwnd=" + DescribeHwnd(hwnd) +
|
||||
" hostBefore=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
" hostBefore=" + m_hostRuntime.DescribeWindows());
|
||||
|
||||
if (!window.IsClosing()) {
|
||||
window.MarkClosing();
|
||||
}
|
||||
|
||||
ShutdownRuntimeIfNeeded(window);
|
||||
if (hwnd != nullptr && IsWindow(hwnd)) {
|
||||
DestroyWindow(hwnd);
|
||||
if (window.HasLiveHostWindow()) {
|
||||
window.DestroyHostWindow();
|
||||
} else {
|
||||
window.MarkDestroyed();
|
||||
}
|
||||
@@ -190,19 +143,20 @@ void EditorWindowLifecycleCoordinator::AbortUnregisteredWindow(EditorWindow& win
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"AbortUnregisteredWindow end windowId='" + std::string(window.GetWindowId()) +
|
||||
"' hostAfter=" + DescribeHostWindows(m_hostRuntime.GetWindows()));
|
||||
"' hostAfter=" + m_hostRuntime.DescribeWindows());
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::ShutdownAllWindows() {
|
||||
std::vector<EditorWindow*> closeTargets = {};
|
||||
closeTargets.reserve(m_hostRuntime.GetWindows().size());
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_hostRuntime.GetWindows()) {
|
||||
std::vector<EditorHostWindow*> closeTargets = {};
|
||||
const std::vector<EditorHostWindow*> windows = m_hostRuntime.GetWindows();
|
||||
closeTargets.reserve(windows.size());
|
||||
for (EditorHostWindow* window : windows) {
|
||||
if (window != nullptr) {
|
||||
closeTargets.push_back(window.get());
|
||||
closeTargets.push_back(window);
|
||||
}
|
||||
}
|
||||
|
||||
for (EditorWindow* closeTarget : closeTargets) {
|
||||
for (EditorHostWindow* closeTarget : closeTargets) {
|
||||
if (closeTarget != nullptr) {
|
||||
ExecuteCloseRequest(*closeTarget);
|
||||
}
|
||||
@@ -212,30 +166,10 @@ void EditorWindowLifecycleCoordinator::ShutdownAllWindows() {
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::ReapDestroyedWindows() {
|
||||
auto& windows = m_hostRuntime.GetWindows();
|
||||
for (auto it = windows.begin(); it != windows.end();) {
|
||||
EditorWindow* const window = it->get();
|
||||
if (window == nullptr || !window->IsDestroyed()) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_hostRuntime.m_pendingCreateWindow == window) {
|
||||
m_hostRuntime.m_pendingCreateWindow = nullptr;
|
||||
}
|
||||
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ReapDestroyedWindows erase windowId='" + std::string(window->GetWindowId()) +
|
||||
"' hostBefore=" + DescribeHostWindows(windows));
|
||||
it = windows.erase(it);
|
||||
LogRuntimeTrace(
|
||||
"window-close",
|
||||
"ReapDestroyedWindows erase end hostAfter=" + DescribeHostWindows(windows));
|
||||
}
|
||||
m_hostRuntime.ReapDestroyedWindows();
|
||||
}
|
||||
|
||||
void EditorWindowLifecycleCoordinator::ShutdownRuntimeIfNeeded(EditorWindow& window) {
|
||||
void EditorWindowLifecycleCoordinator::ShutdownRuntimeIfNeeded(EditorHostWindow& window) {
|
||||
if (window.IsRenderReady()) {
|
||||
window.Shutdown();
|
||||
}
|
||||
@@ -1,32 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "Windowing/Host/EditorWindowHostInterfaces.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorWindow;
|
||||
class EditorWindowHostRuntime;
|
||||
class EditorWindowWorkspaceCoordinator;
|
||||
|
||||
class EditorWindowLifecycleCoordinator final {
|
||||
public:
|
||||
EditorWindowLifecycleCoordinator(
|
||||
EditorWindowHostRuntime& hostRuntime,
|
||||
EditorWindowHost& hostRuntime,
|
||||
EditorWindowWorkspaceCoordinator& workspaceCoordinator);
|
||||
~EditorWindowLifecycleCoordinator();
|
||||
|
||||
void PostCloseRequest(EditorWindow& window);
|
||||
void ExecuteCloseRequest(EditorWindow& window);
|
||||
void HandleNativeWindowDestroyed(EditorWindow& window);
|
||||
void AbortUnregisteredWindow(EditorWindow& window);
|
||||
void PostCloseRequest(EditorHostWindow& window);
|
||||
void ExecuteCloseRequest(EditorHostWindow& window);
|
||||
void HandleNativeWindowDestroyed(EditorHostWindow& window);
|
||||
void AbortUnregisteredWindow(EditorHostWindow& window);
|
||||
void ShutdownAllWindows();
|
||||
void ReapDestroyedWindows();
|
||||
|
||||
private:
|
||||
void ShutdownRuntimeIfNeeded(EditorWindow& window);
|
||||
void ShutdownRuntimeIfNeeded(EditorHostWindow& window);
|
||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
||||
|
||||
EditorWindowHostRuntime& m_hostRuntime;
|
||||
EditorWindowHost& m_hostRuntime;
|
||||
EditorWindowWorkspaceCoordinator& m_workspaceCoordinator;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h"
|
||||
|
||||
#include "Composition/EditorContext.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Platform/Win32/Windowing/EditorFloatingWindowPlacement.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostRuntime.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/Content/EditorWindowContentController.h"
|
||||
#include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/System/EditorWindowPresentationPolicy.h"
|
||||
#include "Windowing/System/EditorWindowSystem.h"
|
||||
#include "Windowing/Content/EditorWindowContentController.h"
|
||||
|
||||
#include <XCEditor/Docking/UIEditorDockHostTransfer.h>
|
||||
#include <XCEditor/Workspace/UIEditorWindowWorkspaceController.h>
|
||||
@@ -17,6 +13,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
@@ -32,37 +29,30 @@ struct ExistingWindowSnapshot {
|
||||
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
|
||||
constexpr LONG kFallbackDragHotspotX = 40;
|
||||
constexpr LONG kFallbackDragHotspotY = 12;
|
||||
constexpr std::int32_t kFallbackDragHotspotX = 40;
|
||||
constexpr std::int32_t kFallbackDragHotspotY = 12;
|
||||
|
||||
POINT BuildFallbackGlobalTabDragHotspot() {
|
||||
POINT hotspot = {};
|
||||
EditorWindowScreenPoint BuildFallbackGlobalTabDragHotspot() {
|
||||
EditorWindowScreenPoint hotspot = {};
|
||||
hotspot.x = kFallbackDragHotspotX;
|
||||
hotspot.y = kFallbackDragHotspotY;
|
||||
return hotspot;
|
||||
}
|
||||
|
||||
POINT ToNativePoint(const EditorWindowScreenPoint& point) {
|
||||
return POINT{
|
||||
static_cast<LONG>(point.x),
|
||||
static_cast<LONG>(point.y),
|
||||
};
|
||||
}
|
||||
|
||||
std::wstring_view ResolvePrimaryWindowTitle(const EditorWindowHostRuntime& hostRuntime) {
|
||||
return hostRuntime.GetHostConfig().primaryWindowTitle != nullptr
|
||||
? std::wstring_view(hostRuntime.GetHostConfig().primaryWindowTitle)
|
||||
: std::wstring_view{};
|
||||
}
|
||||
|
||||
bool CanStartGlobalTabDragFromWindow(
|
||||
const EditorWindow& sourceWindow,
|
||||
const EditorHostWindow& sourceWindow,
|
||||
std::string_view sourceNodeId,
|
||||
std::string_view panelId) {
|
||||
const UIEditorWorkspaceController* workspaceController =
|
||||
sourceWindow.TryGetWorkspaceController();
|
||||
if (workspaceController == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceModel workspace =
|
||||
sourceWindow.GetWorkspaceController().GetWorkspace();
|
||||
workspaceController->GetWorkspace();
|
||||
UIEditorWorkspaceSession session =
|
||||
sourceWindow.GetWorkspaceController().GetSession();
|
||||
workspaceController->GetSession();
|
||||
UIEditorWorkspaceExtractedPanel extractedPanel = {};
|
||||
return TryExtractUIEditorWorkspaceVisiblePanel(
|
||||
workspace,
|
||||
@@ -72,9 +62,9 @@ bool CanStartGlobalTabDragFromWindow(
|
||||
extractedPanel);
|
||||
}
|
||||
|
||||
bool IsLiveInteractiveWindow(const EditorWindow* window) {
|
||||
bool IsLiveInteractiveWindow(const EditorHostWindow* window) {
|
||||
return window != nullptr &&
|
||||
window->GetHwnd() != nullptr &&
|
||||
window->HasLiveHostWindow() &&
|
||||
window->GetLifecycleState() == EditorWindowLifecycleState::Running;
|
||||
}
|
||||
|
||||
@@ -99,7 +89,7 @@ std::string DescribeWindowSetState(const UIEditorWindowWorkspaceSet& windowSet)
|
||||
} // namespace
|
||||
|
||||
EditorWindowWorkspaceCoordinator::EditorWindowWorkspaceCoordinator(
|
||||
EditorWindowHostRuntime& hostRuntime,
|
||||
EditorWindowHost& hostRuntime,
|
||||
EditorWindowSystem& windowSystem)
|
||||
: m_hostRuntime(hostRuntime),
|
||||
m_windowSystem(windowSystem) {}
|
||||
@@ -111,12 +101,12 @@ void EditorWindowWorkspaceCoordinator::BindLifecycleCoordinator(
|
||||
m_lifecycleCoordinator = &lifecycleCoordinator;
|
||||
}
|
||||
|
||||
void EditorWindowWorkspaceCoordinator::RegisterExistingWindow(EditorWindow& window) {
|
||||
void EditorWindowWorkspaceCoordinator::RegisterExistingWindow(EditorHostWindow& window) {
|
||||
RefreshWorkspaceProjectionFromAuthoritativeState(window);
|
||||
RefreshWindowTitle(window);
|
||||
}
|
||||
|
||||
void EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(EditorWindow& window) {
|
||||
void EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(EditorHostWindow& window) {
|
||||
if (!window.IsWorkspaceWindow()) {
|
||||
return;
|
||||
}
|
||||
@@ -128,7 +118,7 @@ void EditorWindowWorkspaceCoordinator::HandleNativeWindowDestroyed(std::string_v
|
||||
EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForDestroyedWindow(
|
||||
windowId,
|
||||
CaptureHostSnapshots(),
|
||||
ResolvePrimaryWindowTitle(m_hostRuntime),
|
||||
m_hostRuntime.GetPrimaryWindowTitle(),
|
||||
error);
|
||||
if (!plan.valid) {
|
||||
LogRuntimeTrace(
|
||||
@@ -160,7 +150,7 @@ EditorWindowWorkspaceCoordinator::BuildWorkspaceMutationController() const {
|
||||
}
|
||||
|
||||
UIEditorWindowWorkspaceState EditorWindowWorkspaceCoordinator::BuildWindowStateForWindow(
|
||||
const EditorWindow& window) const {
|
||||
const EditorHostWindow& window) const {
|
||||
if (const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection();
|
||||
projection != nullptr) {
|
||||
return projection->windowState;
|
||||
@@ -181,7 +171,7 @@ EditorWorkspaceWindowProjection EditorWindowWorkspaceCoordinator::BuildWorkspace
|
||||
bool primary,
|
||||
std::wstring title) const {
|
||||
EditorWorkspaceWindowProjection projection = BuildEditorWorkspaceWindowProjection(
|
||||
ResolvePrimaryWindowTitle(m_hostRuntime),
|
||||
m_hostRuntime.GetPrimaryWindowTitle(),
|
||||
m_windowSystem.GetPanelRegistry(),
|
||||
windowState,
|
||||
primary);
|
||||
@@ -192,7 +182,7 @@ EditorWorkspaceWindowProjection EditorWindowWorkspaceCoordinator::BuildWorkspace
|
||||
}
|
||||
|
||||
EditorWorkspaceWindowProjection EditorWindowWorkspaceCoordinator::BuildWorkspaceProjectionForWindow(
|
||||
const EditorWindow& window) const {
|
||||
const EditorHostWindow& window) const {
|
||||
if (const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection();
|
||||
projection != nullptr) {
|
||||
return *projection;
|
||||
@@ -205,7 +195,7 @@ EditorWorkspaceWindowProjection EditorWindowWorkspaceCoordinator::BuildWorkspace
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::RefreshWorkspaceProjectionFromAuthoritativeState(
|
||||
EditorWindow& window) const {
|
||||
EditorHostWindow& window) const {
|
||||
if (!window.IsWorkspaceWindow()) {
|
||||
return false;
|
||||
}
|
||||
@@ -233,7 +223,7 @@ bool EditorWindowWorkspaceCoordinator::CommitLiveWindowMutation(
|
||||
EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForWorkspaceMutationRequest(
|
||||
request,
|
||||
CaptureHostSnapshots(),
|
||||
ResolvePrimaryWindowTitle(m_hostRuntime),
|
||||
m_hostRuntime.GetPrimaryWindowTitle(),
|
||||
error);
|
||||
if (!plan.valid) {
|
||||
LogRuntimeTrace(
|
||||
@@ -246,7 +236,7 @@ bool EditorWindowWorkspaceCoordinator::CommitLiveWindowMutation(
|
||||
return ApplySynchronizationPlan(plan);
|
||||
}
|
||||
|
||||
void EditorWindowWorkspaceCoordinator::RefreshWindowTitle(EditorWindow& window) const {
|
||||
void EditorWindowWorkspaceCoordinator::RefreshWindowTitle(EditorHostWindow& window) const {
|
||||
const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection();
|
||||
if (projection == nullptr || projection->windowTitle.empty()) {
|
||||
return;
|
||||
@@ -258,35 +248,33 @@ void EditorWindowWorkspaceCoordinator::RefreshWindowTitle(EditorWindow& window)
|
||||
}
|
||||
|
||||
window.SetTitle(title);
|
||||
if (window.GetHwnd() != nullptr) {
|
||||
SetWindowTextW(window.GetHwnd(), window.GetTitle().c_str());
|
||||
}
|
||||
window.ApplyHostWindowTitle();
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet(
|
||||
const UIEditorWindowWorkspaceSet& windowSet,
|
||||
std::string_view preferredNewWindowId,
|
||||
const POINT& preferredScreenPoint,
|
||||
LONG preferredWidth,
|
||||
LONG preferredHeight) {
|
||||
const EditorWindowScreenPoint& preferredScreenPoint,
|
||||
int preferredWidth,
|
||||
int preferredHeight) {
|
||||
EditorWindowSynchronizationPlacement preferredPlacement = {};
|
||||
if (!preferredNewWindowId.empty()) {
|
||||
const RECT detachedRect = BuildEditorFloatingWindowRect(
|
||||
const EditorWindowScreenRect detachedRect = m_hostRuntime.ResolveFloatingPlacement(
|
||||
preferredScreenPoint,
|
||||
preferredWidth,
|
||||
preferredHeight);
|
||||
preferredPlacement.enabled = true;
|
||||
preferredPlacement.initialX = detachedRect.left;
|
||||
preferredPlacement.initialY = detachedRect.top;
|
||||
preferredPlacement.initialWidth = detachedRect.right - detachedRect.left;
|
||||
preferredPlacement.initialHeight = detachedRect.bottom - detachedRect.top;
|
||||
preferredPlacement.initialWidth = detachedRect.Width();
|
||||
preferredPlacement.initialHeight = detachedRect.Height();
|
||||
}
|
||||
|
||||
std::string error = {};
|
||||
EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForWindowSet(
|
||||
windowSet,
|
||||
CaptureHostSnapshots(),
|
||||
ResolvePrimaryWindowTitle(m_hostRuntime),
|
||||
m_hostRuntime.GetPrimaryWindowTitle(),
|
||||
preferredNewWindowId,
|
||||
preferredPlacement,
|
||||
error);
|
||||
@@ -300,9 +288,11 @@ bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet(
|
||||
|
||||
std::vector<EditorWindowHostSnapshot> EditorWindowWorkspaceCoordinator::CaptureHostSnapshots() const {
|
||||
std::vector<EditorWindowHostSnapshot> snapshots = {};
|
||||
snapshots.reserve(m_hostRuntime.GetWindows().size());
|
||||
const std::vector<const EditorHostWindow*> windows =
|
||||
std::as_const(m_hostRuntime).GetWindows();
|
||||
snapshots.reserve(windows.size());
|
||||
|
||||
for (const std::unique_ptr<EditorWindow>& window : m_hostRuntime.GetWindows()) {
|
||||
for (const EditorHostWindow* window : windows) {
|
||||
if (window == nullptr) {
|
||||
continue;
|
||||
}
|
||||
@@ -314,7 +304,7 @@ std::vector<EditorWindowHostSnapshot> EditorWindowWorkspaceCoordinator::CaptureH
|
||||
snapshot.primary = window->IsPrimary();
|
||||
snapshot.running = window->GetLifecycleState() == EditorWindowLifecycleState::Running;
|
||||
snapshot.destroyed = window->IsDestroyed();
|
||||
snapshot.hasNativeWindow = window->GetHwnd() != nullptr;
|
||||
snapshot.hasNativeWindow = window->HasLiveHostWindow();
|
||||
snapshot.title = window->GetTitle();
|
||||
if (window->TryGetWorkspaceProjection() != nullptr ||
|
||||
window->TryGetWorkspaceController() != nullptr) {
|
||||
@@ -330,7 +320,7 @@ std::vector<EditorWindowHostSnapshot> EditorWindowWorkspaceCoordinator::CaptureH
|
||||
bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
const EditorWindowSynchronizationPlan& plan) {
|
||||
const auto restoreWindowSnapshot = [this](const ExistingWindowSnapshot& snapshot) {
|
||||
EditorWindow* const window = m_hostRuntime.FindWindow(snapshot.windowId);
|
||||
EditorHostWindow* const window = m_hostRuntime.FindWindowById(snapshot.windowId);
|
||||
if (window == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -342,7 +332,7 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
};
|
||||
|
||||
const auto destroyAndEraseWindowById = [this](std::string_view windowId) {
|
||||
EditorWindow* const window = m_hostRuntime.FindWindow(windowId);
|
||||
EditorHostWindow* const window = m_hostRuntime.FindWindowById(windowId);
|
||||
if (window == nullptr || m_lifecycleCoordinator == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -357,11 +347,11 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
for (const EditorWindowSynchronizationAction& action : plan.actions) {
|
||||
switch (action.kind) {
|
||||
case EditorWindowSynchronizationActionKind::UpdateWorkspaceWindow: {
|
||||
EditorWindow* existingWindow =
|
||||
m_hostRuntime.FindWindow(action.update.windowState.windowId);
|
||||
EditorHostWindow* existingWindow =
|
||||
m_hostRuntime.FindWindowById(action.update.windowState.windowId);
|
||||
if (existingWindow == nullptr && m_lifecycleCoordinator != nullptr) {
|
||||
m_lifecycleCoordinator->ReapDestroyedWindows();
|
||||
existingWindow = m_hostRuntime.FindWindow(action.update.windowState.windowId);
|
||||
existingWindow = m_hostRuntime.FindWindowById(action.update.windowState.windowId);
|
||||
}
|
||||
if (existingWindow == nullptr) {
|
||||
return false;
|
||||
@@ -383,7 +373,7 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
break;
|
||||
}
|
||||
case EditorWindowSynchronizationActionKind::CreateWorkspaceWindow: {
|
||||
EditorWindowHostRuntime::CreateParams createParams = {};
|
||||
EditorWindowCreateParams createParams = {};
|
||||
createParams.windowId = action.create.windowState.windowId;
|
||||
createParams.category = EditorWindowCategory::Workspace;
|
||||
createParams.primary = action.create.primary;
|
||||
@@ -395,7 +385,7 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
createParams.initialHeight = action.create.placement.initialHeight;
|
||||
}
|
||||
|
||||
EditorWindow* const createdWindow = m_hostRuntime.CreateWorkspaceWindow(
|
||||
EditorHostWindow* const createdWindow = m_hostRuntime.CreateWorkspaceWindow(
|
||||
BuildWorkspaceControllerForWindowState(
|
||||
m_windowSystem.GetPanelRegistry(),
|
||||
action.create.windowState),
|
||||
@@ -419,7 +409,7 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
}
|
||||
case EditorWindowSynchronizationActionKind::CloseWorkspaceWindow:
|
||||
if (m_lifecycleCoordinator != nullptr) {
|
||||
if (EditorWindow* window = m_hostRuntime.FindWindow(action.close.windowId);
|
||||
if (EditorHostWindow* window = m_hostRuntime.FindWindowById(action.close.windowId);
|
||||
window != nullptr) {
|
||||
m_lifecycleCoordinator->PostCloseRequest(*window);
|
||||
}
|
||||
@@ -442,9 +432,9 @@ bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(
|
||||
bool EditorWindowWorkspaceCoordinator::CommitWindowWorkspaceMutation(
|
||||
const UIEditorWindowWorkspaceController& windowWorkspaceController,
|
||||
std::string_view preferredNewWindowId,
|
||||
const POINT& preferredScreenPoint,
|
||||
LONG preferredWidth,
|
||||
LONG preferredHeight) {
|
||||
const EditorWindowScreenPoint& preferredScreenPoint,
|
||||
int preferredWidth,
|
||||
int preferredHeight) {
|
||||
const UIEditorWindowWorkspaceSet nextWindowSet = windowWorkspaceController.GetWindowSet();
|
||||
std::string error = {};
|
||||
if (!m_windowSystem.ValidateWindowSet(nextWindowSet, error)) {
|
||||
@@ -478,8 +468,8 @@ void EditorWindowWorkspaceCoordinator::BeginGlobalTabDragSession(
|
||||
std::string_view panelWindowId,
|
||||
std::string_view sourceNodeId,
|
||||
std::string_view panelId,
|
||||
const POINT& screenPoint,
|
||||
const POINT& dragHotspot) {
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
const EditorWindowScreenPoint& dragHotspot) {
|
||||
m_globalTabDragSession.active = true;
|
||||
m_globalTabDragSession.panelWindowId = std::string(panelWindowId);
|
||||
m_globalTabDragSession.sourceNodeId = std::string(sourceNodeId);
|
||||
@@ -489,11 +479,11 @@ void EditorWindowWorkspaceCoordinator::BeginGlobalTabDragSession(
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::TryResolveGlobalTabDragHotspot(
|
||||
const EditorWindow& sourceWindow,
|
||||
const EditorHostWindow& sourceWindow,
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const POINT& screenPoint,
|
||||
POINT& outDragHotspot) const {
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
EditorWindowScreenPoint& outDragHotspot) const {
|
||||
return sourceWindow.TryResolveDockTabDragHotspot(
|
||||
nodeId,
|
||||
panelId,
|
||||
@@ -506,32 +496,28 @@ void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragOwnerWindowPosition()
|
||||
return;
|
||||
}
|
||||
|
||||
EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId);
|
||||
EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId);
|
||||
if (!IsLiveInteractiveWindow(ownerWindow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RECT windowRect = {};
|
||||
if (!GetWindowRect(ownerWindow->GetHwnd(), &windowRect)) {
|
||||
EditorWindowScreenRect windowRect = {};
|
||||
if (!ownerWindow->TryGetHostScreenRect(windowRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LONG targetLeft =
|
||||
const std::int32_t targetLeft =
|
||||
m_globalTabDragSession.screenPoint.x - m_globalTabDragSession.dragHotspot.x;
|
||||
const LONG targetTop =
|
||||
const std::int32_t targetTop =
|
||||
m_globalTabDragSession.screenPoint.y - m_globalTabDragSession.dragHotspot.y;
|
||||
if (windowRect.left == targetLeft && windowRect.top == targetTop) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetWindowPos(
|
||||
ownerWindow->GetHwnd(),
|
||||
nullptr,
|
||||
targetLeft,
|
||||
targetTop,
|
||||
0,
|
||||
0,
|
||||
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
ownerWindow->SetHostScreenPosition(EditorWindowScreenPoint{
|
||||
.x = targetLeft,
|
||||
.y = targetTop,
|
||||
});
|
||||
}
|
||||
|
||||
void EditorWindowWorkspaceCoordinator::ClearGlobalTabDragDropPreview() {
|
||||
@@ -539,7 +525,7 @@ void EditorWindowWorkspaceCoordinator::ClearGlobalTabDragDropPreview() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (EditorWindow* previewWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.previewWindowId);
|
||||
if (EditorHostWindow* previewWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.previewWindowId);
|
||||
IsLiveInteractiveWindow(previewWindow)) {
|
||||
if (EditorWindowDockHostBinding* dockHostBinding =
|
||||
previewWindow->TryGetDockHostBinding();
|
||||
@@ -556,7 +542,7 @@ void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragDropPreview() {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorWindow* targetWindow = FindTopmostWindowAtScreenPoint(
|
||||
EditorHostWindow* targetWindow = FindTopmostWindowAtScreenPoint(
|
||||
m_globalTabDragSession.screenPoint,
|
||||
m_globalTabDragSession.panelWindowId);
|
||||
if (!IsLiveInteractiveWindow(targetWindow)) {
|
||||
@@ -600,7 +586,7 @@ void EditorWindowWorkspaceCoordinator::EndGlobalTabDragSession() {
|
||||
|
||||
ClearGlobalTabDragDropPreview();
|
||||
|
||||
if (EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId);
|
||||
if (EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId);
|
||||
ownerWindow != nullptr) {
|
||||
ownerWindow->ReleasePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag);
|
||||
if (IsLiveInteractiveWindow(ownerWindow)) {
|
||||
@@ -611,22 +597,22 @@ void EditorWindowWorkspaceCoordinator::EndGlobalTabDragSession() {
|
||||
m_globalTabDragSession = {};
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerMove(HWND hwnd) {
|
||||
bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerMove(EditorHostWindow& window) {
|
||||
if (!m_globalTabDragSession.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId);
|
||||
const EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId);
|
||||
if (!IsLiveInteractiveWindow(ownerWindow)) {
|
||||
EndGlobalTabDragSession();
|
||||
return false;
|
||||
}
|
||||
if (ownerWindow->GetHwnd() != hwnd) {
|
||||
if (ownerWindow->GetWindowId() != window.GetWindowId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
POINT screenPoint = {};
|
||||
if (GetCursorPos(&screenPoint)) {
|
||||
EditorWindowScreenPoint screenPoint = {};
|
||||
if (m_hostRuntime.TryGetCursorScreenPoint(screenPoint)) {
|
||||
m_globalTabDragSession.screenPoint = screenPoint;
|
||||
UpdateGlobalTabDragOwnerWindowPosition();
|
||||
UpdateGlobalTabDragDropPreview();
|
||||
@@ -634,29 +620,29 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerMove(HWND hwnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND hwnd) {
|
||||
bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window) {
|
||||
if (!m_globalTabDragSession.active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EditorWindow* ownerWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.panelWindowId);
|
||||
const EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId);
|
||||
if (!IsLiveInteractiveWindow(ownerWindow)) {
|
||||
EndGlobalTabDragSession();
|
||||
return false;
|
||||
}
|
||||
if (ownerWindow->GetHwnd() != hwnd) {
|
||||
if (ownerWindow->GetWindowId() != window.GetWindowId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
POINT screenPoint = m_globalTabDragSession.screenPoint;
|
||||
GetCursorPos(&screenPoint);
|
||||
EditorWindowScreenPoint screenPoint = m_globalTabDragSession.screenPoint;
|
||||
m_hostRuntime.TryGetCursorScreenPoint(screenPoint);
|
||||
|
||||
const std::string panelWindowId = m_globalTabDragSession.panelWindowId;
|
||||
const std::string sourceNodeId = m_globalTabDragSession.sourceNodeId;
|
||||
const std::string panelId = m_globalTabDragSession.panelId;
|
||||
EndGlobalTabDragSession();
|
||||
|
||||
EditorWindow* targetWindow = FindTopmostWindowAtScreenPoint(screenPoint, panelWindowId);
|
||||
EditorHostWindow* targetWindow = FindTopmostWindowAtScreenPoint(screenPoint, panelWindowId);
|
||||
if (!IsLiveInteractiveWindow(targetWindow)) {
|
||||
return true;
|
||||
}
|
||||
@@ -697,9 +683,9 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h
|
||||
return true;
|
||||
}
|
||||
|
||||
if (EditorWindow* updatedTargetWindow = m_hostRuntime.FindWindow(targetWindow->GetWindowId());
|
||||
if (EditorHostWindow* updatedTargetWindow = m_hostRuntime.FindWindowById(targetWindow->GetWindowId());
|
||||
IsLiveInteractiveWindow(updatedTargetWindow)) {
|
||||
SetForegroundWindow(updatedTargetWindow->GetHwnd());
|
||||
updatedTargetWindow->FocusHostWindow();
|
||||
}
|
||||
LogRuntimeTrace(
|
||||
"drag",
|
||||
@@ -709,7 +695,7 @@ bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(HWND h
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowPanelTransferRequest& request) {
|
||||
if (!sourceWindow.IsWorkspaceWindow()) {
|
||||
LogRuntimeTrace(
|
||||
@@ -723,12 +709,12 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
return false;
|
||||
}
|
||||
|
||||
POINT dragHotspot = BuildFallbackGlobalTabDragHotspot();
|
||||
EditorWindowScreenPoint dragHotspot = BuildFallbackGlobalTabDragHotspot();
|
||||
TryResolveGlobalTabDragHotspot(
|
||||
sourceWindow,
|
||||
request.nodeId,
|
||||
request.panelId,
|
||||
ToNativePoint(request.screenPoint),
|
||||
request.screenPoint,
|
||||
dragHotspot);
|
||||
|
||||
const auto tryStartDetachedPanelGlobalDrag =
|
||||
@@ -738,12 +724,12 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
if (!CommitWindowWorkspaceMutation(
|
||||
windowWorkspaceController,
|
||||
result.targetWindowId,
|
||||
ToNativePoint(request.screenPoint))) {
|
||||
request.screenPoint)) {
|
||||
LogRuntimeTrace("drag", "failed to synchronize detached drag window state");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorWindow* detachedWindow = m_hostRuntime.FindWindow(result.targetWindowId);
|
||||
EditorHostWindow* detachedWindow = m_hostRuntime.FindWindowById(result.targetWindowId);
|
||||
if (!IsLiveInteractiveWindow(detachedWindow)) {
|
||||
LogRuntimeTrace("drag", "detached drag window was not created.");
|
||||
return false;
|
||||
@@ -760,12 +746,12 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
detachedWindow->GetWindowId(),
|
||||
detachedProjection->windowState.workspace.root.nodeId,
|
||||
request.panelId,
|
||||
ToNativePoint(request.screenPoint),
|
||||
request.screenPoint,
|
||||
dragHotspot);
|
||||
UpdateGlobalTabDragOwnerWindowPosition();
|
||||
detachedWindow->AcquirePointerCapture(
|
||||
EditorWindowPointerCaptureOwner::GlobalTabDrag);
|
||||
SetForegroundWindow(detachedWindow->GetHwnd());
|
||||
detachedWindow->FocusHostWindow();
|
||||
LogRuntimeTrace(
|
||||
"drag",
|
||||
"started global tab drag by detaching panel '" + request.panelId +
|
||||
@@ -809,7 +795,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
sourceWindow.GetWindowId(),
|
||||
request.nodeId,
|
||||
request.panelId,
|
||||
ToNativePoint(request.screenPoint),
|
||||
request.screenPoint,
|
||||
dragHotspot);
|
||||
UpdateGlobalTabDragOwnerWindowPosition();
|
||||
sourceWindow.AcquirePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag);
|
||||
@@ -822,7 +808,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag(
|
||||
}
|
||||
|
||||
bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowPanelTransferRequest& request) {
|
||||
if (!sourceWindow.IsWorkspaceWindow()) {
|
||||
LogRuntimeTrace(
|
||||
@@ -852,14 +838,14 @@ bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest(
|
||||
if (!CommitWindowWorkspaceMutation(
|
||||
windowWorkspaceController,
|
||||
result.targetWindowId,
|
||||
ToNativePoint(request.screenPoint))) {
|
||||
request.screenPoint)) {
|
||||
LogRuntimeTrace("detach", "failed to synchronize detached window state");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EditorWindow* detachedWindow = m_hostRuntime.FindWindow(result.targetWindowId);
|
||||
if (EditorHostWindow* detachedWindow = m_hostRuntime.FindWindowById(result.targetWindowId);
|
||||
IsLiveInteractiveWindow(detachedWindow)) {
|
||||
SetForegroundWindow(detachedWindow->GetHwnd());
|
||||
detachedWindow->FocusHostWindow();
|
||||
}
|
||||
|
||||
LogRuntimeTrace(
|
||||
@@ -870,7 +856,7 @@ bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest(
|
||||
}
|
||||
|
||||
void EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowFrameTransferRequests& transferRequests) {
|
||||
if (transferRequests.workspace.workspaceMutation.has_value()) {
|
||||
if (!CommitLiveWindowMutation(*transferRequests.workspace.workspaceMutation)) {
|
||||
@@ -898,21 +884,19 @@ void EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests(
|
||||
}
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
const POINT& screenPoint,
|
||||
EditorHostWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
std::string_view excludedWindowId) {
|
||||
if (const HWND hitWindow = WindowFromPoint(screenPoint); hitWindow != nullptr) {
|
||||
const HWND rootWindow = GetAncestor(hitWindow, GA_ROOT);
|
||||
if (EditorWindow* window = m_hostRuntime.FindWindow(rootWindow);
|
||||
IsLiveInteractiveWindow(window) &&
|
||||
window->IsWorkspaceWindow() &&
|
||||
window->GetWindowId() != excludedWindowId) {
|
||||
return window;
|
||||
}
|
||||
if (EditorHostWindow* window = m_hostRuntime.FindWindowFromScreenPoint(screenPoint);
|
||||
IsLiveInteractiveWindow(window) &&
|
||||
window->IsWorkspaceWindow() &&
|
||||
window->GetWindowId() != excludedWindowId) {
|
||||
return window;
|
||||
}
|
||||
|
||||
for (auto it = m_hostRuntime.GetWindows().rbegin(); it != m_hostRuntime.GetWindows().rend(); ++it) {
|
||||
EditorWindow* const window = it->get();
|
||||
std::vector<EditorHostWindow*> windows = m_hostRuntime.GetWindows();
|
||||
for (auto it = windows.rbegin(); it != windows.rend(); ++it) {
|
||||
EditorHostWindow* const window = *it;
|
||||
if (window == nullptr ||
|
||||
!IsLiveInteractiveWindow(window) ||
|
||||
!window->IsWorkspaceWindow() ||
|
||||
@@ -920,12 +904,9 @@ EditorWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
continue;
|
||||
}
|
||||
|
||||
RECT windowRect = {};
|
||||
if (GetWindowRect(window->GetHwnd(), &windowRect) &&
|
||||
screenPoint.x >= windowRect.left &&
|
||||
screenPoint.x < windowRect.right &&
|
||||
screenPoint.y >= windowRect.top &&
|
||||
screenPoint.y < windowRect.bottom) {
|
||||
EditorWindowScreenRect windowRect = {};
|
||||
if (window->TryGetHostScreenRect(windowRect) &&
|
||||
windowRect.Contains(screenPoint)) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
@@ -933,8 +914,8 @@ EditorWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const EditorWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
const POINT& screenPoint,
|
||||
const EditorHostWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
std::string_view excludedWindowId) const {
|
||||
return const_cast<EditorWindowWorkspaceCoordinator*>(this)->FindTopmostWindowAtScreenPoint(
|
||||
screenPoint,
|
||||
@@ -6,35 +6,32 @@
|
||||
|
||||
#include "Windowing/EditorWorkspaceWindowProjection.h"
|
||||
#include "Windowing/Frame/EditorWindowTransferRequests.h"
|
||||
#include "Windowing/Host/EditorWindowHostInterfaces.h"
|
||||
#include "Windowing/System/EditorWindowSynchronizationPlan.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
#include <XCEditor/Workspace/UIEditorWindowWorkspaceController.h>
|
||||
#include <XCEditor/Workspace/UIEditorWindowWorkspaceModel.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorWindow;
|
||||
class EditorWindowHostRuntime;
|
||||
class EditorWindowLifecycleCoordinator;
|
||||
class EditorWindowSystem;
|
||||
|
||||
class EditorWindowWorkspaceCoordinator final {
|
||||
public:
|
||||
EditorWindowWorkspaceCoordinator(
|
||||
EditorWindowHostRuntime& hostRuntime,
|
||||
EditorWindowHost& hostRuntime,
|
||||
EditorWindowSystem& windowSystem);
|
||||
~EditorWindowWorkspaceCoordinator();
|
||||
|
||||
void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator);
|
||||
void RegisterExistingWindow(EditorWindow& window);
|
||||
void RefreshWindowPresentation(EditorWindow& window);
|
||||
void RegisterExistingWindow(EditorHostWindow& window);
|
||||
void RefreshWindowPresentation(EditorHostWindow& window);
|
||||
void HandleNativeWindowDestroyed(std::string_view windowId);
|
||||
bool IsPrimaryWindowId(std::string_view windowId) const;
|
||||
std::string DescribeWindowSet() const;
|
||||
@@ -42,10 +39,10 @@ public:
|
||||
bool IsGlobalTabDragActive() const;
|
||||
bool OwnsActiveGlobalTabDrag(std::string_view windowId) const;
|
||||
void EndGlobalTabDragSession();
|
||||
bool HandleGlobalTabDragPointerMove(HWND hwnd);
|
||||
bool HandleGlobalTabDragPointerButtonUp(HWND hwnd);
|
||||
bool HandleGlobalTabDragPointerMove(EditorHostWindow& window);
|
||||
bool HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window);
|
||||
void HandleWindowFrameTransferRequests(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowFrameTransferRequests& transferRequests);
|
||||
|
||||
private:
|
||||
@@ -55,65 +52,65 @@ private:
|
||||
std::string sourceNodeId = {};
|
||||
std::string panelId = {};
|
||||
std::string previewWindowId = {};
|
||||
POINT screenPoint = {};
|
||||
POINT dragHotspot = {};
|
||||
EditorWindowScreenPoint screenPoint = {};
|
||||
EditorWindowScreenPoint dragHotspot = {};
|
||||
};
|
||||
|
||||
UIEditorWindowWorkspaceController BuildWorkspaceMutationController() const;
|
||||
bool SynchronizeWindowsFromWindowSet(
|
||||
const UIEditorWindowWorkspaceSet& windowSet,
|
||||
std::string_view preferredNewWindowId,
|
||||
const POINT& preferredScreenPoint,
|
||||
LONG preferredWidth = 0,
|
||||
LONG preferredHeight = 0);
|
||||
const EditorWindowScreenPoint& preferredScreenPoint,
|
||||
int preferredWidth = 0,
|
||||
int preferredHeight = 0);
|
||||
bool ApplySynchronizationPlan(const EditorWindowSynchronizationPlan& plan);
|
||||
std::vector<EditorWindowHostSnapshot> CaptureHostSnapshots() const;
|
||||
bool CommitWindowWorkspaceMutation(
|
||||
const UIEditorWindowWorkspaceController& windowWorkspaceController,
|
||||
std::string_view preferredNewWindowId,
|
||||
const POINT& preferredScreenPoint,
|
||||
LONG preferredWidth = 0,
|
||||
LONG preferredHeight = 0);
|
||||
UIEditorWindowWorkspaceState BuildWindowStateForWindow(const EditorWindow& window) const;
|
||||
const EditorWindowScreenPoint& preferredScreenPoint,
|
||||
int preferredWidth = 0,
|
||||
int preferredHeight = 0);
|
||||
UIEditorWindowWorkspaceState BuildWindowStateForWindow(const EditorHostWindow& window) const;
|
||||
bool CommitLiveWindowMutation(const EditorWindowWorkspaceMutationRequest& request);
|
||||
EditorWorkspaceWindowProjection BuildWorkspaceProjectionForState(
|
||||
const UIEditorWindowWorkspaceState& windowState,
|
||||
bool primary,
|
||||
std::wstring title) const;
|
||||
EditorWorkspaceWindowProjection BuildWorkspaceProjectionForWindow(
|
||||
const EditorWindow& window) const;
|
||||
bool RefreshWorkspaceProjectionFromAuthoritativeState(EditorWindow& window) const;
|
||||
void RefreshWindowTitle(EditorWindow& window) const;
|
||||
const EditorHostWindow& window) const;
|
||||
bool RefreshWorkspaceProjectionFromAuthoritativeState(EditorHostWindow& window) const;
|
||||
void RefreshWindowTitle(EditorHostWindow& window) const;
|
||||
void BeginGlobalTabDragSession(
|
||||
std::string_view panelWindowId,
|
||||
std::string_view sourceNodeId,
|
||||
std::string_view panelId,
|
||||
const POINT& screenPoint,
|
||||
const POINT& dragHotspot);
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
const EditorWindowScreenPoint& dragHotspot);
|
||||
bool TryResolveGlobalTabDragHotspot(
|
||||
const EditorWindow& sourceWindow,
|
||||
const EditorHostWindow& sourceWindow,
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const POINT& screenPoint,
|
||||
POINT& outDragHotspot) const;
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
EditorWindowScreenPoint& outDragHotspot) const;
|
||||
void ClearGlobalTabDragDropPreview();
|
||||
void UpdateGlobalTabDragDropPreview();
|
||||
void UpdateGlobalTabDragOwnerWindowPosition();
|
||||
EditorWindow* FindTopmostWindowAtScreenPoint(
|
||||
const POINT& screenPoint,
|
||||
EditorHostWindow* FindTopmostWindowAtScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
std::string_view excludedWindowId = {});
|
||||
const EditorWindow* FindTopmostWindowAtScreenPoint(
|
||||
const POINT& screenPoint,
|
||||
const EditorHostWindow* FindTopmostWindowAtScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
std::string_view excludedWindowId = {}) const;
|
||||
bool TryStartGlobalTabDrag(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowPanelTransferRequest& request);
|
||||
bool TryProcessDetachRequest(
|
||||
EditorWindow& sourceWindow,
|
||||
EditorHostWindow& sourceWindow,
|
||||
const EditorWindowPanelTransferRequest& request);
|
||||
void LogRuntimeTrace(std::string_view channel, std::string_view message) const;
|
||||
|
||||
EditorWindowHostRuntime& m_hostRuntime;
|
||||
EditorWindowHost& m_hostRuntime;
|
||||
EditorWindowSystem& m_windowSystem;
|
||||
EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr;
|
||||
GlobalTabDragSession m_globalTabDragSession = {};
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "Platform/Win32/Windowing/EditorWindowManager.h"
|
||||
#include "Windowing/EditorWindowManager.h"
|
||||
|
||||
#include "Platform/Win32/Windowing/EditorWindow.h"
|
||||
#include "Windowing/Content/EditorWindowContentFactory.h"
|
||||
#include "Windowing/Content/EditorWindowContentController.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostRuntime.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Platform/Win32/Windowing/EditorUtilityWindowCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowLifecycleCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorUtilityWindowCoordinator.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowMessageDispatcher.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h"
|
||||
#include "Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
|
||||
@@ -25,6 +25,7 @@ EditorWindowManager::EditorWindowManager(
|
||||
hostConfig,
|
||||
std::move(repoRoot),
|
||||
editorContext,
|
||||
windowSystem,
|
||||
*m_contentFactory)) {
|
||||
m_workspaceCoordinator =
|
||||
std::make_unique<EditorWindowWorkspaceCoordinator>(*m_hostRuntime, windowSystem);
|
||||
@@ -40,10 +41,10 @@ EditorWindowManager::EditorWindowManager(
|
||||
|
||||
EditorWindowManager::~EditorWindowManager() = default;
|
||||
|
||||
EditorWindow* EditorWindowManager::CreateEditorWindow(
|
||||
EditorHostWindow* EditorWindowManager::CreateEditorWindow(
|
||||
std::unique_ptr<EditorWindowContentController> contentController,
|
||||
const CreateParams& params) {
|
||||
EditorWindow* const window =
|
||||
const EditorWindowCreateParams& params) {
|
||||
EditorHostWindow* const window =
|
||||
m_hostRuntime->CreateEditorWindow(std::move(contentController), params);
|
||||
if (window != nullptr) {
|
||||
m_workspaceCoordinator->RegisterExistingWindow(*window);
|
||||
@@ -51,10 +52,10 @@ EditorWindow* EditorWindowManager::CreateEditorWindow(
|
||||
return window;
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowManager::CreateWorkspaceWindow(
|
||||
EditorHostWindow* EditorWindowManager::CreateWorkspaceWindow(
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
const CreateParams& params) {
|
||||
EditorWindow* const window =
|
||||
const EditorWindowCreateParams& params) {
|
||||
EditorHostWindow* const window =
|
||||
m_hostRuntime->CreateWorkspaceWindow(std::move(workspaceController), params);
|
||||
if (window != nullptr) {
|
||||
m_workspaceCoordinator->RegisterExistingWindow(*window);
|
||||
@@ -62,10 +63,10 @@ EditorWindow* EditorWindowManager::CreateWorkspaceWindow(
|
||||
return window;
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowManager::CreateUtilityWindow(
|
||||
EditorHostWindow* EditorWindowManager::CreateUtilityWindow(
|
||||
const EditorUtilityWindowDescriptor& descriptor,
|
||||
const CreateParams& params) {
|
||||
EditorWindow* const window = m_hostRuntime->CreateUtilityWindow(descriptor, params);
|
||||
const EditorWindowCreateParams& params) {
|
||||
EditorHostWindow* const window = m_hostRuntime->CreateUtilityWindow(descriptor, params);
|
||||
if (window != nullptr) {
|
||||
m_workspaceCoordinator->RegisterExistingWindow(*window);
|
||||
}
|
||||
@@ -81,6 +82,20 @@ void EditorWindowManager::Shutdown() {
|
||||
m_lifecycleCoordinator->ShutdownAllWindows();
|
||||
}
|
||||
|
||||
bool EditorWindowManager::RequestPrimaryWindowClose() {
|
||||
if (m_hostRuntime == nullptr || m_lifecycleCoordinator == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorHostWindow* const primaryWindow = m_hostRuntime->FindPrimaryWindow();
|
||||
if (primaryWindow == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lifecycleCoordinator->PostCloseRequest(*primaryWindow);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorWindowManager::TryDispatchWindowMessage(
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
@@ -109,27 +124,27 @@ bool EditorWindowManager::TryDispatchWindowMessage(
|
||||
outResult);
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowManager::FindWindow(HWND hwnd) {
|
||||
EditorHostWindow* EditorWindowManager::FindWindow(HWND hwnd) {
|
||||
return m_hostRuntime->FindWindow(hwnd);
|
||||
}
|
||||
|
||||
const EditorWindow* EditorWindowManager::FindWindow(HWND hwnd) const {
|
||||
const EditorHostWindow* EditorWindowManager::FindWindow(HWND hwnd) const {
|
||||
return m_hostRuntime->FindWindow(hwnd);
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowManager::FindWindow(std::string_view windowId) {
|
||||
return m_hostRuntime->FindWindow(windowId);
|
||||
EditorHostWindow* EditorWindowManager::FindWindow(std::string_view windowId) {
|
||||
return m_hostRuntime->FindWindowById(windowId);
|
||||
}
|
||||
|
||||
const EditorWindow* EditorWindowManager::FindWindow(std::string_view windowId) const {
|
||||
return m_hostRuntime->FindWindow(windowId);
|
||||
const EditorHostWindow* EditorWindowManager::FindWindow(std::string_view windowId) const {
|
||||
return m_hostRuntime->FindWindowById(windowId);
|
||||
}
|
||||
|
||||
EditorWindow* EditorWindowManager::FindPrimaryWindow() {
|
||||
EditorHostWindow* EditorWindowManager::FindPrimaryWindow() {
|
||||
return m_hostRuntime->FindPrimaryWindow();
|
||||
}
|
||||
|
||||
const EditorWindow* EditorWindowManager::FindPrimaryWindow() const {
|
||||
const EditorHostWindow* EditorWindowManager::FindPrimaryWindow() const {
|
||||
return m_hostRuntime->FindPrimaryWindow();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "Platform/Win32/Windowing/EditorWindowState.h"
|
||||
#include "Platform/Win32/Windowing/EditorWindowHostConfig.h"
|
||||
#include "Windowing/Host/EditorWindowHostInterfaces.h"
|
||||
#include "Windowing/Host/EditorWindowHostTypes.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -28,7 +30,6 @@ namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorContext;
|
||||
class EditorWindowSystem;
|
||||
class EditorWindow;
|
||||
class EditorWindowContentController;
|
||||
class EditorWindowContentFactory;
|
||||
class EditorWindowHostRuntime;
|
||||
@@ -39,31 +40,8 @@ struct EditorUtilityWindowDescriptor;
|
||||
struct EditorWindowPanelTransferRequest;
|
||||
struct EditorWindowFrameTransferRequests;
|
||||
|
||||
struct EditorWindowHostConfig {
|
||||
HINSTANCE hInstance = nullptr;
|
||||
const wchar_t* windowClassName = L"";
|
||||
DWORD windowStyle = 0;
|
||||
const wchar_t* primaryWindowTitle = L"";
|
||||
void* windowUserData = nullptr;
|
||||
};
|
||||
|
||||
class EditorWindowManager final {
|
||||
public:
|
||||
struct CreateParams {
|
||||
std::string windowId = {};
|
||||
std::wstring title = {};
|
||||
int initialX = CW_USEDEFAULT;
|
||||
int initialY = CW_USEDEFAULT;
|
||||
int initialWidth = 1540;
|
||||
int initialHeight = 940;
|
||||
int showCommand = SW_SHOW;
|
||||
EditorWindowCategory category = EditorWindowCategory::Workspace;
|
||||
EditorWindowChromePolicy chromePolicy = {};
|
||||
EditorWindowNativeStylePolicy nativeStylePolicy = {};
|
||||
bool primary = false;
|
||||
bool autoCaptureOnStartup = false;
|
||||
};
|
||||
|
||||
EditorWindowManager(
|
||||
EditorWindowHostConfig hostConfig,
|
||||
std::filesystem::path repoRoot,
|
||||
@@ -76,17 +54,18 @@ public:
|
||||
EditorWindowManager(EditorWindowManager&&) = delete;
|
||||
EditorWindowManager& operator=(EditorWindowManager&&) = delete;
|
||||
|
||||
EditorWindow* CreateEditorWindow(
|
||||
EditorHostWindow* CreateEditorWindow(
|
||||
std::unique_ptr<EditorWindowContentController> contentController,
|
||||
const CreateParams& params);
|
||||
EditorWindow* CreateWorkspaceWindow(
|
||||
const EditorWindowCreateParams& params);
|
||||
EditorHostWindow* CreateWorkspaceWindow(
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
const CreateParams& params);
|
||||
EditorWindow* CreateUtilityWindow(
|
||||
const EditorWindowCreateParams& params);
|
||||
EditorHostWindow* CreateUtilityWindow(
|
||||
const EditorUtilityWindowDescriptor& descriptor,
|
||||
const CreateParams& params);
|
||||
const EditorWindowCreateParams& params);
|
||||
void HandlePendingNativeWindowCreated(HWND hwnd);
|
||||
void Shutdown();
|
||||
bool RequestPrimaryWindowClose();
|
||||
bool TryDispatchWindowMessage(
|
||||
HWND hwnd,
|
||||
UINT message,
|
||||
@@ -94,12 +73,12 @@ public:
|
||||
LPARAM lParam,
|
||||
LRESULT& outResult);
|
||||
|
||||
EditorWindow* FindWindow(HWND hwnd);
|
||||
const EditorWindow* FindWindow(HWND hwnd) const;
|
||||
EditorWindow* FindWindow(std::string_view windowId);
|
||||
const EditorWindow* FindWindow(std::string_view windowId) const;
|
||||
EditorWindow* FindPrimaryWindow();
|
||||
const EditorWindow* FindPrimaryWindow() const;
|
||||
EditorHostWindow* FindWindow(HWND hwnd);
|
||||
const EditorHostWindow* FindWindow(HWND hwnd) const;
|
||||
EditorHostWindow* FindWindow(std::string_view windowId);
|
||||
const EditorHostWindow* FindWindow(std::string_view windowId) const;
|
||||
EditorHostWindow* FindPrimaryWindow();
|
||||
const EditorHostWindow* FindPrimaryWindow() const;
|
||||
|
||||
bool HasWindows() const;
|
||||
void DestroyClosedWindows();
|
||||
@@ -13,6 +13,26 @@ struct EditorWindowScreenPoint {
|
||||
std::int32_t y = 0;
|
||||
};
|
||||
|
||||
struct EditorWindowScreenRect {
|
||||
std::int32_t left = 0;
|
||||
std::int32_t top = 0;
|
||||
std::int32_t right = 0;
|
||||
std::int32_t bottom = 0;
|
||||
|
||||
[[nodiscard]] std::int32_t Width() const {
|
||||
return right - left;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::int32_t Height() const {
|
||||
return bottom - top;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Contains(const EditorWindowScreenPoint& point) const {
|
||||
return point.x >= left && point.x < right &&
|
||||
point.y >= top && point.y < bottom;
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr float kBorderlessTitleBarHeightDips = 28.0f;
|
||||
|
||||
inline const ::XCEngine::UI::UIColor kShellSurfaceColor(0.10f, 0.10f, 0.10f, 1.0f);
|
||||
|
||||
107
editor/app/Windowing/Host/EditorWindowHostInterfaces.h
Normal file
107
editor/app/Windowing/Host/EditorWindowHostInterfaces.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "Windowing/EditorWindowShared.h"
|
||||
#include "Windowing/EditorWorkspaceWindowProjection.h"
|
||||
#include "Windowing/Host/EditorWindowHostTypes.h"
|
||||
#include "Windowing/Host/EditorWindowPointerCapture.h"
|
||||
#include "Windowing/Host/EditorWindowTypes.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine::UI {
|
||||
|
||||
struct UIPoint;
|
||||
|
||||
} // namespace XCEngine::UI
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
struct UIEditorDockHostTabDropTarget;
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
class EditorWindowDockHostBinding;
|
||||
struct EditorUtilityWindowDescriptor;
|
||||
|
||||
class EditorHostWindow {
|
||||
public:
|
||||
virtual ~EditorHostWindow() = default;
|
||||
|
||||
virtual std::string_view GetWindowId() const = 0;
|
||||
virtual EditorWindowLifecycleState GetLifecycleState() const = 0;
|
||||
virtual bool IsPrimary() const = 0;
|
||||
virtual bool IsWorkspaceWindow() const = 0;
|
||||
virtual bool IsUtilityWindow() const = 0;
|
||||
virtual bool IsClosing() const = 0;
|
||||
virtual bool IsDestroyed() const = 0;
|
||||
virtual bool HasLiveHostWindow() const = 0;
|
||||
virtual const std::wstring& GetTitle() const = 0;
|
||||
virtual const UIEditorWorkspaceController* TryGetWorkspaceController() const = 0;
|
||||
virtual const EditorWorkspaceWindowProjection* TryGetWorkspaceProjection() const = 0;
|
||||
virtual EditorWindowDockHostBinding* TryGetDockHostBinding() = 0;
|
||||
virtual const EditorWindowDockHostBinding* TryGetDockHostBinding() const = 0;
|
||||
virtual ::XCEngine::UI::UIPoint ConvertScreenPixelsToClientDips(
|
||||
const EditorWindowScreenPoint& screenPoint) const = 0;
|
||||
virtual bool TryResolveDockTabDragHotspot(
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
EditorWindowScreenPoint& outHotspot) const = 0;
|
||||
virtual bool TryResolveDockTabDropTarget(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
UIEditorDockHostTabDropTarget& outTarget) const = 0;
|
||||
virtual void InvalidateHostWindow() const = 0;
|
||||
virtual void SetPrimary(bool primary) = 0;
|
||||
virtual void SetTitle(std::wstring title) = 0;
|
||||
virtual void ApplyHostWindowTitle() = 0;
|
||||
virtual void RefreshWorkspaceProjection(EditorWorkspaceWindowProjection projection) = 0;
|
||||
virtual void ResetInteractionState() = 0;
|
||||
virtual void AcquirePointerCapture(EditorWindowPointerCaptureOwner owner) = 0;
|
||||
virtual void ReleasePointerCapture(EditorWindowPointerCaptureOwner owner) = 0;
|
||||
virtual void MarkClosing() = 0;
|
||||
virtual void MarkDestroyed() = 0;
|
||||
virtual bool IsRenderReady() const = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual bool TryGetHostScreenRect(EditorWindowScreenRect& outRect) const = 0;
|
||||
virtual void SetHostScreenPosition(const EditorWindowScreenPoint& screenPoint) = 0;
|
||||
virtual void FocusHostWindow() = 0;
|
||||
virtual void PostCloseToHost() = 0;
|
||||
virtual void DestroyHostWindow() = 0;
|
||||
};
|
||||
|
||||
class EditorWindowHost {
|
||||
public:
|
||||
virtual ~EditorWindowHost() = default;
|
||||
|
||||
virtual EditorHostWindow* CreateWorkspaceWindow(
|
||||
UIEditorWorkspaceController workspaceController,
|
||||
const EditorWindowCreateParams& params) = 0;
|
||||
virtual EditorHostWindow* CreateUtilityWindow(
|
||||
const EditorUtilityWindowDescriptor& descriptor,
|
||||
const EditorWindowCreateParams& params) = 0;
|
||||
virtual EditorHostWindow* FindWindowById(std::string_view windowId) = 0;
|
||||
virtual const EditorHostWindow* FindWindowById(std::string_view windowId) const = 0;
|
||||
virtual std::vector<EditorHostWindow*> GetWindows() = 0;
|
||||
virtual std::vector<const EditorHostWindow*> GetWindows() const = 0;
|
||||
virtual std::wstring_view GetPrimaryWindowTitle() const = 0;
|
||||
virtual bool TryGetCursorScreenPoint(EditorWindowScreenPoint& outPoint) const = 0;
|
||||
virtual EditorWindowScreenRect ResolveFloatingPlacement(
|
||||
const EditorWindowScreenPoint& screenPoint,
|
||||
int preferredWidth,
|
||||
int preferredHeight) const = 0;
|
||||
virtual EditorHostWindow* FindWindowFromScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint) = 0;
|
||||
virtual const EditorHostWindow* FindWindowFromScreenPoint(
|
||||
const EditorWindowScreenPoint& screenPoint) const = 0;
|
||||
virtual void ReapDestroyedWindows() = 0;
|
||||
virtual std::string DescribeWindows() const = 0;
|
||||
virtual void LogRuntimeTrace(std::string_view channel, std::string_view message) const = 0;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
29
editor/app/Windowing/Host/EditorWindowHostTypes.h
Normal file
29
editor/app/Windowing/Host/EditorWindowHostTypes.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "Windowing/Host/EditorWindowTypes.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
inline constexpr int kEditorWindowDefaultPosition =
|
||||
(std::numeric_limits<int>::min)();
|
||||
inline constexpr int kEditorWindowDefaultShowCommand = 5;
|
||||
|
||||
struct EditorWindowCreateParams {
|
||||
std::string windowId = {};
|
||||
std::wstring title = {};
|
||||
int initialX = kEditorWindowDefaultPosition;
|
||||
int initialY = kEditorWindowDefaultPosition;
|
||||
int initialWidth = 1540;
|
||||
int initialHeight = 940;
|
||||
int showCommand = kEditorWindowDefaultShowCommand;
|
||||
EditorWindowCategory category = EditorWindowCategory::Workspace;
|
||||
EditorWindowChromePolicy chromePolicy = {};
|
||||
EditorWindowNativeStylePolicy nativeStylePolicy = {};
|
||||
bool primary = false;
|
||||
bool autoCaptureOnStartup = false;
|
||||
};
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
42
editor/app/Windowing/Host/EditorWindowPointerCapture.h
Normal file
42
editor/app/Windowing/Host/EditorWindowPointerCapture.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
enum class EditorWindowPointerCaptureOwner : std::uint8_t {
|
||||
None = 0,
|
||||
Shell,
|
||||
HostedContent,
|
||||
BorderlessResize,
|
||||
BorderlessChrome,
|
||||
GlobalTabDrag,
|
||||
};
|
||||
|
||||
constexpr bool CanRouteEditorWindowGlobalTabDragPointerMessages(
|
||||
EditorWindowPointerCaptureOwner owner,
|
||||
bool ownsActiveGlobalTabDrag) {
|
||||
return ownsActiveGlobalTabDrag &&
|
||||
owner == EditorWindowPointerCaptureOwner::GlobalTabDrag;
|
||||
}
|
||||
|
||||
constexpr bool CanRouteEditorWindowBorderlessResizePointerMessages(
|
||||
EditorWindowPointerCaptureOwner owner) {
|
||||
return owner == EditorWindowPointerCaptureOwner::BorderlessResize;
|
||||
}
|
||||
|
||||
constexpr bool CanRouteEditorWindowBorderlessChromePointerMessages(
|
||||
EditorWindowPointerCaptureOwner owner) {
|
||||
return owner == EditorWindowPointerCaptureOwner::BorderlessChrome;
|
||||
}
|
||||
|
||||
constexpr bool CanConsumeEditorWindowChromeHover(
|
||||
EditorWindowPointerCaptureOwner owner,
|
||||
bool shellInteractiveCaptureActive,
|
||||
bool hostedContentCaptureActive) {
|
||||
return owner == EditorWindowPointerCaptureOwner::None &&
|
||||
!shellInteractiveCaptureActive &&
|
||||
!hostedContentCaptureActive;
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
67
editor/app/Windowing/Host/EditorWindowTypes.h
Normal file
67
editor/app/Windowing/Host/EditorWindowTypes.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
enum class EditorWindowLifecycleState : std::uint8_t {
|
||||
PendingNativeCreate = 0,
|
||||
NativeAttached,
|
||||
Initializing,
|
||||
Running,
|
||||
Closing,
|
||||
Destroyed,
|
||||
};
|
||||
|
||||
enum class EditorWindowCategory : std::uint8_t {
|
||||
Workspace = 0,
|
||||
Utility,
|
||||
};
|
||||
|
||||
inline std::string_view GetEditorWindowCategoryName(
|
||||
EditorWindowCategory category) {
|
||||
switch (category) {
|
||||
case EditorWindowCategory::Workspace:
|
||||
return "Workspace";
|
||||
case EditorWindowCategory::Utility:
|
||||
return "Utility";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
struct EditorWindowChromePolicy {
|
||||
bool allowDetachedTitleBarTabStrip = true;
|
||||
bool showFrameStats = true;
|
||||
bool showTopmostButton = false;
|
||||
bool topmostByDefault = false;
|
||||
};
|
||||
|
||||
struct EditorWindowNativeStylePolicy {
|
||||
std::uint32_t extendedWindowStyle = 0u;
|
||||
std::uint32_t windowStyle = 0u;
|
||||
bool useHostWindowStyle = true;
|
||||
};
|
||||
|
||||
inline std::string_view GetEditorWindowLifecycleStateName(
|
||||
EditorWindowLifecycleState state) {
|
||||
switch (state) {
|
||||
case EditorWindowLifecycleState::PendingNativeCreate:
|
||||
return "PendingNativeCreate";
|
||||
case EditorWindowLifecycleState::NativeAttached:
|
||||
return "NativeAttached";
|
||||
case EditorWindowLifecycleState::Initializing:
|
||||
return "Initializing";
|
||||
case EditorWindowLifecycleState::Running:
|
||||
return "Running";
|
||||
case EditorWindowLifecycleState::Closing:
|
||||
return "Closing";
|
||||
case EditorWindowLifecycleState::Destroyed:
|
||||
return "Destroyed";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -54,6 +54,30 @@ UIEditorWindowWorkspaceController EditorWindowSystem::BuildWorkspaceMutationCont
|
||||
return UIEditorWindowWorkspaceController(GetPanelRegistry(), GetWindowSet());
|
||||
}
|
||||
|
||||
bool EditorWindowSystem::CommitLiveWindowMutation(
|
||||
std::string_view windowId,
|
||||
const UIEditorWorkspaceController& workspaceController,
|
||||
std::string& outError) {
|
||||
if (windowId.empty()) {
|
||||
outError = "live window mutation missing window id";
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWindowWorkspaceSet nextWindowSet = GetWindowSet();
|
||||
UIEditorWindowWorkspaceState* existingState =
|
||||
FindMutableUIEditorWindowWorkspaceState(nextWindowSet, windowId);
|
||||
if (existingState == nullptr) {
|
||||
outError =
|
||||
"live window mutation references unknown window '" +
|
||||
std::string(windowId) + "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
existingState->workspace = workspaceController.GetWorkspace();
|
||||
existingState->session = workspaceController.GetSession();
|
||||
return m_workspaceStore.TrySetWindowSet(std::move(nextWindowSet), outError);
|
||||
}
|
||||
|
||||
EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForWindowSet(
|
||||
const UIEditorWindowWorkspaceSet& targetWindowSet,
|
||||
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
||||
|
||||
@@ -29,6 +29,10 @@ public:
|
||||
|
||||
const UIEditorWindowWorkspaceSet& GetWindowSet() const;
|
||||
UIEditorWindowWorkspaceController BuildWorkspaceMutationController() const;
|
||||
bool CommitLiveWindowMutation(
|
||||
std::string_view windowId,
|
||||
const UIEditorWorkspaceController& workspaceController,
|
||||
std::string& outError);
|
||||
EditorWindowSynchronizationPlan BuildPlanForWindowSet(
|
||||
const UIEditorWindowWorkspaceSet& targetWindowSet,
|
||||
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
||||
|
||||
@@ -1360,6 +1360,11 @@ namespace Gameplay
|
||||
internal sealed class ManagedRendererInvalidationProbeFeature
|
||||
: ScriptableRendererFeature
|
||||
{
|
||||
public override void Create()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.CreateFeatureCallCount++;
|
||||
}
|
||||
|
||||
protected override void ReleaseRuntimeResources()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.DisposeFeatureCallCount++;
|
||||
@@ -1381,7 +1386,6 @@ namespace Gameplay
|
||||
public ManagedRendererInvalidationProbeRendererData()
|
||||
: base(false)
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.CreateFeatureCallCount++;
|
||||
rendererFeatures =
|
||||
ProbeScriptableObjectFactory
|
||||
.CreateRendererFeatureList(
|
||||
|
||||
@@ -269,6 +269,12 @@ namespace ProjectScripts
|
||||
public sealed class ProjectRendererInvalidationProbeFeature
|
||||
: ScriptableRendererFeature
|
||||
{
|
||||
public override void Create()
|
||||
{
|
||||
ProjectRendererInvalidationProbeState
|
||||
.CreateFeatureCallCount++;
|
||||
}
|
||||
|
||||
protected override void ReleaseRuntimeResources()
|
||||
{
|
||||
ProjectRendererInvalidationProbeState
|
||||
@@ -305,8 +311,6 @@ namespace ProjectScripts
|
||||
public ProjectRendererInvalidationProbeRendererData()
|
||||
: base(false)
|
||||
{
|
||||
ProjectRendererInvalidationProbeState
|
||||
.CreateFeatureCallCount++;
|
||||
rendererFeatures = new ScriptableRendererFeature[]
|
||||
{
|
||||
new ProjectRendererInvalidationProbeFeature()
|
||||
|
||||
184
scripts/Run-RendererPhaseRegression.ps1
Normal file
184
scripts/Run-RendererPhaseRegression.ps1
Normal file
@@ -0,0 +1,184 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$RepoRoot = (Split-Path -Parent $PSScriptRoot),
|
||||
[string]$BuildDir = "",
|
||||
[string]$Config = "Debug",
|
||||
[switch]$SkipBuild,
|
||||
[switch]$IncludeSmoke,
|
||||
[switch]$SkipCtestEnumerate
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Resolve-NormalizedPath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
if (Test-Path -LiteralPath $Path) {
|
||||
return (Resolve-Path -LiteralPath $Path).Path
|
||||
}
|
||||
|
||||
return [System.IO.Path]::GetFullPath($Path)
|
||||
}
|
||||
|
||||
function Join-NormalizedPath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string[]]$Parts
|
||||
)
|
||||
|
||||
return [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($Parts))
|
||||
}
|
||||
|
||||
function Assert-PathExists {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Label
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "$Label not found: $Path"
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-NativeStep {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$FilePath,
|
||||
[string[]]$Arguments = @()
|
||||
)
|
||||
|
||||
Write-Host "==> $Name" -ForegroundColor Cyan
|
||||
& $FilePath @Arguments
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
throw "$Name failed with exit code $exitCode."
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-CtestEnumeration {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$BuildDir,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Config
|
||||
)
|
||||
|
||||
Write-Host "==> Enumerate CTest registry" -ForegroundColor Cyan
|
||||
$output = & ctest --test-dir $BuildDir -N -C $Config 2>&1
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
$output | ForEach-Object { Write-Host $_ }
|
||||
throw "Enumerate CTest registry failed with exit code $exitCode."
|
||||
}
|
||||
|
||||
$lines = $output | ForEach-Object { $_.ToString() }
|
||||
$registeredCount = ($lines | Where-Object { $_ -match '^\s*Test\s+#\d+:' }).Count
|
||||
$notBuiltCount = ($lines | Where-Object { $_ -match '_NOT_BUILT' }).Count
|
||||
$totalLine = $lines | Where-Object { $_ -match '^Total Tests:\s+\d+' } | Select-Object -Last 1
|
||||
if ($null -ne $totalLine) {
|
||||
Write-Host ("{0}; NOT_BUILT placeholders: {1}" -f $totalLine.Trim(), $notBuiltCount) `
|
||||
-ForegroundColor DarkGray
|
||||
} else {
|
||||
Write-Host ("CTest enumeration completed; registered tests: {0}; NOT_BUILT placeholders: {1}" `
|
||||
-f $registeredCount, $notBuiltCount) -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
$RepoRoot = Resolve-NormalizedPath -Path $RepoRoot
|
||||
if ([string]::IsNullOrWhiteSpace($BuildDir)) {
|
||||
$BuildDir = Join-NormalizedPath -Parts @($RepoRoot, "build")
|
||||
} else {
|
||||
$BuildDir = Resolve-NormalizedPath -Path $BuildDir
|
||||
}
|
||||
|
||||
$ctestRoot = Join-NormalizedPath -Parts @($BuildDir, "CTestTestfile.cmake")
|
||||
Assert-PathExists -Path $ctestRoot -Label "CTest root"
|
||||
|
||||
$scriptingExe = Join-NormalizedPath -Parts @(
|
||||
$BuildDir, "tests", "Scripting", $Config, "scripting_tests.exe")
|
||||
$windowingExe = Join-NormalizedPath -Parts @(
|
||||
$BuildDir, "tests", "UI", "Editor", "unit", $Config, "editor_windowing_phase1_tests.exe")
|
||||
|
||||
$srpValidationTests = @(
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineBridgeRuntimeExposesDefaultNativeBackendPolicy",
|
||||
"MonoScriptRuntimeTest.ScriptCoreUniversalRenderPipelineAssetExposesDefaultNativeBackendPolicy",
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineBridgeUsesDefaultRendererSelectionForNativeBackendPolicy",
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineBridgeUsesCameraRendererOverrideAcrossPlanningAndExecution",
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineBridgeRebuildsRendererAfterRendererDataInvalidation",
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineBridgeReleasesPersistentRendererFeatureAcrossRendererInvalidationAndAssetRuntimeRelease",
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineBridgeRebuildsPipelineAfterAssetInvalidation",
|
||||
"MonoScriptRuntimeTest.ManagedRenderPipelineAssetPlansFullscreenStagesFromPipelineStageSupport",
|
||||
"MonoScriptRuntimeTest.ManagedRendererFeatureConfiguresCameraFramePlanThroughPlanningContext",
|
||||
"ProjectScriptAssemblyTest.ProjectManagedBridgeRebuildsRendererAfterProjectRendererDataInvalidation",
|
||||
"ProjectScriptAssemblyTest.ProjectManagedBridgeReleasesProjectRendererCachesAcrossInvalidationAndAssetRuntimeRelease",
|
||||
"ProjectScriptAssemblyTest.ProjectManagedBridgeRebuildsPipelineAfterProjectAssetInvalidation"
|
||||
)
|
||||
$srpValidationFilter = [string]::Join(":", $srpValidationTests)
|
||||
|
||||
Push-Location $RepoRoot
|
||||
try {
|
||||
if (-not $SkipBuild) {
|
||||
$buildTargets = @(
|
||||
"scripting_tests",
|
||||
"editor_windowing_phase1_tests"
|
||||
)
|
||||
if ($IncludeSmoke) {
|
||||
$buildTargets += "editor_ui_smoke_targets"
|
||||
}
|
||||
|
||||
$buildArguments = @(
|
||||
"--build",
|
||||
$BuildDir,
|
||||
"--config",
|
||||
$Config,
|
||||
"--target"
|
||||
) + $buildTargets
|
||||
|
||||
Invoke-NativeStep `
|
||||
-Name "Build renderer phase regression targets ($Config)" `
|
||||
-FilePath "cmake" `
|
||||
-Arguments $buildArguments
|
||||
}
|
||||
|
||||
Assert-PathExists -Path $scriptingExe -Label "scripting_tests.exe"
|
||||
Assert-PathExists -Path $windowingExe -Label "editor_windowing_phase1_tests.exe"
|
||||
|
||||
if (-not $SkipCtestEnumerate) {
|
||||
Write-Host "ctest enumeration may still print *_NOT_BUILT placeholders for targets that were not built." `
|
||||
-ForegroundColor DarkGray
|
||||
Invoke-CtestEnumeration -BuildDir $BuildDir -Config $Config
|
||||
}
|
||||
|
||||
Invoke-NativeStep `
|
||||
-Name "Run managed SRP validation batch (12 tests)" `
|
||||
-FilePath $scriptingExe `
|
||||
-Arguments @("--gtest_filter=$srpValidationFilter")
|
||||
|
||||
Invoke-NativeStep `
|
||||
-Name "Run editor windowing phase1 tests" `
|
||||
-FilePath $windowingExe
|
||||
|
||||
if ($IncludeSmoke) {
|
||||
Invoke-NativeStep `
|
||||
-Name "Run XCUI editor smoke" `
|
||||
-FilePath "ctest" `
|
||||
-Arguments @(
|
||||
"--test-dir", $BuildDir,
|
||||
"-C", $Config,
|
||||
"-R", "^xcui_editor_app_smoke$",
|
||||
"--output-on-failure"
|
||||
)
|
||||
}
|
||||
|
||||
Write-Host "Renderer phase regression passed." -ForegroundColor Green
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
@@ -65,16 +65,10 @@ if(WIN32)
|
||||
find_program(XCENGINE_POWERSHELL_EXECUTABLE NAMES powershell pwsh REQUIRED)
|
||||
|
||||
set(XCENGINE_RENDERING_PHASE_REGRESSION_DEPENDENCIES
|
||||
rendering_all_tests
|
||||
XCEditor
|
||||
scripting_tests
|
||||
editor_windowing_phase1_tests
|
||||
)
|
||||
|
||||
if(TARGET editor_tests)
|
||||
list(APPEND XCENGINE_RENDERING_PHASE_REGRESSION_DEPENDENCIES
|
||||
editor_tests
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_target(rendering_phase_regression_build
|
||||
DEPENDS
|
||||
${XCENGINE_RENDERING_PHASE_REGRESSION_DEPENDENCIES}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# XCEngine 测试规范
|
||||
|
||||
最后更新:`2026-04-04`
|
||||
最后更新:`2026-04-26`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
@@ -178,16 +178,28 @@ ctest --test-dir build -C Debug --output-on-failure
|
||||
- `rendering_integration_material_state_scene`
|
||||
- `rendering_integration_offscreen_scene`
|
||||
|
||||
`rendering_phase_regression` 当前是 Windows 下的 PowerShell 回归入口,依赖:
|
||||
`rendering_phase_regression` 当前是 Windows 下的最小渲染/SRP 回归入口,依赖:
|
||||
|
||||
- `rendering_all_tests`
|
||||
- `editor_tests`
|
||||
- `XCEditor`
|
||||
- `scripting_tests`
|
||||
- `editor_windowing_phase1_tests`
|
||||
|
||||
并执行:
|
||||
|
||||
- `scripts/Run-RendererPhaseRegression.ps1`
|
||||
|
||||
默认覆盖:
|
||||
|
||||
- `ctest --test-dir build -N -C <Config>` 的测试注册枚举健康检查
|
||||
- 一组固定的 managed SRP focused scripting 回归
|
||||
- `editor_windowing_phase1_tests`
|
||||
|
||||
补充说明:
|
||||
|
||||
- 该入口当前刻意不依赖 `editor_ui_tests`。
|
||||
- `xcui_editor_app_smoke` 当前保留为可选补充验证,不再作为默认构建门槛。
|
||||
- 如果要在现有产物上补跑 smoke,使用 `powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\Run-RendererPhaseRegression.ps1 -RepoRoot . -BuildDir .\build -Config Debug -SkipBuild -IncludeSmoke`。
|
||||
- `ctest -N` 现在应能正常完成枚举;输出里的 `*_NOT_BUILT` 占位项表示目标尚未构建,不再表示 discover/include 生成损坏。
|
||||
|
||||
### 4.5 RHI
|
||||
|
||||
| 类别 | target |
|
||||
@@ -316,7 +328,7 @@ build\tests\RHI\integration\backpack\Debug\rhi_integration_backpack.exe --gtest_
|
||||
按改动类型选择最小有效验证:
|
||||
|
||||
- 改 `engine/RHI`:先跑 `rhi_abstraction_tests` 或 `rhi_backend_tests`
|
||||
- 改 `engine/Rendering`:先跑 `rendering_unit_tests` 和最相关的 `rendering_integration_*`
|
||||
- 改 `engine/Rendering`:先跑 `rendering_unit_tests` 和最相关的 `rendering_integration_*`;若改动涉及当前 managed SRP / renderer invalidation / editor viewport 主链回归,补跑 `rendering_phase_regression`
|
||||
- 改 `editor/Viewport`:先跑 `editor_tests`,必要时再跑 `rendering_phase_regression`
|
||||
- 改脚本运行时 / managed / 项目脚本程序集:先跑 `scripting_tests`;如果还要验证 editor / runtime 实际使用的项目程序集目录,再补构建 `xcengine_project_managed_assemblies`
|
||||
- 改资源导入 / `.meta` / artifact:优先跑对应 `Resources/*` tests
|
||||
|
||||
@@ -95,7 +95,7 @@ endif()
|
||||
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(editor_ui_tests
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
DISCOVERY_MODE POST_BUILD
|
||||
)
|
||||
|
||||
add_executable(editor_windowing_phase1_tests
|
||||
@@ -139,7 +139,7 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
gtest_discover_tests(editor_windowing_phase1_tests
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
DISCOVERY_MODE POST_BUILD
|
||||
)
|
||||
|
||||
if(TARGET XCUIEditorAppLib)
|
||||
@@ -214,6 +214,6 @@ if(TARGET XCUIEditorAppLib)
|
||||
endif()
|
||||
|
||||
gtest_discover_tests(editor_app_feature_tests
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
DISCOVERY_MODE POST_BUILD
|
||||
)
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user