Refactor new editor boundaries and test ownership

This commit is contained in:
2026-04-19 15:52:28 +08:00
parent dc13b56cf3
commit 93f06e84ed
279 changed files with 6349 additions and 3238 deletions

View File

@@ -1,11 +1,15 @@
#include "Platform/Win32/EditorWindow.h"
#include "Bootstrap/EditorResources.h"
#include "Platform/Win32/EditorWindowChromeController.h"
#include "Platform/Win32/EditorWindowConstants.h"
#include "Platform/Win32/EditorWindowFrameOrchestrator.h"
#include "Platform/Win32/EditorWindowInputController.h"
#include "Platform/Win32/EditorWindowInternalState.h"
#include "Platform/Win32/EditorWindowPlatformInternal.h"
#include "Platform/Win32/EditorWindowRuntimeController.h"
#include "Platform/Win32/EditorWindowRuntimeInternal.h"
#include "State/EditorContext.h"
#include "Composition/EditorContext.h"
#include <XCEditor/Foundation/UIEditorRuntimeTrace.h>
#include <XCEngine/UI/Types.h>
@@ -74,11 +78,15 @@ EditorWindow::EditorWindow(
std::wstring title,
bool primary,
UIEditorWorkspaceController workspaceController)
: m_state(std::make_unique<EditorWindowState>()) {
: m_state(std::make_unique<EditorWindowState>())
, m_chromeController(std::make_unique<Internal::EditorWindowChromeController>())
, m_frameOrchestrator(std::make_unique<Internal::EditorWindowFrameOrchestrator>())
, m_inputController(std::make_unique<Internal::EditorWindowInputController>())
, m_runtime(std::make_unique<Internal::EditorWindowRuntimeController>(
std::move(workspaceController))) {
m_state->window.windowId = std::move(windowId);
m_state->window.title = std::move(title);
m_state->window.primary = primary;
m_state->composition.workspaceController = std::move(workspaceController);
UpdateCachedTitleText();
}
@@ -105,15 +113,15 @@ bool EditorWindow::IsClosing() const {
}
bool EditorWindow::IsRenderReady() const {
return m_state->render.ready;
return m_runtime->IsReady();
}
bool EditorWindow::IsTrackingMouseLeave() const {
return m_state->input.trackingMouseLeave;
return m_inputController->IsTrackingMouseLeave();
}
bool EditorWindow::HasHoveredBorderlessResizeEdge() const {
return m_state->chrome.runtime.GetHoveredBorderlessResizeEdge() !=
return m_chromeController->GetHoveredBorderlessResizeEdge() !=
Host::BorderlessWindowResizeEdge::None;
}
@@ -122,36 +130,36 @@ const std::wstring& EditorWindow::GetTitle() const {
}
const UIEditorWorkspaceController& EditorWindow::GetWorkspaceController() const {
return m_state->composition.workspaceController;
return m_runtime->GetWorkspaceController();
}
UIEditorWorkspaceController& EditorWindow::GetMutableWorkspaceController() {
return m_state->composition.workspaceController;
return m_runtime->GetMutableWorkspaceController();
}
const EditorShellRuntime& EditorWindow::GetShellRuntime() const {
return m_state->composition.shellRuntime;
return m_runtime->GetShellRuntime();
}
EditorShellRuntime& EditorWindow::GetShellRuntime() {
return m_state->composition.shellRuntime;
return m_runtime->GetShellRuntime();
}
const UIEditorShellInteractionFrame& EditorWindow::GetShellFrame() const {
return m_state->composition.shellRuntime.GetShellFrame();
return m_runtime->GetShellFrame();
}
const UIEditorShellInteractionState& EditorWindow::GetShellInteractionState() const {
return m_state->composition.shellRuntime.GetShellInteractionState();
return m_runtime->GetShellInteractionState();
}
void EditorWindow::SetExternalDockHostDropPreview(
const Widgets::UIEditorDockHostDropPreviewState& preview) {
m_state->composition.shellRuntime.SetExternalDockHostDropPreview(preview);
m_runtime->SetExternalDockHostDropPreview(preview);
}
void EditorWindow::ClearExternalDockHostDropPreview() {
m_state->composition.shellRuntime.ClearExternalDockHostDropPreview();
m_runtime->ClearExternalDockHostDropPreview();
}
void EditorWindow::AttachHwnd(HWND hwnd) {
@@ -162,7 +170,7 @@ void EditorWindow::AttachHwnd(HWND hwnd) {
void EditorWindow::MarkDestroyed() {
m_state->window.hwnd = nullptr;
m_state->window.closing = false;
m_state->input.trackingMouseLeave = false;
m_inputController->ResetWindowState();
}
void EditorWindow::MarkClosing() {
@@ -174,7 +182,7 @@ void EditorWindow::ClearClosing() {
}
void EditorWindow::SetTrackingMouseLeave(bool trackingMouseLeave) {
m_state->input.trackingMouseLeave = trackingMouseLeave;
m_inputController->SetTrackingMouseLeave(trackingMouseLeave);
}
void EditorWindow::SetTitle(std::wstring title) {
@@ -183,7 +191,7 @@ void EditorWindow::SetTitle(std::wstring title) {
}
void EditorWindow::ReplaceWorkspaceController(UIEditorWorkspaceController workspaceController) {
m_state->composition.workspaceController = std::move(workspaceController);
m_runtime->ReplaceWorkspaceController(std::move(workspaceController));
}
void EditorWindow::InvalidateHostWindow() const {
@@ -203,130 +211,50 @@ bool EditorWindow::Initialize(
}
Host::RefreshBorderlessWindowDwmDecorations(m_state->window.hwnd);
m_state->chrome.runtime.Reset();
m_state->chrome.runtime.SetWindowDpi(QueryWindowDpi(m_state->window.hwnd));
m_state->render.renderer.SetDpiScale(GetDpiScale());
m_chromeController->Reset();
m_chromeController->SetWindowDpi(QueryWindowDpi(m_state->window.hwnd));
m_runtime->SetDpiScale(GetDpiScale());
std::ostringstream dpiTrace = {};
dpiTrace << "initial dpi=" << m_state->chrome.runtime.GetWindowDpi()
dpiTrace << "initial dpi=" << m_chromeController->GetWindowDpi()
<< " scale=" << GetDpiScale();
LogRuntimeTrace("window", dpiTrace.str());
if (!m_state->render.renderer.Initialize(m_state->window.hwnd)) {
LogRuntimeTrace("app", "renderer initialization failed");
return false;
}
RECT clientRect = {};
GetClientRect(m_state->window.hwnd, &clientRect);
const int clientWidth = (std::max)(clientRect.right - clientRect.left, 1L);
const int clientHeight = (std::max)(clientRect.bottom - clientRect.top, 1L);
if (!m_state->render.windowRenderer.Initialize(
m_state->window.hwnd,
clientWidth,
clientHeight)) {
LogRuntimeTrace("app", "d3d12 window renderer initialization failed");
m_state->render.renderer.Shutdown();
return false;
}
const Host::D3D12WindowRenderLoopAttachResult attachResult =
m_state->render.windowRenderLoop.Attach(
m_state->render.renderer,
m_state->render.windowRenderer);
if (!attachResult.interopWarning.empty()) {
LogRuntimeTrace("app", attachResult.interopWarning);
}
editorContext.AttachTextMeasurer(m_state->render.renderer);
m_state->composition.shellRuntime.Initialize(
return m_runtime->Initialize(
m_state->window.hwnd,
repoRoot,
m_state->render.renderer,
m_state->render.renderer);
m_state->composition.shellRuntime.AttachViewportWindowRenderer(
m_state->render.windowRenderer);
m_state->composition.shellRuntime.SetViewportSurfacePresentationEnabled(
attachResult.hasViewportSurfacePresentation);
std::string titleBarLogoError = {};
if (!LoadEmbeddedPngTexture(
m_state->render.renderer,
IDR_PNG_LOGO_ICON,
m_state->render.titleBarLogoIcon,
titleBarLogoError)) {
LogRuntimeTrace("icons", "titlebar logo_icon.png: " + titleBarLogoError);
}
if (!m_state->composition.shellRuntime.GetBuiltInIconError().empty()) {
LogRuntimeTrace("icons", m_state->composition.shellRuntime.GetBuiltInIconError());
}
LogRuntimeTrace(
"app",
"shell runtime initialized: " +
editorContext.DescribeWorkspaceState(
m_state->composition.workspaceController,
m_state->composition.shellRuntime.GetShellInteractionState()));
m_state->render.ready = true;
m_state->render.autoScreenshot.Initialize(captureRoot);
if (autoCaptureOnStartup && IsAutoCaptureOnStartupEnabled()) {
m_state->render.autoScreenshot.RequestCapture("startup");
editorContext.SetStatus("Capture", "Startup capture requested.");
}
return true;
editorContext,
captureRoot,
autoCaptureOnStartup);
}
void EditorWindow::Shutdown() {
ForceReleasePointerCapture();
m_state->render.ready = false;
m_state->render.autoScreenshot.Shutdown();
m_state->composition.shellRuntime.Shutdown();
m_state->render.renderer.ReleaseTexture(m_state->render.titleBarLogoIcon);
m_state->render.windowRenderLoop.Detach();
m_state->render.windowRenderer.Shutdown();
m_state->render.renderer.Shutdown();
m_state->input.pendingEvents.clear();
m_state->chrome.chromeState = {};
m_state->chrome.runtime.Reset();
m_runtime->Shutdown();
m_inputController->ClearPendingEvents();
m_chromeController->Reset();
}
void EditorWindow::ResetInteractionState() {
ForceReleasePointerCapture();
m_state->input.pendingEvents.clear();
m_state->input.trackingMouseLeave = false;
m_state->input.modifierTracker.Reset();
m_state->composition.shellRuntime.ResetInteractionState();
m_state->chrome.chromeState = {};
m_state->chrome.runtime.EndBorderlessResize();
m_state->chrome.runtime.EndBorderlessWindowDragRestore();
m_state->chrome.runtime.EndInteractiveResize();
m_state->chrome.runtime.SetHoveredBorderlessResizeEdge(
m_inputController->ResetInteractionState();
m_runtime->ResetInteractionState();
m_chromeController->ResetChromeState();
m_chromeController->EndBorderlessResize();
m_chromeController->EndBorderlessWindowDragRestore();
m_chromeController->EndInteractiveResize();
m_chromeController->SetHoveredBorderlessResizeEdge(
Host::BorderlessWindowResizeEdge::None);
m_state->chrome.runtime.ClearPredictedClientPixelSize();
m_chromeController->ClearPredictedClientPixelSize();
}
bool EditorWindow::ApplyWindowResize(UINT width, UINT height) {
if (!m_state->render.ready || width == 0u || height == 0u) {
if (!m_runtime->IsReady() || width == 0u || height == 0u) {
return false;
}
const Host::D3D12WindowRenderLoopResizeResult resizeResult =
m_state->render.windowRenderLoop.ApplyResize(width, height);
m_state->composition.shellRuntime.SetViewportSurfacePresentationEnabled(
resizeResult.hasViewportSurfacePresentation);
if (!resizeResult.windowRendererWarning.empty()) {
LogRuntimeTrace("present", resizeResult.windowRendererWarning);
}
if (!resizeResult.interopWarning.empty()) {
LogRuntimeTrace("present", resizeResult.interopWarning);
}
return resizeResult.hasViewportSurfacePresentation;
return m_runtime->ApplyResize(width, height);
}
bool EditorWindow::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight) const {
@@ -353,7 +281,7 @@ bool EditorWindow::QueryCurrentClientPixelSize(UINT& outWidth, UINT& outHeight)
}
bool EditorWindow::ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight) const {
if (m_state->chrome.runtime.TryGetPredictedClientPixelSize(outWidth, outHeight)) {
if (m_chromeController->TryGetPredictedClientPixelSize(outWidth, outHeight)) {
return true;
}
@@ -361,7 +289,7 @@ bool EditorWindow::ResolveRenderClientPixelSize(UINT& outWidth, UINT& outHeight)
}
float EditorWindow::GetDpiScale() const {
return m_state->chrome.runtime.GetDpiScale(kBaseDpiScale);
return m_chromeController->GetDpiScale(kBaseDpiScale);
}
float EditorWindow::PixelsToDips(float pixels) const {
@@ -381,7 +309,7 @@ UIPoint EditorWindow::ConvertScreenPixelsToClientDips(const POINT& screenPoint)
ScreenToClient(m_state->window.hwnd, &clientPoint);
}
const float dpiScale = m_state->chrome.runtime.GetDpiScale(kBaseDpiScale);
const float dpiScale = m_chromeController->GetDpiScale(kBaseDpiScale);
return UIPoint(
dpiScale > 0.0f
? static_cast<float>(clientPoint.x) / dpiScale
@@ -395,7 +323,7 @@ void EditorWindow::OnResize(UINT width, UINT height) {
bool matchesPredictedClientSize = false;
UINT predictedWidth = 0u;
UINT predictedHeight = 0u;
if (m_state->chrome.runtime.TryGetPredictedClientPixelSize(
if (m_chromeController->TryGetPredictedClientPixelSize(
predictedWidth,
predictedHeight)) {
matchesPredictedClientSize =
@@ -403,7 +331,7 @@ void EditorWindow::OnResize(UINT width, UINT height) {
predictedHeight == height;
}
m_state->chrome.runtime.ClearPredictedClientPixelSize();
m_chromeController->ClearPredictedClientPixelSize();
if (IsBorderlessWindowEnabled() && m_state->window.hwnd != nullptr) {
Host::RefreshBorderlessWindowDwmDecorations(m_state->window.hwnd);
}
@@ -414,12 +342,12 @@ void EditorWindow::OnResize(UINT width, UINT height) {
}
void EditorWindow::OnEnterSizeMove() {
m_state->chrome.runtime.BeginInteractiveResize();
m_chromeController->BeginInteractiveResize();
}
void EditorWindow::OnExitSizeMove() {
m_state->chrome.runtime.EndInteractiveResize();
m_state->chrome.runtime.ClearPredictedClientPixelSize();
m_chromeController->EndInteractiveResize();
m_chromeController->ClearPredictedClientPixelSize();
UINT width = 0u;
UINT height = 0u;
if (QueryCurrentClientPixelSize(width, height)) {
@@ -428,8 +356,8 @@ void EditorWindow::OnExitSizeMove() {
}
void EditorWindow::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
m_state->chrome.runtime.SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
m_state->render.renderer.SetDpiScale(GetDpiScale());
m_chromeController->SetWindowDpi(dpi == 0u ? kDefaultDpi : dpi);
m_runtime->SetDpiScale(GetDpiScale());
if (m_state->window.hwnd != nullptr) {
const LONG windowWidth = suggestedRect.right - suggestedRect.left;
const LONG windowHeight = suggestedRect.bottom - suggestedRect.top;
@@ -450,7 +378,7 @@ void EditorWindow::OnDpiChanged(UINT dpi, const RECT& suggestedRect) {
}
std::ostringstream trace = {};
trace << "dpi changed to " << m_state->chrome.runtime.GetWindowDpi()
trace << "dpi changed to " << m_chromeController->GetWindowDpi()
<< " scale=" << GetDpiScale();
LogRuntimeTrace("window", trace.str());
}