233 lines
7.9 KiB
C++
233 lines
7.9 KiB
C++
#include "Windowing/System/EditorWindowSystem.h"
|
|
|
|
#include "Windowing/System/EditorWindowSynchronizationPlanner.h"
|
|
#include "Windowing/System/EditorWindowWorkspaceStore.h"
|
|
|
|
#include <XCEditor/Workspace/UIEditorWindowWorkspaceModel.h>
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
namespace XCEngine::UI::Editor {
|
|
|
|
EditorWindowSystem::EditorWindowSystem(UIEditorPanelRegistry panelRegistry)
|
|
: m_workspaceStore(std::make_unique<EditorWindowWorkspaceStore>(std::move(panelRegistry))) {}
|
|
|
|
EditorWindowSystem::~EditorWindowSystem() = default;
|
|
|
|
const UIEditorPanelRegistry& EditorWindowSystem::GetPanelRegistry() const {
|
|
return m_workspaceStore->GetPanelRegistry();
|
|
}
|
|
|
|
bool EditorWindowSystem::BootstrapPrimaryWindow(
|
|
std::string_view primaryWindowId,
|
|
const UIEditorWindowWorkspaceState& primaryWindowState,
|
|
std::string& outError) {
|
|
const std::string resolvedPrimaryWindowId =
|
|
primaryWindowId.empty() ? std::string("main-window") : std::string(primaryWindowId);
|
|
|
|
UIEditorWindowWorkspaceSet windowSet = {};
|
|
windowSet.primaryWindowId = resolvedPrimaryWindowId;
|
|
windowSet.activeWindowId = resolvedPrimaryWindowId;
|
|
|
|
UIEditorWindowWorkspaceState resolvedPrimaryWindowState = primaryWindowState;
|
|
resolvedPrimaryWindowState.windowId = resolvedPrimaryWindowId;
|
|
windowSet.windows.push_back(std::move(resolvedPrimaryWindowState));
|
|
|
|
return m_workspaceStore->TrySetWindowSet(std::move(windowSet), outError);
|
|
}
|
|
|
|
bool EditorWindowSystem::ValidateWindowSet(
|
|
const UIEditorWindowWorkspaceSet& windowSet,
|
|
std::string& outError) const {
|
|
return m_workspaceStore->ValidateWindowSet(windowSet, outError);
|
|
}
|
|
|
|
bool EditorWindowSystem::IsPrimaryWindowId(std::string_view windowId) const {
|
|
return m_workspaceStore->IsPrimaryWindowId(windowId);
|
|
}
|
|
|
|
const UIEditorWindowWorkspaceSet& EditorWindowSystem::GetWindowSet() const {
|
|
return m_workspaceStore->GetWindowSet();
|
|
}
|
|
|
|
const UIEditorWindowWorkspaceState* EditorWindowSystem::FindWindowState(
|
|
std::string_view windowId) const {
|
|
return FindUIEditorWindowWorkspaceState(GetWindowSet(), windowId);
|
|
}
|
|
|
|
bool EditorWindowSystem::TryBuildLiveWindowWorkspaceController(
|
|
std::string_view windowId,
|
|
UIEditorWorkspaceController& outController) {
|
|
UIEditorWindowWorkspaceState* const windowState =
|
|
m_workspaceStore->FindMutableWindowState(windowId);
|
|
if (windowState == nullptr) {
|
|
outController = {};
|
|
return false;
|
|
}
|
|
|
|
outController = UIEditorWorkspaceController::BindToState(
|
|
GetPanelRegistry(),
|
|
windowState->workspace,
|
|
windowState->session);
|
|
return true;
|
|
}
|
|
|
|
UIEditorWindowWorkspaceController EditorWindowSystem::BuildWorkspaceMutationController() const {
|
|
return UIEditorWindowWorkspaceController(GetPanelRegistry(), GetWindowSet());
|
|
}
|
|
|
|
EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForWindowSet(
|
|
const UIEditorWindowWorkspaceSet& targetWindowSet,
|
|
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
|
std::wstring_view primaryWindowTitle,
|
|
std::string_view preferredNewWindowId,
|
|
const EditorWindowSynchronizationPlacement& preferredPlacement,
|
|
std::string& outError) const {
|
|
return BuildSynchronizationPlan(
|
|
EditorWindowSynchronizationPlannerInput{
|
|
.targetWindowSet = &targetWindowSet,
|
|
.primaryWindowTitle = primaryWindowTitle,
|
|
.preferredNewWindowId = preferredNewWindowId,
|
|
.preferredPlacement = preferredPlacement,
|
|
.hostWindows = hostWindows,
|
|
},
|
|
outError);
|
|
}
|
|
|
|
EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForDestroyedWindow(
|
|
std::string_view windowId,
|
|
const std::vector<EditorWindowHostSnapshot>& hostWindows,
|
|
std::wstring_view primaryWindowTitle,
|
|
std::string& outError) const {
|
|
if (windowId.empty()) {
|
|
outError = "window destroy event missing window id";
|
|
return {};
|
|
}
|
|
|
|
UIEditorWindowWorkspaceSet nextWindowSet = GetWindowSet();
|
|
const bool destroyedPrimary = nextWindowSet.primaryWindowId == windowId;
|
|
if (!RemoveWindowStateFromSet(nextWindowSet, windowId)) {
|
|
outError =
|
|
"window destroy event references unknown authoritative window '" +
|
|
std::string(windowId) + "'";
|
|
return {};
|
|
}
|
|
if (destroyedPrimary) {
|
|
EditorWindowSynchronizationPlan plan = {};
|
|
plan.valid = true;
|
|
plan.targetWindowSet = {};
|
|
for (const EditorWindowHostSnapshot& snapshot : hostWindows) {
|
|
if (!snapshot.workspaceWindow ||
|
|
!snapshot.hasNativeWindow ||
|
|
!snapshot.running ||
|
|
snapshot.windowId == windowId) {
|
|
continue;
|
|
}
|
|
|
|
EditorWindowSynchronizationAction action = {};
|
|
action.kind = EditorWindowSynchronizationActionKind::CloseWorkspaceWindow;
|
|
action.close.windowId = snapshot.windowId;
|
|
plan.actions.push_back(std::move(action));
|
|
}
|
|
outError.clear();
|
|
return plan;
|
|
}
|
|
|
|
return BuildPlanForWindowSet(
|
|
nextWindowSet,
|
|
hostWindows,
|
|
primaryWindowTitle,
|
|
{},
|
|
{},
|
|
outError);
|
|
}
|
|
|
|
EditorWindowSynchronizationPlan EditorWindowSystem::BuildSynchronizationPlan(
|
|
const EditorWindowSynchronizationPlannerInput& input,
|
|
std::string& outError) const {
|
|
if (input.targetWindowSet == nullptr) {
|
|
outError = "window synchronization planner missing target window set";
|
|
return {};
|
|
}
|
|
|
|
if (!ValidateWindowSet(*input.targetWindowSet, outError)) {
|
|
return {};
|
|
}
|
|
|
|
EditorWindowSynchronizationPlannerInput resolvedInput = input;
|
|
resolvedInput.panelRegistry = &GetPanelRegistry();
|
|
EditorWindowSynchronizationPlan plan =
|
|
EditorWindowSynchronizationPlanner::Build(resolvedInput);
|
|
if (!plan.valid) {
|
|
outError = plan.errorMessage;
|
|
return {};
|
|
}
|
|
|
|
outError.clear();
|
|
return plan;
|
|
}
|
|
|
|
bool EditorWindowSystem::CommitSynchronizationPlan(
|
|
const EditorWindowSynchronizationPlan& plan,
|
|
std::string& outError) {
|
|
if (!plan.valid) {
|
|
outError = !plan.errorMessage.empty()
|
|
? plan.errorMessage
|
|
: "cannot commit invalid synchronization plan";
|
|
return false;
|
|
}
|
|
|
|
if (plan.targetWindowSet.windows.empty()) {
|
|
m_workspaceStore->ClearWindowSet();
|
|
outError.clear();
|
|
return true;
|
|
}
|
|
|
|
return m_workspaceStore->TrySetWindowSet(plan.targetWindowSet, outError);
|
|
}
|
|
|
|
UIEditorWindowWorkspaceOperationResult EditorWindowSystem::EvaluateDetachPanelToNewWindow(
|
|
std::string_view sourceWindowId,
|
|
std::string_view sourceNodeId,
|
|
std::string_view panelId,
|
|
UIEditorWindowWorkspaceController& outController) const {
|
|
outController = BuildWorkspaceMutationController();
|
|
return outController.DetachPanelToNewWindow(sourceWindowId, sourceNodeId, panelId);
|
|
}
|
|
|
|
bool EditorWindowSystem::RemoveWindowStateFromSet(
|
|
UIEditorWindowWorkspaceSet& windowSet,
|
|
std::string_view windowId) {
|
|
const auto originalSize = windowSet.windows.size();
|
|
windowSet.windows.erase(
|
|
std::remove_if(
|
|
windowSet.windows.begin(),
|
|
windowSet.windows.end(),
|
|
[windowId](const UIEditorWindowWorkspaceState& state) {
|
|
return state.windowId == windowId;
|
|
}),
|
|
windowSet.windows.end());
|
|
if (windowSet.windows.size() == originalSize) {
|
|
return false;
|
|
}
|
|
|
|
if (windowSet.windows.empty()) {
|
|
windowSet = {};
|
|
return true;
|
|
}
|
|
|
|
if (windowSet.primaryWindowId == windowId ||
|
|
FindUIEditorWindowWorkspaceState(windowSet, windowSet.primaryWindowId) == nullptr) {
|
|
windowSet.primaryWindowId = windowSet.windows.front().windowId;
|
|
}
|
|
if (windowSet.activeWindowId == windowId ||
|
|
FindUIEditorWindowWorkspaceState(windowSet, windowSet.activeWindowId) == nullptr) {
|
|
windowSet.activeWindowId = windowSet.primaryWindowId;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace XCEngine::UI::Editor
|