diff --git a/docs/used/Editor_Window_Architecture_Refactor_Plan_2026-04-25.md b/docs/used/Editor_Window_Architecture_Refactor_Plan_2026-04-25.md deleted file mode 100644 index b168d20b..00000000 --- a/docs/used/Editor_Window_Architecture_Refactor_Plan_2026-04-25.md +++ /dev/null @@ -1,176 +0,0 @@ -# Editor Window Architecture Refactor Plan - 2026-04-25 - -## 背景 - -当前 `editor/` 的窗口宿主已经有两类窗口: - -- Workspace window:主窗口和由 dock tab/panel 拖出的独立窗口,内容是 `EditorWorkspaceWindowContentController`。 -- Utility window:颜色选择器、Add Component 等天然浮窗,内容是 `EditorUtilityWindowContentController`。 - -两者共享 `EditorWindow -> EditorWindowRuntimeController -> D3D12WindowRenderLoop` 这套 native/D3D12/chrome/input/lifecycle 宿主。当前主要问题是窗口类型依赖 `TryGetWorkspaceBinding()`、`TryGetWorkspaceController()` 等 capability 是否存在来隐式推断,导致 workspace coordinator、utility coordinator、lifecycle、chrome policy 的语义不够清晰。 - -## 目标 - -把窗口类型变成一等概念,让代码明确区分: - -```text -Native EditorWindow host - -> Workspace content window - -> Utility content window -``` - -并让 workspace docking、utility registry、frame request、chrome policy 分别只处理自己的窗口域。 - -## 非目标 - -- 不重写 docking/workspace model。 -- 不重写 D3D12 UI renderer。 -- 不一次性重排 `editor/app/Platform/Win32` 的目录结构。 -- 不改变颜色选择器和 Add Component 的现有用户行为,第一轮保持单例复用。 - -## 收口状态 - -第一轮重构已收口: - -- `EditorWindowCategory` 已接入窗口创建、session 和 runtime 查询。 -- Workspace / utility frame request 已拆分为独立子结构。 -- Workspace coordinator 显式过滤 workspace window。 -- Utility coordinator 通过 descriptor 创建 utility window,并保留单例复用策略。 -- Utility descriptor 已携带 chrome/native style policy。 -- Window content 已有显式 capabilities,用于创建时校验 category 和 content 是否匹配。 -- `XCUIEditorApp` 输出名已改为 `XCEngine.exe`。 -- 用户已完成 GUI 行为验证。 -- `cmake --build build --config Debug --target XCUIEditorApp` 已通过。 -- `build/editor/Debug/XCEngine.exe` 12 秒启动冒烟测试已通过,进程可正常响应关闭请求。 - -## 执行步骤 - -### 1. 显式窗口分类 - -新增 `EditorWindowCategory`: - -```cpp -enum class EditorWindowCategory { - Workspace, - Utility, -}; -``` - -改动点: - -- `EditorWindowState` -- `EditorWindowSession` -- `EditorWindow` -- `EditorWindowManager::CreateParams` -- `EditorWindowHostRuntime::CreateEditorWindow` -- workspace window 创建处传 `Workspace` -- utility window 创建处传 `Utility` - -完成后,调用方可以使用: - -```cpp -window.GetCategory(); -window.IsWorkspaceWindow(); -window.IsUtilityWindow(); -``` - -### 2. Workspace coordinator 显式过滤 workspace window - -改动点: - -- `BuildLiveWindowWorkspaceSet` -- `SynchronizeWindowsFromWindowSet` -- `FindTopmostWindowAtScreenPoint` -- global tab drag / detach 入口 - -规则: - -- `EditorWindowWorkspaceCoordinator` 只处理 `Workspace` 窗口。 -- utility window 不参与 workspace set。 -- cross-window dock hit test 不把 utility window 当作 drop target。 - -### 3. Utility coordinator 显式创建 utility window - -扩展 `EditorUtilityWindowDescriptor`,逐步承载: - -- window category -- reuse policy -- preferred/minimum outer size -- 后续可加入 style/chrome policy - -第一轮保持: - -- `ColorPicker` 单例 windowId:`utility.color-picker` -- `AddComponent` 单例 windowId:`utility.add-component` -- 重复打开时 focus 现有窗口 - -### 4. 拆分 frame transfer request - -把当前混合结构: - -```cpp -EditorWindowFrameTransferRequests { - beginGlobalTabDrag; - detachPanel; - openUtilityWindow; -} -``` - -拆成: - -```cpp -EditorWorkspaceWindowFrameRequests { - beginGlobalTabDrag; - detachPanel; -} - -EditorUtilityWindowFrameRequests { - openUtilityWindow; -} - -EditorWindowFrameTransferRequests { - workspace; - utility; -} -``` - -规则: - -- `EditorWindowWorkspaceCoordinator` 只消费 `transferRequests.workspace`。 -- `EditorUtilityWindowCoordinator` 只消费 `transferRequests.utility`。 -- queued immediate frame 合并时分别合并两个子结构。 - -### 5. 最小 chrome/window policy - -新增轻量 policy,不急着改完整视觉: - -```cpp -struct EditorWindowChromePolicy { - bool allowDetachedTitleBarTabStrip = false; - bool showFrameStats = true; -}; -``` - -初始规则: - -- Workspace window:允许 detached titlebar tab strip。 -- Utility window:不允许 detached titlebar tab strip。 - -后续再扩展 maximize/minimize/toolwindow/taskbar style。 - -## 验证 - -已执行: - -- `cmake --build build --config Debug --target XCUIEditorApp` -- 启动 `build/editor/Debug/XCEngine.exe`,运行 12 秒后关闭 - -行为检查: - -- 主窗口仍能启动。 -- 从 dock tab 拖出 workspace window。 -- detached workspace window 可拖回其他 workspace window。 -- 打开 Color Picker 时创建 utility window。 -- 重复打开 Color Picker 时 focus 已有窗口。 -- utility window 不作为 dock drop target。 -- 关闭 primary workspace window 仍关闭所有窗口。 diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 3123644a..76c22397 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -240,6 +240,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) ) set(XCUI_EDITOR_APP_WINDOWING_SOURCES + app/Windowing/Content/EditorWindowContentFactory.cpp app/Windowing/Content/EditorUtilityWindowContentController.cpp app/Windowing/Content/EditorWorkspaceWindowContentController.cpp app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp diff --git a/editor/app/Bootstrap/Application.cpp b/editor/app/Bootstrap/Application.cpp index 88ee02d6..fed0f071 100644 --- a/editor/app/Bootstrap/Application.cpp +++ b/editor/app/Bootstrap/Application.cpp @@ -4,7 +4,6 @@ #include "Composition/EditorContext.h" #include "Platform/Win32/Windowing/EditorWindowManager.h" #include "Platform/Win32/Windowing/EditorWindow.h" -#include "Windowing/Content/EditorWorkspaceWindowContentController.h" #include "Platform/Win32/System/Win32SystemInteractionHost.h" #include "Support/EnvironmentFlags.h" #include "Support/ExecutablePath.h" @@ -149,9 +148,8 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { createParams.primary = true; createParams.autoCaptureOnStartup = App::IsEnvironmentFlagEnabled("XCUI_AUTO_CAPTURE_ON_STARTUP"); - if (m_windowManager->CreateEditorWindow( - App::CreateEditorWorkspaceWindowContentController( - m_editorContext->BuildWorkspaceController()), + if (m_windowManager->CreateWorkspaceWindow( + m_editorContext->BuildWorkspaceController(), createParams) == nullptr) { AppendUIEditorRuntimeTrace("app", "primary window creation failed"); return false; diff --git a/editor/app/Composition/EditorWindowWorkspaceStore.cpp b/editor/app/Composition/EditorWindowWorkspaceStore.cpp index 95116b0c..8e0fe9c7 100644 --- a/editor/app/Composition/EditorWindowWorkspaceStore.cpp +++ b/editor/app/Composition/EditorWindowWorkspaceStore.cpp @@ -1,5 +1,6 @@ #include "Composition/EditorWindowWorkspaceStore.h" +#include #include namespace XCEngine::UI::Editor::App { @@ -21,4 +22,86 @@ bool EditorWindowWorkspaceStore::ValidateWindowSet( return true; } +bool EditorWindowWorkspaceStore::TrySetWindowSet( + UIEditorWindowWorkspaceSet windowSet, + std::string& outError) { + if (!ValidateWindowSet(windowSet, outError)) { + return false; + } + + m_windowSet = std::move(windowSet); + outError.clear(); + return true; +} + +bool EditorWindowWorkspaceStore::UpsertWindowState( + const UIEditorWindowWorkspaceState& windowState, + bool primary, + std::string& outError) { + UIEditorWindowWorkspaceSet nextWindowSet = m_windowSet; + if (UIEditorWindowWorkspaceState* existingState = + FindMutableUIEditorWindowWorkspaceState(nextWindowSet, windowState.windowId); + existingState != nullptr) { + *existingState = windowState; + } else { + nextWindowSet.windows.push_back(windowState); + } + + if (primary || nextWindowSet.primaryWindowId.empty()) { + nextWindowSet.primaryWindowId = windowState.windowId; + } + if (nextWindowSet.activeWindowId.empty() || + FindUIEditorWindowWorkspaceState(nextWindowSet, nextWindowSet.activeWindowId) == nullptr) { + nextWindowSet.activeWindowId = windowState.windowId; + } + + if (!ValidateWindowSet(nextWindowSet, outError)) { + return false; + } + + m_windowSet = std::move(nextWindowSet); + outError.clear(); + return true; +} + +void EditorWindowWorkspaceStore::RemoveWindowState(std::string_view windowId, bool primary) { + if (primary) { + m_windowSet = {}; + return; + } + + UIEditorWindowWorkspaceSet nextWindowSet = m_windowSet; + nextWindowSet.windows.erase( + std::remove_if( + nextWindowSet.windows.begin(), + nextWindowSet.windows.end(), + [windowId](const UIEditorWindowWorkspaceState& state) { + return state.windowId == windowId; + }), + nextWindowSet.windows.end()); + + if (nextWindowSet.windows.empty()) { + m_windowSet = {}; + return; + } + + if (nextWindowSet.primaryWindowId == windowId || + FindUIEditorWindowWorkspaceState(nextWindowSet, nextWindowSet.primaryWindowId) == nullptr) { + nextWindowSet.primaryWindowId = nextWindowSet.windows.front().windowId; + } + if (nextWindowSet.activeWindowId == windowId || + FindUIEditorWindowWorkspaceState(nextWindowSet, nextWindowSet.activeWindowId) == nullptr) { + nextWindowSet.activeWindowId = nextWindowSet.primaryWindowId; + } + + std::string ignoredError = {}; + if (ValidateWindowSet(nextWindowSet, ignoredError)) { + m_windowSet = std::move(nextWindowSet); + } +} + +bool EditorWindowWorkspaceStore::IsPrimaryWindowId(std::string_view windowId) const { + return !windowId.empty() && m_windowSet.primaryWindowId == windowId; +} + } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorWindowWorkspaceStore.h b/editor/app/Composition/EditorWindowWorkspaceStore.h index 5b58c8fc..236e580d 100644 --- a/editor/app/Composition/EditorWindowWorkspaceStore.h +++ b/editor/app/Composition/EditorWindowWorkspaceStore.h @@ -17,9 +17,22 @@ public: bool ValidateWindowSet( const UIEditorWindowWorkspaceSet& windowSet, std::string& outError) const; + bool TrySetWindowSet( + UIEditorWindowWorkspaceSet windowSet, + std::string& outError); + bool UpsertWindowState( + const UIEditorWindowWorkspaceState& windowState, + bool primary, + std::string& outError); + void RemoveWindowState(std::string_view windowId, bool primary); + bool IsPrimaryWindowId(std::string_view windowId) const; + const UIEditorWindowWorkspaceSet& GetWindowSet() const { + return m_windowSet; + } private: UIEditorPanelRegistry m_panelRegistry = {}; + UIEditorWindowWorkspaceSet m_windowSet = {}; }; } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp b/editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp index f5d4be4c..870dec64 100644 --- a/editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorUtilityWindowCoordinator.cpp @@ -2,9 +2,7 @@ #include "UtilityWindows/EditorUtilityWindowRegistry.h" #include "Platform/Win32/Windowing/EditorFloatingWindowPlacement.h" -#include "Windowing/Content/EditorUtilityWindowContentController.h" #include "Platform/Win32/Windowing/EditorWindow.h" -#include "Windowing/Content/EditorWindowContentController.h" #include "Platform/Win32/Windowing/EditorWindowHostRuntime.h" #include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h" @@ -26,6 +24,13 @@ LONG ResolveOuterDimension(float value, LONG fallback) { : fallback; } +POINT ToNativePoint(const EditorWindowScreenPoint& point) { + return POINT{ + static_cast(point.x), + static_cast(point.y), + }; +} + } EditorUtilityWindowCoordinator::EditorUtilityWindowCoordinator( @@ -98,15 +103,6 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest( } } - std::unique_ptr contentController = - CreateEditorUtilityWindowContentController(*descriptor); - if (contentController == nullptr) { - LogRuntimeTrace( - "utility", - "open utility window request rejected: content factory returned null"); - return false; - } - EditorWindowHostRuntime::CreateParams createParams = {}; createParams.windowId = std::string(descriptor->windowId); createParams.title = descriptor->title; @@ -122,7 +118,7 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest( createParams.initialHeight); if (request.useCursorPlacement) { const RECT rect = BuildEditorFloatingWindowRect( - request.screenPoint, + ToNativePoint(request.screenPoint), createParams.initialWidth, createParams.initialHeight); createParams.initialX = rect.left; @@ -132,7 +128,7 @@ bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest( } EditorWindow* utilityWindow = - m_hostRuntime.CreateEditorWindow(std::move(contentController), createParams); + m_hostRuntime.CreateUtilityWindow(*descriptor, createParams); if (utilityWindow == nullptr) { LogRuntimeTrace("utility", "failed to create utility window"); return false; diff --git a/editor/app/Platform/Win32/Windowing/EditorWindow.cpp b/editor/app/Platform/Win32/Windowing/EditorWindow.cpp index f9e125a1..5bfb0acc 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindow.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindow.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -55,12 +56,7 @@ UINT QueryWindowDpi(HWND hwnd) { } bool ResolveVerboseRuntimeTraceEnabled() { - wchar_t buffer[8] = {}; - const DWORD length = GetEnvironmentVariableW( - L"XCUIEDITOR_VERBOSE_TRACE", - buffer, - static_cast(std::size(buffer))); - return length > 0u && buffer[0] != L'0'; + return IsEditorWindowVerboseRuntimeTraceEnabled(); } void LogRuntimeTrace(std::string_view channel, std::string_view message) { @@ -164,6 +160,14 @@ const UIEditorWorkspaceController& EditorWindow::GetWorkspaceController() const return *workspaceController; } +EditorWindowDockHostBinding* EditorWindow::TryGetDockHostBinding() { + return m_runtime->TryGetDockHostBinding(); +} + +const EditorWindowDockHostBinding* EditorWindow::TryGetDockHostBinding() const { + return m_runtime->TryGetDockHostBinding(); +} + void EditorWindow::AttachHwnd(HWND hwnd) { m_session->AttachHwnd(hwnd); } @@ -474,6 +478,18 @@ using ::XCEngine::UI::UIRect; namespace { +std::optional QueryCursorScreenPoint() { + POINT screenPoint = {}; + if (!GetCursorPos(&screenPoint)) { + return std::nullopt; + } + + return EditorWindowScreenPoint{ + .x = screenPoint.x, + .y = screenPoint.y, + }; +} + std::uint8_t ButtonMask(UIPointerButton button) { switch (button) { case UIPointerButton::Left: return 1u << 0u; @@ -646,6 +662,7 @@ EditorWindowFrameTransferRequests EditorWindow::RenderRuntimeFrame( .editorContext = editorContext, .bounds = workspaceBounds, .inputEvents = frameEvents, + .cursorScreenPoint = QueryCursorScreenPoint(), .captureStatusText = m_runtime->BuildCaptureStatusText(), .primary = m_session->IsPrimary(), .globalTabDragActive = globalTabDragActive, diff --git a/editor/app/Platform/Win32/Windowing/EditorWindow.h b/editor/app/Platform/Win32/Windowing/EditorWindow.h index 4d7f4afc..3b92e97b 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindow.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindow.h @@ -52,6 +52,7 @@ namespace XCEngine::UI::Editor::App { class EditorContext; class EditorWindowContentController; class EditorWindowChromeController; +class EditorWindowDockHostBinding; class EditorWindowFrameDriver; class EditorWindowFrameOrchestrator; class EditorWindowHostRuntime; @@ -93,6 +94,8 @@ public: std::string_view GetCachedTitleText() const; const UIEditorWorkspaceController* TryGetWorkspaceController() const; const UIEditorWorkspaceController& GetWorkspaceController() const; + EditorWindowDockHostBinding* TryGetDockHostBinding(); + const EditorWindowDockHostBinding* TryGetDockHostBinding() const; ::XCEngine::UI::UIPoint ConvertScreenPixelsToClientDips(const POINT& screenPoint) const; private: diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp index b8ac50ef..e1bbb607 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp @@ -4,12 +4,15 @@ #include "Composition/EditorContext.h" #include "Platform/Win32/Chrome/EditorWindowChromeController.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 + #include #include @@ -57,10 +60,12 @@ std::string DescribeHostWindows( EditorWindowHostRuntime::EditorWindowHostRuntime( EditorWindowHostConfig hostConfig, std::filesystem::path repoRoot, - EditorContext& editorContext) + EditorContext& editorContext, + EditorWindowContentFactory& contentFactory) : m_hostConfig(hostConfig), m_repoRoot(std::move(repoRoot)), - m_editorContext(editorContext) {} + m_editorContext(editorContext), + m_contentFactory(contentFactory) {} EditorWindowHostRuntime::~EditorWindowHostRuntime() = default; @@ -174,6 +179,22 @@ EditorWindow* EditorWindowHostRuntime::CreateEditorWindow( return rawWindow; } +EditorWindow* EditorWindowHostRuntime::CreateWorkspaceWindow( + UIEditorWorkspaceController workspaceController, + const CreateParams& params) { + return CreateEditorWindow( + m_contentFactory.CreateWorkspaceContentController(std::move(workspaceController)), + params); +} + +EditorWindow* EditorWindowHostRuntime::CreateUtilityWindow( + const EditorUtilityWindowDescriptor& descriptor, + const CreateParams& params) { + return CreateEditorWindow( + m_contentFactory.CreateUtilityContentController(descriptor), + params); +} + void EditorWindowHostRuntime::BindLifecycleCoordinator( EditorWindowLifecycleCoordinator& lifecycleCoordinator) { m_lifecycleCoordinator = &lifecycleCoordinator; diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h index a7ded4f2..54ec99dc 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.h @@ -14,9 +14,11 @@ namespace XCEngine::UI::Editor::App { class EditorContext; class EditorWindow; class EditorWindowContentController; +class EditorWindowContentFactory; class EditorWindowLifecycleCoordinator; class EditorUtilityWindowCoordinator; class EditorWindowWorkspaceCoordinator; +struct EditorUtilityWindowDescriptor; class EditorWindowHostRuntime final { public: @@ -25,12 +27,19 @@ public: EditorWindowHostRuntime( EditorWindowHostConfig hostConfig, std::filesystem::path repoRoot, - EditorContext& editorContext); + EditorContext& editorContext, + EditorWindowContentFactory& contentFactory); ~EditorWindowHostRuntime(); EditorWindow* CreateEditorWindow( std::unique_ptr contentController, const CreateParams& params); + EditorWindow* CreateWorkspaceWindow( + UIEditorWorkspaceController workspaceController, + const CreateParams& params); + EditorWindow* CreateUtilityWindow( + const EditorUtilityWindowDescriptor& descriptor, + const CreateParams& params); void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator); void HandlePendingNativeWindowCreated(HWND hwnd); @@ -79,6 +88,7 @@ private: EditorWindowHostConfig m_hostConfig = {}; std::filesystem::path m_repoRoot = {}; EditorContext& m_editorContext; + EditorWindowContentFactory& m_contentFactory; std::vector> m_windows = {}; EditorWindow* m_pendingCreateWindow = nullptr; EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr; diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowManager.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowManager.cpp index 7cf57f53..dc859e4b 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowManager.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowManager.cpp @@ -1,6 +1,7 @@ #include "Platform/Win32/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" @@ -8,6 +9,8 @@ #include "Platform/Win32/Windowing/EditorWindowMessageDispatcher.h" #include "Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h" +#include + #include namespace XCEngine::UI::Editor::App { @@ -16,10 +19,12 @@ EditorWindowManager::EditorWindowManager( EditorWindowHostConfig hostConfig, std::filesystem::path repoRoot, EditorContext& editorContext) - : m_hostRuntime(std::make_unique( + : m_contentFactory(CreateDefaultEditorWindowContentFactory()) + , m_hostRuntime(std::make_unique( hostConfig, std::move(repoRoot), - editorContext)) { + editorContext, + *m_contentFactory)) { m_workspaceCoordinator = std::make_unique(*m_hostRuntime); m_utilityCoordinator = @@ -45,6 +50,27 @@ EditorWindow* EditorWindowManager::CreateEditorWindow( return window; } +EditorWindow* EditorWindowManager::CreateWorkspaceWindow( + UIEditorWorkspaceController workspaceController, + const CreateParams& params) { + EditorWindow* const window = + m_hostRuntime->CreateWorkspaceWindow(std::move(workspaceController), params); + if (window != nullptr) { + m_workspaceCoordinator->RegisterExistingWindow(*window); + } + return window; +} + +EditorWindow* EditorWindowManager::CreateUtilityWindow( + const EditorUtilityWindowDescriptor& descriptor, + const CreateParams& params) { + EditorWindow* const window = m_hostRuntime->CreateUtilityWindow(descriptor, params); + if (window != nullptr) { + m_workspaceCoordinator->RegisterExistingWindow(*window); + } + return window; +} + void EditorWindowManager::HandlePendingNativeWindowCreated(HWND hwnd) { m_hostRuntime->HandlePendingNativeWindowCreated(hwnd); } diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowManager.h b/editor/app/Platform/Win32/Windowing/EditorWindowManager.h index ffc966bd..0a2e6e82 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowManager.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindowManager.h @@ -16,8 +16,8 @@ namespace XCEngine::UI::Editor { -class UIEditorWindowWorkspaceController; class UIEditorWorkspaceController; +class UIEditorWindowWorkspaceController; struct UIEditorWindowWorkspaceSet; struct UIEditorWindowWorkspaceState; @@ -29,10 +29,12 @@ namespace XCEngine::UI::Editor::App { class EditorContext; class EditorWindow; class EditorWindowContentController; +class EditorWindowContentFactory; class EditorWindowHostRuntime; class EditorWindowLifecycleCoordinator; class EditorUtilityWindowCoordinator; class EditorWindowWorkspaceCoordinator; +struct EditorUtilityWindowDescriptor; struct EditorWindowPanelTransferRequest; struct EditorWindowFrameTransferRequests; @@ -75,6 +77,12 @@ public: EditorWindow* CreateEditorWindow( std::unique_ptr contentController, const CreateParams& params); + EditorWindow* CreateWorkspaceWindow( + UIEditorWorkspaceController workspaceController, + const CreateParams& params); + EditorWindow* CreateUtilityWindow( + const EditorUtilityWindowDescriptor& descriptor, + const CreateParams& params); void HandlePendingNativeWindowCreated(HWND hwnd); void Shutdown(); bool TryDispatchWindowMessage( @@ -96,6 +104,7 @@ public: void RenderAllWindows(); private: + std::unique_ptr m_contentFactory = {}; std::unique_ptr m_hostRuntime = {}; std::unique_ptr m_lifecycleCoordinator = {}; std::unique_ptr m_utilityCoordinator = {}; diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowSupport.h b/editor/app/Platform/Win32/Windowing/EditorWindowSupport.h index 563a753e..37ee127f 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowSupport.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindowSupport.h @@ -1,10 +1,8 @@ #pragma once -#include "Support/EnvironmentFlags.h" #include "Support/StringEncoding.h" #include "Support/TextFormat.h" - -#include +#include "Windowing/EditorWindowShared.h" #include @@ -18,14 +16,8 @@ namespace XCEngine::UI::Editor::App::EditorWindowSupport { inline constexpr unsigned int kDefaultDpi = 96u; inline constexpr float kBaseDpiScale = 96.0f; -inline constexpr float kBorderlessTitleBarHeightDips = 28.0f; inline constexpr float kBorderlessTitleBarFontSize = 12.0f; -inline const ::XCEngine::UI::UIColor kShellSurfaceColor(0.10f, 0.10f, 0.10f, 1.0f); -inline const ::XCEngine::UI::UIColor kShellBorderColor(0.15f, 0.15f, 0.15f, 1.0f); -inline const ::XCEngine::UI::UIColor kShellTextColor(0.92f, 0.92f, 0.92f, 1.0f); -inline const ::XCEngine::UI::UIColor kShellMutedTextColor(0.70f, 0.70f, 0.70f, 1.0f); - UINT QuerySystemDpi(); UINT QueryWindowDpi(HWND hwnd); bool ResolveVerboseRuntimeTraceEnabled(); diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp index 4d439e1c..e9ff683b 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp @@ -3,10 +3,9 @@ #include "Composition/EditorContext.h" #include "Platform/Win32/Windowing/EditorWindow.h" #include "Platform/Win32/Windowing/EditorFloatingWindowPlacement.h" -#include "Windowing/Content/EditorWorkspaceWindowContentController.h" -#include "Platform/Win32/Runtime/EditorWindowRuntimeController.h" #include "Platform/Win32/Windowing/EditorWindowHostRuntime.h" #include "Platform/Win32/Windowing/EditorWindowLifecycleCoordinator.h" +#include "Windowing/Content/EditorWindowContentController.h" #include #include @@ -42,6 +41,13 @@ POINT BuildFallbackGlobalTabDragHotspot() { return hotspot; } +POINT ToNativePoint(const EditorWindowScreenPoint& point) { + return POINT{ + static_cast(point.x), + static_cast(point.y), + }; +} + bool CanStartGlobalTabDragFromWindow( const EditorWindow& sourceWindow, std::string_view sourceNodeId, @@ -83,49 +89,6 @@ std::string DescribeWindowSetState(const UIEditorWindowWorkspaceSet& windowSet) return stream.str(); } -UIEditorWindowWorkspaceSet BuildLiveWindowWorkspaceSet( - const EditorWindowHostRuntime& hostRuntime, - std::string_view excludedWindowId = {}, - bool includeClosingWindows = false) { - UIEditorWindowWorkspaceSet windowSet = {}; - - for (const std::unique_ptr& window : hostRuntime.GetWindows()) { - if (window == nullptr || - window->GetHwnd() == nullptr || - !window->IsWorkspaceWindow() || - window->GetWindowId() == excludedWindowId || - (!includeClosingWindows && - window->GetLifecycleState() != EditorWindowLifecycleState::Running)) { - continue; - } - - UIEditorWindowWorkspaceState state = {}; - state.windowId = std::string(window->GetWindowId()); - const UIEditorWorkspaceController* const workspaceController = - window->TryGetWorkspaceController(); - if (workspaceController == nullptr) { - continue; - } - state.workspace = workspaceController->GetWorkspace(); - state.session = workspaceController->GetSession(); - if (window->IsPrimary()) { - windowSet.primaryWindowId = state.windowId; - } - windowSet.windows.push_back(std::move(state)); - } - - if (windowSet.activeWindowId.empty()) { - if (!windowSet.primaryWindowId.empty() && - FindUIEditorWindowWorkspaceState(windowSet, windowSet.primaryWindowId) != nullptr) { - windowSet.activeWindowId = windowSet.primaryWindowId; - } else if (!windowSet.windows.empty()) { - windowSet.activeWindowId = windowSet.windows.front().windowId; - } - } - - return windowSet; -} - } // namespace EditorWindowWorkspaceCoordinator::EditorWindowWorkspaceCoordinator( @@ -141,26 +104,24 @@ void EditorWindowWorkspaceCoordinator::BindLifecycleCoordinator( } void EditorWindowWorkspaceCoordinator::RegisterExistingWindow(EditorWindow& window) { + UpdateWindowProjection(window); RefreshWindowTitle(window); } -void EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(EditorWindow& window) const { +void EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(EditorWindow& window) { if (!window.IsWorkspaceWindow()) { return; } + UpdateWindowProjection(window); RefreshWindowTitle(window); } bool EditorWindowWorkspaceCoordinator::IsPrimaryWindowId(std::string_view windowId) const { - if (const EditorWindow* window = m_hostRuntime.FindWindow(windowId); window != nullptr) { - return window->IsWorkspaceWindow() && window->IsPrimary(); - } - - return false; + return m_workspaceStore.IsPrimaryWindowId(windowId); } std::string EditorWindowWorkspaceCoordinator::DescribeWindowSet() const { - return DescribeWindowSetState(BuildLiveWindowWorkspaceSet(m_hostRuntime, {}, true)); + return DescribeWindowSetState(m_workspaceStore.GetWindowSet()); } void EditorWindowWorkspaceCoordinator::RemoveWindowProjection( @@ -172,21 +133,50 @@ void EditorWindowWorkspaceCoordinator::RemoveWindowProjection( "' primaryArg=" + (primary ? "1" : "0") + " stateBefore=" + DescribeWindowSet()); - const UIEditorWindowWorkspaceSet stateAfter = - primary - ? UIEditorWindowWorkspaceSet{} - : BuildLiveWindowWorkspaceSet(m_hostRuntime, windowId, true); + m_workspaceStore.RemoveWindowState(windowId, primary); LogRuntimeTrace( "window-close", "workspace remove end windowId='" + std::string(windowId) + - "' stateAfter=" + DescribeWindowSetState(stateAfter)); + "' stateAfter=" + DescribeWindowSet()); } UIEditorWindowWorkspaceController EditorWindowWorkspaceCoordinator::BuildWorkspaceMutationController() const { return UIEditorWindowWorkspaceController( m_workspaceStore.GetPanelRegistry(), - BuildLiveWindowWorkspaceSet(m_hostRuntime)); + m_workspaceStore.GetWindowSet()); +} + +UIEditorWindowWorkspaceState EditorWindowWorkspaceCoordinator::BuildWindowStateForWindow( + const EditorWindow& window) const { + UIEditorWindowWorkspaceState state = {}; + state.windowId = std::string(window.GetWindowId()); + const UIEditorWorkspaceController* const workspaceController = + window.TryGetWorkspaceController(); + if (workspaceController != nullptr) { + state.workspace = workspaceController->GetWorkspace(); + state.session = workspaceController->GetSession(); + } + return state; +} + +void EditorWindowWorkspaceCoordinator::UpdateWindowProjection(EditorWindow& window) { + if (!window.IsWorkspaceWindow() || + window.GetHwnd() == nullptr || + window.TryGetWorkspaceController() == nullptr) { + return; + } + + std::string error = {}; + if (!m_workspaceStore.UpsertWindowState( + BuildWindowStateForWindow(window), + window.IsPrimary(), + error)) { + LogRuntimeTrace( + "window", + "workspace projection update rejected for window '" + + std::string(window.GetWindowId()) + "': " + error); + } } UIEditorWorkspaceController EditorWindowWorkspaceCoordinator::BuildWorkspaceControllerForWindow( @@ -343,9 +333,8 @@ bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet( createParams.initialHeight = detachedRect.bottom - detachedRect.top; } - if (m_hostRuntime.CreateEditorWindow( - CreateEditorWorkspaceWindowContentController( - BuildWorkspaceControllerForWindow(entry)), + if (m_hostRuntime.CreateWorkspaceWindow( + BuildWorkspaceControllerForWindow(entry), createParams) == nullptr) { for (const ExistingWindowSnapshot& snapshot : existingWindowSnapshots) { restoreWindowSnapshot(snapshot); @@ -379,6 +368,14 @@ bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet( } } + std::string storeError = {}; + if (!m_workspaceStore.TrySetWindowSet(windowSet, storeError)) { + LogRuntimeTrace( + "window", + "workspace synchronization produced invalid stored state: " + storeError); + return false; + } + return true; } @@ -485,7 +482,7 @@ void EditorWindowWorkspaceCoordinator::ClearGlobalTabDragDropPreview() { if (EditorWindow* previewWindow = m_hostRuntime.FindWindow(m_globalTabDragSession.previewWindowId); IsLiveInteractiveWindow(previewWindow)) { if (EditorWindowDockHostBinding* dockHostBinding = - previewWindow->m_runtime->TryGetDockHostBinding(); + previewWindow->TryGetDockHostBinding(); dockHostBinding != nullptr) { dockHostBinding->ClearExternalDockHostDropPreview(); previewWindow->InvalidateHostWindow(); @@ -528,7 +525,7 @@ void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragDropPreview() { preview.placement = dropTarget.placement; preview.insertionIndex = dropTarget.insertionIndex; if (EditorWindowDockHostBinding* dockHostBinding = - targetWindow->m_runtime->TryGetDockHostBinding(); + targetWindow->TryGetDockHostBinding(); dockHostBinding != nullptr) { dockHostBinding->SetExternalDockHostDropPreview(preview); targetWindow->InvalidateHostWindow(); @@ -671,7 +668,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( sourceWindow, request.nodeId, request.panelId, - request.screenPoint, + ToNativePoint(request.screenPoint), dragHotspot); const auto tryStartDetachedPanelGlobalDrag = @@ -681,7 +678,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( if (!CommitWindowWorkspaceMutation( windowWorkspaceController, result.targetWindowId, - request.screenPoint)) { + ToNativePoint(request.screenPoint))) { LogRuntimeTrace("drag", "failed to synchronize detached drag window state"); return false; } @@ -696,7 +693,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( detachedWindow->GetWindowId(), detachedWindow->GetWorkspaceController().GetWorkspace().root.nodeId, request.panelId, - request.screenPoint, + ToNativePoint(request.screenPoint), dragHotspot); UpdateGlobalTabDragOwnerWindowPosition(); detachedWindow->AcquirePointerCapture( @@ -745,7 +742,7 @@ bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( sourceWindow.GetWindowId(), request.nodeId, request.panelId, - request.screenPoint, + ToNativePoint(request.screenPoint), dragHotspot); UpdateGlobalTabDragOwnerWindowPosition(); sourceWindow.AcquirePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag); @@ -788,7 +785,7 @@ bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest( if (!CommitWindowWorkspaceMutation( windowWorkspaceController, result.targetWindowId, - request.screenPoint)) { + ToNativePoint(request.screenPoint))) { LogRuntimeTrace("detach", "failed to synchronize detached window state"); return false; } @@ -808,6 +805,10 @@ bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest( void EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests( EditorWindow& sourceWindow, const EditorWindowFrameTransferRequests& transferRequests) { + if (sourceWindow.IsWorkspaceWindow()) { + UpdateWindowProjection(sourceWindow); + } + if (!m_globalTabDragSession.active && transferRequests.workspace.beginGlobalTabDrag.has_value() && transferRequests.workspace.beginGlobalTabDrag->IsValid()) { diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h index d35cb9eb..28f67379 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h @@ -27,7 +27,7 @@ public: void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator); void RegisterExistingWindow(EditorWindow& window); - void RefreshWindowPresentation(EditorWindow& window) const; + void RefreshWindowPresentation(EditorWindow& window); bool IsPrimaryWindowId(std::string_view windowId) const; std::string DescribeWindowSet() const; void RemoveWindowProjection(std::string_view windowId, bool primary); @@ -65,6 +65,8 @@ private: const POINT& preferredScreenPoint, LONG preferredWidth = 0, LONG preferredHeight = 0); + UIEditorWindowWorkspaceState BuildWindowStateForWindow(const EditorWindow& window) const; + void UpdateWindowProjection(EditorWindow& window); UIEditorWorkspaceController BuildWorkspaceControllerForWindow( const UIEditorWindowWorkspaceState& windowState) const; std::wstring BuildWindowTitle( diff --git a/editor/app/Windowing/Content/EditorWindowContentController.h b/editor/app/Windowing/Content/EditorWindowContentController.h index b3c229f1..d95cde47 100644 --- a/editor/app/Windowing/Content/EditorWindowContentController.h +++ b/editor/app/Windowing/Content/EditorWindowContentController.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -124,6 +125,7 @@ struct EditorWindowContentFrameContext { EditorContext& editorContext; const ::XCEngine::UI::UIRect& bounds; const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents; + std::optional cursorScreenPoint = {}; std::string_view captureStatusText; bool primary = false; bool globalTabDragActive = false; diff --git a/editor/app/Windowing/Content/EditorWindowContentFactory.cpp b/editor/app/Windowing/Content/EditorWindowContentFactory.cpp new file mode 100644 index 00000000..e294e41f --- /dev/null +++ b/editor/app/Windowing/Content/EditorWindowContentFactory.cpp @@ -0,0 +1,31 @@ +#include "Windowing/Content/EditorWindowContentFactory.h" + +#include "Windowing/Content/EditorUtilityWindowContentController.h" +#include "Windowing/Content/EditorWorkspaceWindowContentController.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +class DefaultEditorWindowContentFactory final : public EditorWindowContentFactory { +public: + std::unique_ptr CreateWorkspaceContentController( + UIEditorWorkspaceController workspaceController) const override { + return CreateEditorWorkspaceWindowContentController(std::move(workspaceController)); + } + + std::unique_ptr CreateUtilityContentController( + const EditorUtilityWindowDescriptor& descriptor) const override { + return CreateEditorUtilityWindowContentController(descriptor); + } +}; + +} // namespace + +std::unique_ptr CreateDefaultEditorWindowContentFactory() { + return std::make_unique(); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWindowContentFactory.h b/editor/app/Windowing/Content/EditorWindowContentFactory.h new file mode 100644 index 00000000..866dde40 --- /dev/null +++ b/editor/app/Windowing/Content/EditorWindowContentFactory.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace XCEngine::UI::Editor { + +class UIEditorWorkspaceController; + +} // namespace XCEngine::UI::Editor + +namespace XCEngine::UI::Editor::App { + +class EditorWindowContentController; +struct EditorUtilityWindowDescriptor; + +class EditorWindowContentFactory { +public: + virtual ~EditorWindowContentFactory() = default; + + virtual std::unique_ptr CreateWorkspaceContentController( + UIEditorWorkspaceController workspaceController) const = 0; + virtual std::unique_ptr CreateUtilityContentController( + const EditorUtilityWindowDescriptor& descriptor) const = 0; +}; + +std::unique_ptr CreateDefaultEditorWindowContentFactory(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp index c2233224..2b8af5dd 100644 --- a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp +++ b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp @@ -114,6 +114,7 @@ EditorWindowFrameTransferRequests EditorWorkspaceWindowContentController::Update m_shellRuntime, context.bounds, context.inputEvents, + context.cursorScreenPoint, context.captureStatusText, context.primary, context.globalTabDragActive, diff --git a/editor/app/Windowing/EditorWindowShared.h b/editor/app/Windowing/EditorWindowShared.h new file mode 100644 index 00000000..c2dc640c --- /dev/null +++ b/editor/app/Windowing/EditorWindowShared.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Support/EnvironmentFlags.h" + +#include + +#include + +namespace XCEngine::UI::Editor::App { + +struct EditorWindowScreenPoint { + std::int32_t x = 0; + std::int32_t y = 0; +}; + +inline constexpr float kBorderlessTitleBarHeightDips = 28.0f; + +inline const ::XCEngine::UI::UIColor kShellSurfaceColor(0.10f, 0.10f, 0.10f, 1.0f); +inline const ::XCEngine::UI::UIColor kShellBorderColor(0.15f, 0.15f, 0.15f, 1.0f); +inline const ::XCEngine::UI::UIColor kShellTextColor(0.92f, 0.92f, 0.92f, 1.0f); +inline const ::XCEngine::UI::UIColor kShellMutedTextColor(0.70f, 0.70f, 0.70f, 1.0f); + +inline bool IsEditorWindowVerboseRuntimeTraceEnabled() { + return IsEnvironmentFlagEnabled("XCUIEDITOR_VERBOSE_TRACE"); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp b/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp index 47148c5f..e9078303 100644 --- a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp +++ b/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp @@ -3,8 +3,9 @@ #include "Composition/EditorContext.h" #include "Composition/EditorShellRuntime.h" #include "Composition/EditorShellVariant.h" -#include "Platform/Win32/Windowing/EditorWindowSupport.h" +#include "Windowing/EditorWindowShared.h" +#include #include #include @@ -12,7 +13,6 @@ namespace XCEngine::UI::Editor::App { -using namespace EditorWindowSupport; using ::XCEngine::UI::UIDrawData; using ::XCEngine::UI::UIDrawList; using ::XCEngine::UI::UIInputEvent; @@ -22,7 +22,7 @@ using ::XCEngine::UI::UIPoint; namespace { bool IsVerboseRuntimeTraceEnabled() { - static const bool s_enabled = ResolveVerboseRuntimeTraceEnabled(); + static const bool s_enabled = IsEditorWindowVerboseRuntimeTraceEnabled(); return s_enabled; } @@ -51,6 +51,7 @@ EditorWindowFrameTransferRequests EditorWindowFrameOrchestrator::UpdateAndAppend EditorShellRuntime& shellRuntime, const ::XCEngine::UI::UIRect& workspaceBounds, const std::vector& frameEvents, + const std::optional& cursorScreenPoint, std::string_view captureStatusText, bool primary, bool globalTabDragActive, @@ -78,22 +79,25 @@ EditorWindowFrameTransferRequests EditorWindowFrameOrchestrator::UpdateAndAppend LogFrameInteractionTrace(workspaceController, frameEvents, shellFrame); EditorWindowFrameTransferRequests transferRequests = - BuildShellTransferRequests(globalTabDragActive, dockHostInteractionState, shellFrame); - POINT screenPoint = {}; + BuildShellTransferRequests( + globalTabDragActive, + cursorScreenPoint, + dockHostInteractionState, + shellFrame); if (const std::optional requestedKind = editorContext.ConsumeOpenUtilityWindowRequest(); requestedKind.has_value()) { transferRequests.utility.openUtilityWindow = EditorWindowOpenUtilityWindowRequest{ .kind = *requestedKind, - .useCursorPlacement = GetCursorPos(&screenPoint) != FALSE, + .useCursorPlacement = cursorScreenPoint.has_value(), }; if (transferRequests.utility.openUtilityWindow->useCursorPlacement) { - transferRequests.utility.openUtilityWindow->screenPoint = screenPoint; + transferRequests.utility.openUtilityWindow->screenPoint = *cursorScreenPoint; } } for (const WorkspaceTraceEntry& entry : shellRuntime.GetTraceEntries()) { - LogRuntimeTrace(entry.channel, entry.message); + AppendUIEditorRuntimeTrace(entry.channel, entry.message); } shellRuntime.Append(drawData); @@ -146,7 +150,7 @@ void EditorWindowFrameOrchestrator::LogInputTrace( return; } - LogRuntimeTrace( + AppendUIEditorRuntimeTrace( "input", DescribeInputEvents(frameEvents) + " | " + editorContext.DescribeWorkspaceState( @@ -178,33 +182,34 @@ void EditorWindowFrameOrchestrator::LogFrameInteractionTrace( << workspaceController.GetWorkspace().activePanelId << " message=" << shellFrame.result.workspaceResult.dockHostResult.layoutResult.message; - LogRuntimeTrace("frame", frameTrace.str()); + AppendUIEditorRuntimeTrace("frame", frameTrace.str()); } EditorWindowFrameTransferRequests EditorWindowFrameOrchestrator::BuildShellTransferRequests( bool globalTabDragActive, + const std::optional& cursorScreenPoint, const UIEditorDockHostInteractionState& dockHostInteractionState, const UIEditorShellInteractionFrame& shellFrame) const { EditorWindowFrameTransferRequests transferRequests = {}; - POINT screenPoint = {}; - const bool hasScreenPoint = GetCursorPos(&screenPoint) != FALSE; + if (!cursorScreenPoint.has_value()) { + return transferRequests; + } if (!globalTabDragActive && !dockHostInteractionState.activeTabDragNodeId.empty() && - !dockHostInteractionState.activeTabDragPanelId.empty() && - hasScreenPoint) { + !dockHostInteractionState.activeTabDragPanelId.empty()) { transferRequests.workspace.beginGlobalTabDrag = EditorWindowPanelTransferRequest{ dockHostInteractionState.activeTabDragNodeId, dockHostInteractionState.activeTabDragPanelId, - screenPoint, + *cursorScreenPoint, }; } - if (shellFrame.result.workspaceResult.dockHostResult.detachRequested && hasScreenPoint) { + if (shellFrame.result.workspaceResult.dockHostResult.detachRequested) { transferRequests.workspace.detachPanel = EditorWindowPanelTransferRequest{ shellFrame.result.workspaceResult.dockHostResult.detachedNodeId, shellFrame.result.workspaceResult.dockHostResult.detachedPanelId, - screenPoint, + *cursorScreenPoint, }; } diff --git a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h b/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h index b23c7d55..d13095b9 100644 --- a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h +++ b/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -49,6 +50,7 @@ public: EditorShellRuntime& shellRuntime, const ::XCEngine::UI::UIRect& workspaceBounds, const std::vector<::XCEngine::UI::UIInputEvent>& frameEvents, + const std::optional& cursorScreenPoint, std::string_view captureStatusText, bool primary, bool globalTabDragActive, @@ -72,6 +74,7 @@ private: const UIEditorShellInteractionFrame& shellFrame) const; EditorWindowFrameTransferRequests BuildShellTransferRequests( bool globalTabDragActive, + const std::optional& cursorScreenPoint, const UIEditorDockHostInteractionState& dockHostInteractionState, const UIEditorShellInteractionFrame& shellFrame) const; }; diff --git a/editor/app/Windowing/Frame/EditorWindowTransferRequests.h b/editor/app/Windowing/Frame/EditorWindowTransferRequests.h index 29f55d4b..8afa0cce 100644 --- a/editor/app/Windowing/Frame/EditorWindowTransferRequests.h +++ b/editor/app/Windowing/Frame/EditorWindowTransferRequests.h @@ -4,10 +4,9 @@ #define NOMINMAX #endif +#include "Windowing/EditorWindowShared.h" #include "UtilityWindows/EditorUtilityWindowKind.h" -#include - #include #include @@ -16,7 +15,7 @@ namespace XCEngine::UI::Editor::App { struct EditorWindowPanelTransferRequest { std::string nodeId = {}; std::string panelId = {}; - POINT screenPoint = {}; + EditorWindowScreenPoint screenPoint = {}; bool IsValid() const { return !nodeId.empty() && !panelId.empty(); @@ -25,7 +24,7 @@ struct EditorWindowPanelTransferRequest { struct EditorWindowOpenUtilityWindowRequest { EditorUtilityWindowKind kind = EditorUtilityWindowKind::None; - POINT screenPoint = {}; + EditorWindowScreenPoint screenPoint = {}; bool useCursorPlacement = false; bool IsValid() const {