From c62a7fec0aad7a95fbe769b82efcccaa0e14c131 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 26 Apr 2026 00:25:49 +0800 Subject: [PATCH] Use explicit workspace mutation requests for window sync --- .../Windowing/EditorWindowHostRuntime.cpp | 4 +- .../EditorWindowWorkspaceCoordinator.cpp | 18 ++--- .../EditorWindowWorkspaceCoordinator.h | 2 +- .../Content/EditorWindowContentFactory.cpp | 5 +- .../Content/EditorWindowContentFactory.h | 1 + ...EditorWorkspaceWindowContentController.cpp | 7 +- .../EditorWorkspaceWindowContentController.h | 6 +- .../Windowing/System/EditorWindowSystem.cpp | 30 ++++++-- .../app/Windowing/System/EditorWindowSystem.h | 6 ++ ..._editor_window_synchronization_planner.cpp | 74 +++++++++++++++++++ 10 files changed, 130 insertions(+), 23 deletions(-) diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp index e1bbb607..53bb121f 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.cpp @@ -183,7 +183,9 @@ EditorWindow* EditorWindowHostRuntime::CreateWorkspaceWindow( UIEditorWorkspaceController workspaceController, const CreateParams& params) { return CreateEditorWindow( - m_contentFactory.CreateWorkspaceContentController(std::move(workspaceController)), + m_contentFactory.CreateWorkspaceContentController( + params.windowId, + std::move(workspaceController)), params); } diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp index ec53cbfb..60d99103 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp +++ b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.cpp @@ -169,21 +169,15 @@ UIEditorWindowWorkspaceState EditorWindowWorkspaceCoordinator::BuildWindowStateF return state; } -bool EditorWindowWorkspaceCoordinator::CommitLiveWindowMutation(EditorWindow& window) { - if (!window.IsWorkspaceWindow() || - window.GetHwnd() == nullptr || - window.TryGetWorkspaceController() == nullptr) { - return true; - } - +bool EditorWindowWorkspaceCoordinator::CommitLiveWindowMutation( + const EditorWindowWorkspaceMutationRequest& request) { const std::wstring_view primaryWindowTitle = m_hostRuntime.GetHostConfig().primaryWindowTitle != nullptr ? std::wstring_view(m_hostRuntime.GetHostConfig().primaryWindowTitle) : std::wstring_view{}; std::string error = {}; - EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForLiveWindowMutation( - window.GetWindowId(), - window.GetWorkspaceController(), + EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForWorkspaceMutationRequest( + request, CaptureHostSnapshots(), primaryWindowTitle, error); @@ -191,7 +185,7 @@ bool EditorWindowWorkspaceCoordinator::CommitLiveWindowMutation(EditorWindow& wi LogRuntimeTrace( "window", "workspace live mutation rejected for window '" + - std::string(window.GetWindowId()) + "': " + error); + request.windowState.windowId + "': " + error); return false; } @@ -825,7 +819,7 @@ void EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests( EditorWindow& sourceWindow, const EditorWindowFrameTransferRequests& transferRequests) { if (transferRequests.workspace.workspaceMutation.has_value()) { - if (!CommitLiveWindowMutation(sourceWindow)) { + if (!CommitLiveWindowMutation(*transferRequests.workspace.workspaceMutation)) { LogRuntimeTrace( "window", "failed to commit live workspace mutation for window '" + diff --git a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h index d5a26098..84e520c1 100644 --- a/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h +++ b/editor/app/Platform/Win32/Windowing/EditorWindowWorkspaceCoordinator.h @@ -74,7 +74,7 @@ private: LONG preferredWidth = 0, LONG preferredHeight = 0); UIEditorWindowWorkspaceState BuildWindowStateForWindow(const EditorWindow& window) const; - bool CommitLiveWindowMutation(EditorWindow& window); + bool CommitLiveWindowMutation(const EditorWindowWorkspaceMutationRequest& request); void RefreshWindowTitle(EditorWindow& window) const; void BeginGlobalTabDragSession( std::string_view panelWindowId, diff --git a/editor/app/Windowing/Content/EditorWindowContentFactory.cpp b/editor/app/Windowing/Content/EditorWindowContentFactory.cpp index e294e41f..5ef277c3 100644 --- a/editor/app/Windowing/Content/EditorWindowContentFactory.cpp +++ b/editor/app/Windowing/Content/EditorWindowContentFactory.cpp @@ -12,8 +12,11 @@ namespace { class DefaultEditorWindowContentFactory final : public EditorWindowContentFactory { public: std::unique_ptr CreateWorkspaceContentController( + std::string_view windowId, UIEditorWorkspaceController workspaceController) const override { - return CreateEditorWorkspaceWindowContentController(std::move(workspaceController)); + return CreateEditorWorkspaceWindowContentController( + windowId, + std::move(workspaceController)); } std::unique_ptr CreateUtilityContentController( diff --git a/editor/app/Windowing/Content/EditorWindowContentFactory.h b/editor/app/Windowing/Content/EditorWindowContentFactory.h index 866dde40..b5998c2a 100644 --- a/editor/app/Windowing/Content/EditorWindowContentFactory.h +++ b/editor/app/Windowing/Content/EditorWindowContentFactory.h @@ -18,6 +18,7 @@ public: virtual ~EditorWindowContentFactory() = default; virtual std::unique_ptr CreateWorkspaceContentController( + std::string_view windowId, UIEditorWorkspaceController workspaceController) const = 0; virtual std::unique_ptr CreateUtilityContentController( const EditorUtilityWindowDescriptor& descriptor) const = 0; diff --git a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp index cc2cbbca..f1b2e772 100644 --- a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp +++ b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp @@ -33,8 +33,10 @@ EditorWindowContentCursorKind ToContentCursorKind( } // namespace EditorWorkspaceWindowContentController::EditorWorkspaceWindowContentController( + std::string windowId, UIEditorWorkspaceController workspaceController) - : m_workspaceController(std::move(workspaceController)) {} + : m_windowId(std::move(windowId)), + m_workspaceController(std::move(workspaceController)) {} EditorWorkspaceWindowContentController::~EditorWorkspaceWindowContentController() = default; @@ -131,6 +133,7 @@ EditorWindowFrameTransferRequests EditorWorkspaceWindowContentController::Update transferRequests.workspace.workspaceMutation = EditorWindowWorkspaceMutationRequest{ .windowState = UIEditorWindowWorkspaceState{ + .windowId = m_windowId, .workspace = m_workspaceController.GetWorkspace(), .session = m_workspaceController.GetSession(), }, @@ -220,8 +223,10 @@ std::string EditorWorkspaceWindowContentController::ResolveDetachedWindowTitleTe } std::unique_ptr CreateEditorWorkspaceWindowContentController( + std::string_view windowId, UIEditorWorkspaceController workspaceController) { return std::make_unique( + std::string(windowId), std::move(workspaceController)); } diff --git a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h index 5cd0c388..3bf758a4 100644 --- a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h +++ b/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h @@ -17,7 +17,9 @@ class EditorWorkspaceWindowContentController final , public EditorWindowInputFeedbackBinding , public EditorWindowTitleBarBinding { public: - explicit EditorWorkspaceWindowContentController(UIEditorWorkspaceController workspaceController); + EditorWorkspaceWindowContentController( + std::string windowId, + UIEditorWorkspaceController workspaceController); ~EditorWorkspaceWindowContentController() override; EditorWindowContentCapabilities GetCapabilities() const override; @@ -69,12 +71,14 @@ public: std::string_view fallbackWindowTitle) const override; private: + std::string m_windowId = {}; UIEditorWorkspaceController m_workspaceController = {}; EditorShellRuntime m_shellRuntime = {}; EditorWindowFrameOrchestrator m_frameOrchestrator = {}; }; std::unique_ptr CreateEditorWorkspaceWindowContentController( + std::string_view windowId, UIEditorWorkspaceController workspaceController); } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/System/EditorWindowSystem.cpp b/editor/app/Windowing/System/EditorWindowSystem.cpp index 11b9d7a2..17479d0d 100644 --- a/editor/app/Windowing/System/EditorWindowSystem.cpp +++ b/editor/app/Windowing/System/EditorWindowSystem.cpp @@ -78,21 +78,39 @@ EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForLiveWindowMutati const std::vector& hostWindows, std::wstring_view primaryWindowTitle, std::string& outError) const { - if (windowId.empty()) { - outError = "live window mutation missing window id"; + EditorWindowWorkspaceMutationRequest request = {}; + request.windowState.windowId = std::string(windowId); + request.windowState.workspace = workspaceController.GetWorkspace(); + request.windowState.session = workspaceController.GetSession(); + return BuildPlanForWorkspaceMutationRequest( + request, + hostWindows, + primaryWindowTitle, + outError); +} + +EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForWorkspaceMutationRequest( + const EditorWindowWorkspaceMutationRequest& request, + const std::vector& hostWindows, + std::wstring_view primaryWindowTitle, + std::string& outError) const { + if (!request.IsValid()) { + outError = "live window mutation request missing window id"; return {}; } UIEditorWindowWorkspaceSet nextWindowSet = GetWindowSet(); UIEditorWindowWorkspaceState* existingState = - FindMutableUIEditorWindowWorkspaceState(nextWindowSet, windowId); + FindMutableUIEditorWindowWorkspaceState(nextWindowSet, request.windowState.windowId); if (existingState == nullptr) { - outError = "live window mutation references unknown window '" + std::string(windowId) + "'"; + outError = + "live window mutation references unknown window '" + + request.windowState.windowId + "'"; return {}; } - existingState->workspace = workspaceController.GetWorkspace(); - existingState->session = workspaceController.GetSession(); + existingState->workspace = request.windowState.workspace; + existingState->session = request.windowState.session; return BuildPlanForWindowSet( nextWindowSet, hostWindows, diff --git a/editor/app/Windowing/System/EditorWindowSystem.h b/editor/app/Windowing/System/EditorWindowSystem.h index 4596daf3..c1ec9ac2 100644 --- a/editor/app/Windowing/System/EditorWindowSystem.h +++ b/editor/app/Windowing/System/EditorWindowSystem.h @@ -1,6 +1,7 @@ #pragma once #include "Composition/EditorWindowWorkspaceStore.h" +#include "Windowing/Frame/EditorWindowTransferRequests.h" #include "Windowing/System/EditorWindowSynchronizationPlan.h" #include @@ -41,6 +42,11 @@ public: const std::vector& hostWindows, std::wstring_view primaryWindowTitle, std::string& outError) const; + EditorWindowSynchronizationPlan BuildPlanForWorkspaceMutationRequest( + const EditorWindowWorkspaceMutationRequest& request, + const std::vector& hostWindows, + std::wstring_view primaryWindowTitle, + std::string& outError) const; EditorWindowSynchronizationPlan BuildPlanForDestroyedWindow( std::string_view windowId, const std::vector& hostWindows, diff --git a/tests/UI/Editor/unit/test_editor_window_synchronization_planner.cpp b/tests/UI/Editor/unit/test_editor_window_synchronization_planner.cpp index d182f87d..ad14d167 100644 --- a/tests/UI/Editor/unit/test_editor_window_synchronization_planner.cpp +++ b/tests/UI/Editor/unit/test_editor_window_synchronization_planner.cpp @@ -1,5 +1,6 @@ #include +#include "Windowing/Frame/EditorWindowTransferRequests.h" #include "Windowing/System/EditorWindowSystem.h" #include @@ -13,6 +14,7 @@ using XCEngine::UI::Editor::App::EditorWindowHostSnapshot; using XCEngine::UI::Editor::App::EditorWindowSynchronizationActionKind; using XCEngine::UI::Editor::App::EditorWindowSynchronizationPlannerInput; using XCEngine::UI::Editor::App::EditorWindowSystem; +using XCEngine::UI::Editor::App::EditorWindowWorkspaceMutationRequest; using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceController; using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel; using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack; @@ -319,6 +321,78 @@ TEST(EditorWindowSynchronizationPlannerTest, LiveWindowMutationBuildsCommitPlanW EXPECT_EQ(system.GetWindowSet().windows.front().workspace.activePanelId, "doc-b"); } +TEST(EditorWindowSynchronizationPlannerTest, WorkspaceMutationRequestBuildsCommitPlanFromExplicitPayload) { + EditorWindowSystem system = BuildSystem(); + const auto workspaceController = + BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace()); + std::string error = {}; + ASSERT_TRUE(system.BootstrapPrimaryWindow("main", workspaceController, error)) << error; + + auto liveController = workspaceController; + const auto commandResult = liveController.Dispatch( + UIEditorWorkspaceCommand{ + .kind = UIEditorWorkspaceCommandKind::ActivatePanel, + .panelId = "doc-b", + }); + ASSERT_EQ(commandResult.status, UIEditorWorkspaceCommandStatus::Changed); + + EditorWindowWorkspaceMutationRequest request = {}; + request.windowState.windowId = "main"; + request.windowState.workspace = liveController.GetWorkspace(); + request.windowState.session = liveController.GetSession(); + + auto plan = system.BuildPlanForWorkspaceMutationRequest( + request, + { + BuildWorkspaceSnapshot( + "main", + true, + workspaceController, + L"Main Scene - XCEngine Editor"), + }, + L"Main Scene - XCEngine Editor", + error); + + ASSERT_TRUE(plan.valid) << error; + ASSERT_EQ(plan.actions.size(), 1u); + EXPECT_EQ(plan.actions[0].kind, EditorWindowSynchronizationActionKind::UpdateWorkspaceWindow); + EXPECT_EQ(plan.actions[0].update.windowState.windowId, "main"); + const auto* mutatedState = FindUIEditorWindowWorkspaceState(plan.targetWindowSet, "main"); + ASSERT_NE(mutatedState, nullptr); + EXPECT_EQ(mutatedState->workspace.activePanelId, "doc-b"); + + ASSERT_TRUE(system.CommitSynchronizationPlan(plan, error)) << error; + EXPECT_EQ(system.GetWindowSet().windows.front().workspace.activePanelId, "doc-b"); +} + +TEST(EditorWindowSynchronizationPlannerTest, WorkspaceMutationRequestRejectsUnknownWindowId) { + EditorWindowSystem system = BuildSystem(); + const auto workspaceController = + BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace()); + std::string error = {}; + ASSERT_TRUE(system.BootstrapPrimaryWindow("main", workspaceController, error)) << error; + + EditorWindowWorkspaceMutationRequest request = {}; + request.windowState.windowId = "missing-window"; + request.windowState.workspace = workspaceController.GetWorkspace(); + request.windowState.session = workspaceController.GetSession(); + + auto plan = system.BuildPlanForWorkspaceMutationRequest( + request, + { + BuildWorkspaceSnapshot( + "main", + true, + workspaceController, + L"Main Scene - XCEngine Editor"), + }, + L"Main Scene - XCEngine Editor", + error); + + EXPECT_FALSE(plan.valid); + EXPECT_NE(error.find("missing-window"), std::string::npos); +} + TEST(EditorWindowSynchronizationPlannerTest, DestroyedPrimaryWindowProducesCloseActionsForRemainingDetachedWindows) { EditorWindowSystem system = BuildSystem(); const auto workspaceController =