Use explicit workspace mutation requests for window sync
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 '" +
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -12,8 +12,11 @@ namespace {
|
||||
class DefaultEditorWindowContentFactory final : public EditorWindowContentFactory {
|
||||
public:
|
||||
std::unique_ptr<EditorWindowContentController> CreateWorkspaceContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController) const override {
|
||||
return CreateEditorWorkspaceWindowContentController(std::move(workspaceController));
|
||||
return CreateEditorWorkspaceWindowContentController(
|
||||
windowId,
|
||||
std::move(workspaceController));
|
||||
}
|
||||
|
||||
std::unique_ptr<EditorWindowContentController> CreateUtilityContentController(
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
virtual ~EditorWindowContentFactory() = default;
|
||||
|
||||
virtual std::unique_ptr<EditorWindowContentController> CreateWorkspaceContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController) const = 0;
|
||||
virtual std::unique_ptr<EditorWindowContentController> CreateUtilityContentController(
|
||||
const EditorUtilityWindowDescriptor& descriptor) const = 0;
|
||||
|
||||
@@ -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<EditorWindowContentController> CreateEditorWorkspaceWindowContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController) {
|
||||
return std::make_unique<EditorWorkspaceWindowContentController>(
|
||||
std::string(windowId),
|
||||
std::move(workspaceController));
|
||||
}
|
||||
|
||||
|
||||
@@ -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<EditorWindowContentController> CreateEditorWorkspaceWindowContentController(
|
||||
std::string_view windowId,
|
||||
UIEditorWorkspaceController workspaceController);
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -78,21 +78,39 @@ EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForLiveWindowMutati
|
||||
const std::vector<EditorWindowHostSnapshot>& 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<EditorWindowHostSnapshot>& 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,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Composition/EditorWindowWorkspaceStore.h"
|
||||
#include "Windowing/Frame/EditorWindowTransferRequests.h"
|
||||
#include "Windowing/System/EditorWindowSynchronizationPlan.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceController.h>
|
||||
@@ -41,6 +42,11 @@ public:
|
||||
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
||||
std::wstring_view primaryWindowTitle,
|
||||
std::string& outError) const;
|
||||
EditorWindowSynchronizationPlan BuildPlanForWorkspaceMutationRequest(
|
||||
const EditorWindowWorkspaceMutationRequest& request,
|
||||
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
||||
std::wstring_view primaryWindowTitle,
|
||||
std::string& outError) const;
|
||||
EditorWindowSynchronizationPlan BuildPlanForDestroyedWindow(
|
||||
std::string_view windowId,
|
||||
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Windowing/Frame/EditorWindowTransferRequests.h"
|
||||
#include "Windowing/System/EditorWindowSystem.h"
|
||||
|
||||
#include <XCEditor/Workspace/UIEditorWindowWorkspaceController.h>
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user