refactor(new_editor/workspace): split workspace model responsibilities
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceModel.h>
|
||||
#include <XCEditor/Workspace/UIEditorWorkspaceSession.h>
|
||||
#include "Workspace/UIEditorWorkspaceModelInternal.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
namespace {
|
||||
namespace XCEngine::UI::Editor::Detail {
|
||||
|
||||
UIEditorWorkspaceValidationResult MakeValidationError(
|
||||
UIEditorWorkspaceValidationCode code,
|
||||
@@ -50,9 +46,6 @@ void CollapseSplitNodeToOnlyChild(UIEditorWorkspaceNode& node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the remaining child through a temporary object first. Assigning
|
||||
// directly from node.children.front() aliases a subobject of node and can
|
||||
// trigger use-after-move when the vector storage is torn down.
|
||||
UIEditorWorkspaceNode remainingChild = std::move(node.children.front());
|
||||
node = std::move(remainingChild);
|
||||
}
|
||||
@@ -88,7 +81,8 @@ void CanonicalizeNodeRecursive(
|
||||
const UIEditorPanelDescriptor& RequirePanelDescriptor(
|
||||
const UIEditorPanelRegistry& registry,
|
||||
std::string_view panelId) {
|
||||
if (const UIEditorPanelDescriptor* descriptor = FindUIEditorPanelDescriptor(registry, panelId);
|
||||
if (const UIEditorPanelDescriptor* descriptor =
|
||||
FindUIEditorPanelDescriptor(registry, panelId);
|
||||
descriptor != nullptr) {
|
||||
return *descriptor;
|
||||
}
|
||||
@@ -105,7 +99,8 @@ const UIEditorWorkspacePanelState* FindPanelRecursive(
|
||||
}
|
||||
|
||||
for (const UIEditorWorkspaceNode& child : node.children) {
|
||||
if (const UIEditorWorkspacePanelState* found = FindPanelRecursive(child, panelId)) {
|
||||
if (const UIEditorWorkspacePanelState* found =
|
||||
FindPanelRecursive(child, panelId)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -121,7 +116,8 @@ const UIEditorWorkspaceNode* FindNodeRecursive(
|
||||
}
|
||||
|
||||
for (const UIEditorWorkspaceNode& child : node.children) {
|
||||
if (const UIEditorWorkspaceNode* found = FindNodeRecursive(child, nodeId)) {
|
||||
if (const UIEditorWorkspaceNode* found =
|
||||
FindNodeRecursive(child, nodeId)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +133,8 @@ UIEditorWorkspaceNode* FindMutableNodeRecursive(
|
||||
}
|
||||
|
||||
for (UIEditorWorkspaceNode& child : node.children) {
|
||||
if (UIEditorWorkspaceNode* found = FindMutableNodeRecursive(child, nodeId)) {
|
||||
if (UIEditorWorkspaceNode* found =
|
||||
FindMutableNodeRecursive(child, nodeId)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -181,7 +178,8 @@ UIEditorWorkspaceNode* ResolveMutableNodeByPath(
|
||||
bool IsPanelOpenAndVisibleInSession(
|
||||
const UIEditorWorkspaceSession& session,
|
||||
std::string_view panelId) {
|
||||
const UIEditorPanelSessionState* state = FindUIEditorPanelSessionState(session, panelId);
|
||||
const UIEditorPanelSessionState* state =
|
||||
FindUIEditorPanelSessionState(session, panelId);
|
||||
return state != nullptr && state->open && state->visible;
|
||||
}
|
||||
|
||||
@@ -261,7 +259,8 @@ bool RemoveNodeByIdRecursive(
|
||||
|
||||
for (std::size_t index = 0; index < node.children.size(); ++index) {
|
||||
if (node.children[index].nodeId == nodeId) {
|
||||
node.children.erase(node.children.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
node.children.erase(
|
||||
node.children.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
if (node.children.size() == 1u) {
|
||||
CollapseSplitNodeToOnlyChild(node);
|
||||
}
|
||||
@@ -293,7 +292,8 @@ bool IsLeadingDockPlacement(UIEditorWorkspaceDockPlacement placement) {
|
||||
placement == UIEditorWorkspaceDockPlacement::Top;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceSplitAxis ResolveDockSplitAxis(UIEditorWorkspaceDockPlacement placement) {
|
||||
UIEditorWorkspaceSplitAxis ResolveDockSplitAxis(
|
||||
UIEditorWorkspaceDockPlacement placement) {
|
||||
return placement == UIEditorWorkspaceDockPlacement::Left ||
|
||||
placement == UIEditorWorkspaceDockPlacement::Right
|
||||
? UIEditorWorkspaceSplitAxis::Horizontal
|
||||
@@ -307,13 +307,13 @@ std::string MakeUniqueNodeId(
|
||||
base = "workspace-node";
|
||||
}
|
||||
|
||||
if (FindUIEditorWorkspaceNode(workspace, base) == nullptr) {
|
||||
if (FindNodeRecursive(workspace.root, base) == nullptr) {
|
||||
return base;
|
||||
}
|
||||
|
||||
for (std::size_t suffix = 1u; suffix < 1024u; ++suffix) {
|
||||
const std::string candidate = base + "-" + std::to_string(suffix);
|
||||
if (FindUIEditorWorkspaceNode(workspace, candidate) == nullptr) {
|
||||
if (FindNodeRecursive(workspace.root, candidate) == nullptr) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
@@ -489,7 +489,8 @@ UIEditorWorkspaceValidationResult ValidateNodeRecursive(
|
||||
if (!panelIds.insert(node.panel.panelId).second) {
|
||||
return MakeValidationError(
|
||||
UIEditorWorkspaceValidationCode::DuplicatePanelId,
|
||||
"Panel id '" + node.panel.panelId + "' is duplicated in the workspace tree.");
|
||||
"Panel id '" + node.panel.panelId +
|
||||
"' is duplicated in the workspace tree.");
|
||||
}
|
||||
|
||||
return {};
|
||||
@@ -511,10 +512,12 @@ UIEditorWorkspaceValidationResult ValidateNodeRecursive(
|
||||
if (child.kind != UIEditorWorkspaceNodeKind::Panel) {
|
||||
return MakeValidationError(
|
||||
UIEditorWorkspaceValidationCode::NonPanelTabChild,
|
||||
"Tab stack '" + node.nodeId + "' may only contain panel leaf nodes.");
|
||||
"Tab stack '" + node.nodeId +
|
||||
"' may only contain panel leaf nodes.");
|
||||
}
|
||||
|
||||
if (UIEditorWorkspaceValidationResult result = ValidateNodeRecursive(child, panelIds);
|
||||
if (UIEditorWorkspaceValidationResult result =
|
||||
ValidateNodeRecursive(child, panelIds);
|
||||
!result.IsValid()) {
|
||||
return result;
|
||||
}
|
||||
@@ -526,17 +529,20 @@ UIEditorWorkspaceValidationResult ValidateNodeRecursive(
|
||||
if (node.children.size() != 2u) {
|
||||
return MakeValidationError(
|
||||
UIEditorWorkspaceValidationCode::InvalidSplitChildCount,
|
||||
"Split node '" + node.nodeId + "' must contain exactly two child nodes.");
|
||||
"Split node '" + node.nodeId +
|
||||
"' must contain exactly two child nodes.");
|
||||
}
|
||||
|
||||
if (!IsValidSplitRatio(node.splitRatio)) {
|
||||
return MakeValidationError(
|
||||
UIEditorWorkspaceValidationCode::InvalidSplitRatio,
|
||||
"Split node '" + node.nodeId + "' must define a ratio in the open interval (0, 1).");
|
||||
"Split node '" + node.nodeId +
|
||||
"' must define a ratio in the open interval (0, 1).");
|
||||
}
|
||||
|
||||
for (const UIEditorWorkspaceNode& child : node.children) {
|
||||
if (UIEditorWorkspaceValidationResult result = ValidateNodeRecursive(child, panelIds);
|
||||
if (UIEditorWorkspaceValidationResult result =
|
||||
ValidateNodeRecursive(child, panelIds);
|
||||
!result.IsValid()) {
|
||||
return result;
|
||||
}
|
||||
@@ -548,7 +554,9 @@ UIEditorWorkspaceValidationResult ValidateNodeRecursive(
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace XCEngine::UI::Editor::Detail
|
||||
|
||||
namespace XCEngine::UI::Editor {
|
||||
|
||||
bool AreUIEditorWorkspaceNodesEquivalent(
|
||||
const UIEditorWorkspaceNode& lhs,
|
||||
@@ -566,7 +574,9 @@ bool AreUIEditorWorkspaceNodesEquivalent(
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < lhs.children.size(); ++index) {
|
||||
if (!AreUIEditorWorkspaceNodesEquivalent(lhs.children[index], rhs.children[index])) {
|
||||
if (!AreUIEditorWorkspaceNodesEquivalent(
|
||||
lhs.children[index],
|
||||
rhs.children[index])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -584,7 +594,7 @@ bool AreUIEditorWorkspaceModelsEquivalent(
|
||||
UIEditorWorkspaceModel BuildDefaultEditorShellWorkspaceModel() {
|
||||
const UIEditorPanelRegistry registry = BuildDefaultEditorShellPanelRegistry();
|
||||
const UIEditorPanelDescriptor& rootPanel =
|
||||
RequirePanelDescriptor(registry, "editor-foundation-root");
|
||||
Detail::RequirePanelDescriptor(registry, "editor-foundation-root");
|
||||
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSingleTabStack(
|
||||
@@ -616,7 +626,7 @@ UIEditorWorkspaceNode BuildUIEditorWorkspaceSingleTabStack(
|
||||
std::string title,
|
||||
bool placeholder) {
|
||||
UIEditorWorkspaceNode panel = BuildUIEditorWorkspacePanel(
|
||||
BuildSingleTabPanelNodeId(nodeId),
|
||||
Detail::BuildSingleTabPanelNodeId(nodeId),
|
||||
std::move(panelId),
|
||||
std::move(title),
|
||||
placeholder);
|
||||
@@ -654,445 +664,10 @@ UIEditorWorkspaceNode BuildUIEditorWorkspaceSplit(
|
||||
return node;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceValidationResult ValidateUIEditorWorkspace(
|
||||
const UIEditorWorkspaceModel& workspace) {
|
||||
std::unordered_set<std::string> panelIds = {};
|
||||
UIEditorWorkspaceValidationResult result = ValidateNodeRecursive(workspace.root, panelIds);
|
||||
if (!result.IsValid()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!workspace.activePanelId.empty()) {
|
||||
const UIEditorWorkspacePanelState* activePanel = FindUIEditorWorkspaceActivePanel(workspace);
|
||||
if (activePanel == nullptr) {
|
||||
return MakeValidationError(
|
||||
UIEditorWorkspaceValidationCode::InvalidActivePanelId,
|
||||
"Active panel id '" + workspace.activePanelId + "' is missing or hidden by the current tab selection.");
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
UIEditorWorkspaceModel CanonicalizeUIEditorWorkspaceModel(
|
||||
UIEditorWorkspaceModel workspace) {
|
||||
CanonicalizeNodeRecursive(workspace.root, false);
|
||||
Detail::CanonicalizeNodeRecursive(workspace.root, false);
|
||||
return workspace;
|
||||
}
|
||||
|
||||
std::vector<UIEditorWorkspaceVisiblePanel> CollectUIEditorWorkspaceVisiblePanels(
|
||||
const UIEditorWorkspaceModel& workspace) {
|
||||
std::vector<UIEditorWorkspaceVisiblePanel> visiblePanels = {};
|
||||
CollectVisiblePanelsRecursive(workspace.root, workspace.activePanelId, visiblePanels);
|
||||
return visiblePanels;
|
||||
}
|
||||
|
||||
bool ContainsUIEditorWorkspacePanel(
|
||||
const UIEditorWorkspaceModel& workspace,
|
||||
std::string_view panelId) {
|
||||
return FindPanelRecursive(workspace.root, panelId) != nullptr;
|
||||
}
|
||||
|
||||
const UIEditorWorkspaceNode* FindUIEditorWorkspaceNode(
|
||||
const UIEditorWorkspaceModel& workspace,
|
||||
std::string_view nodeId) {
|
||||
return FindNodeRecursive(workspace.root, nodeId);
|
||||
}
|
||||
|
||||
const UIEditorWorkspacePanelState* FindUIEditorWorkspaceActivePanel(
|
||||
const UIEditorWorkspaceModel& workspace) {
|
||||
if (workspace.activePanelId.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<UIEditorWorkspaceVisiblePanel> visiblePanels =
|
||||
CollectUIEditorWorkspaceVisiblePanels(workspace);
|
||||
for (const UIEditorWorkspaceVisiblePanel& panel : visiblePanels) {
|
||||
if (panel.panelId == workspace.activePanelId) {
|
||||
return FindPanelRecursive(workspace.root, workspace.activePanelId);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TryActivateUIEditorWorkspacePanel(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
std::string_view panelId) {
|
||||
if (!TryActivateRecursive(workspace.root, panelId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
workspace.activePanelId = std::string(panelId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrySetUIEditorWorkspaceSplitRatio(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
std::string_view nodeId,
|
||||
float splitRatio) {
|
||||
if (!IsValidSplitRatio(splitRatio)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceNode* node = FindMutableNodeRecursive(workspace.root, nodeId);
|
||||
if (node == nullptr || node->kind != UIEditorWorkspaceNodeKind::Split) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::fabs(node->splitRatio - splitRatio) <= 0.0001f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
node->splitRatio = splitRatio;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryReorderUIEditorWorkspaceTab(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
const UIEditorWorkspaceSession& session,
|
||||
std::string_view nodeId,
|
||||
std::string_view panelId,
|
||||
std::size_t targetVisibleInsertionIndex) {
|
||||
UIEditorWorkspaceNode* node = FindMutableNodeRecursive(workspace.root, nodeId);
|
||||
if (node == nullptr || node->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::size_t> visibleChildIndices = {};
|
||||
std::vector<UIEditorWorkspaceNode> reorderedVisibleChildren = {};
|
||||
visibleChildIndices.reserve(node->children.size());
|
||||
reorderedVisibleChildren.reserve(node->children.size());
|
||||
|
||||
std::size_t sourceVisibleIndex = node->children.size();
|
||||
for (std::size_t index = 0; index < node->children.size(); ++index) {
|
||||
const UIEditorWorkspaceNode& child = node->children[index];
|
||||
if (child.kind != UIEditorWorkspaceNodeKind::Panel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsPanelOpenAndVisibleInSession(session, child.panel.panelId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child.panel.panelId == panelId) {
|
||||
sourceVisibleIndex = visibleChildIndices.size();
|
||||
}
|
||||
|
||||
visibleChildIndices.push_back(index);
|
||||
reorderedVisibleChildren.push_back(child);
|
||||
}
|
||||
|
||||
if (sourceVisibleIndex >= reorderedVisibleChildren.size() ||
|
||||
targetVisibleInsertionIndex > reorderedVisibleChildren.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetVisibleInsertionIndex == sourceVisibleIndex ||
|
||||
targetVisibleInsertionIndex == sourceVisibleIndex + 1u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceNode movedChild =
|
||||
std::move(reorderedVisibleChildren[sourceVisibleIndex]);
|
||||
reorderedVisibleChildren.erase(
|
||||
reorderedVisibleChildren.begin() + static_cast<std::ptrdiff_t>(sourceVisibleIndex));
|
||||
|
||||
std::size_t adjustedInsertionIndex = targetVisibleInsertionIndex;
|
||||
if (adjustedInsertionIndex > sourceVisibleIndex) {
|
||||
--adjustedInsertionIndex;
|
||||
}
|
||||
if (adjustedInsertionIndex > reorderedVisibleChildren.size()) {
|
||||
adjustedInsertionIndex = reorderedVisibleChildren.size();
|
||||
}
|
||||
|
||||
reorderedVisibleChildren.insert(
|
||||
reorderedVisibleChildren.begin() +
|
||||
static_cast<std::ptrdiff_t>(adjustedInsertionIndex),
|
||||
std::move(movedChild));
|
||||
|
||||
std::string selectedPanelId = {};
|
||||
if (node->selectedTabIndex < node->children.size()) {
|
||||
selectedPanelId = node->children[node->selectedTabIndex].panel.panelId;
|
||||
}
|
||||
|
||||
const std::vector<UIEditorWorkspaceNode> originalChildren = node->children;
|
||||
std::size_t nextVisibleIndex = 0u;
|
||||
for (std::size_t index = 0; index < originalChildren.size(); ++index) {
|
||||
const UIEditorWorkspaceNode& originalChild = originalChildren[index];
|
||||
if (!IsPanelOpenAndVisibleInSession(session, originalChild.panel.panelId)) {
|
||||
node->children[index] = originalChild;
|
||||
continue;
|
||||
}
|
||||
|
||||
node->children[index] = reorderedVisibleChildren[nextVisibleIndex];
|
||||
++nextVisibleIndex;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < node->children.size(); ++index) {
|
||||
if (node->children[index].panel.panelId == selectedPanelId) {
|
||||
node->selectedTabIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryExtractUIEditorWorkspaceVisiblePanelNode(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
const UIEditorWorkspaceSession& session,
|
||||
std::string_view sourceNodeId,
|
||||
std::string_view panelId,
|
||||
UIEditorWorkspaceNode& extractedPanel) {
|
||||
return TryExtractVisiblePanelFromTabStack(
|
||||
workspace,
|
||||
session,
|
||||
sourceNodeId,
|
||||
panelId,
|
||||
extractedPanel);
|
||||
}
|
||||
|
||||
bool TryInsertUIEditorWorkspacePanelNodeToStack(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
const UIEditorWorkspaceSession& session,
|
||||
UIEditorWorkspaceNode panelNode,
|
||||
std::string_view targetNodeId,
|
||||
std::size_t targetVisibleInsertionIndex) {
|
||||
if (targetNodeId.empty() ||
|
||||
panelNode.kind != UIEditorWorkspaceNodeKind::Panel ||
|
||||
panelNode.panel.panelId.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const UIEditorWorkspaceNode* targetNode =
|
||||
FindUIEditorWorkspaceNode(workspace, targetNodeId);
|
||||
if (targetNode == nullptr ||
|
||||
targetNode->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetVisibleInsertionIndex > CountVisibleChildren(*targetNode, session)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceNode* targetStack =
|
||||
FindMutableNodeRecursive(workspace.root, targetNodeId);
|
||||
if (targetStack == nullptr ||
|
||||
targetStack->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string movedPanelId = panelNode.panel.panelId;
|
||||
const std::size_t actualInsertionIndex =
|
||||
ResolveActualInsertionIndexForVisibleInsertion(
|
||||
*targetStack,
|
||||
session,
|
||||
targetVisibleInsertionIndex);
|
||||
if (actualInsertionIndex > targetStack->children.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
targetStack->children.insert(
|
||||
targetStack->children.begin() +
|
||||
static_cast<std::ptrdiff_t>(actualInsertionIndex),
|
||||
std::move(panelNode));
|
||||
targetStack->selectedTabIndex = actualInsertionIndex;
|
||||
workspace.activePanelId = movedPanelId;
|
||||
workspace = CanonicalizeUIEditorWorkspaceModel(std::move(workspace));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryDockUIEditorWorkspacePanelNodeRelative(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
const UIEditorWorkspaceSession& session,
|
||||
UIEditorWorkspaceNode panelNode,
|
||||
std::string_view targetNodeId,
|
||||
UIEditorWorkspaceDockPlacement placement,
|
||||
float splitRatio) {
|
||||
if (targetNodeId.empty() ||
|
||||
panelNode.kind != UIEditorWorkspaceNodeKind::Panel ||
|
||||
panelNode.panel.panelId.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const UIEditorWorkspaceNode* targetNode =
|
||||
FindUIEditorWorkspaceNode(workspace, targetNodeId);
|
||||
if (targetNode == nullptr ||
|
||||
targetNode->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (placement == UIEditorWorkspaceDockPlacement::Center) {
|
||||
return TryInsertUIEditorWorkspacePanelNodeToStack(
|
||||
workspace,
|
||||
session,
|
||||
std::move(panelNode),
|
||||
targetNodeId,
|
||||
CountVisibleChildren(*targetNode, session));
|
||||
}
|
||||
|
||||
UIEditorWorkspaceNode* targetStack =
|
||||
FindMutableNodeRecursive(workspace.root, targetNodeId);
|
||||
if (targetStack == nullptr ||
|
||||
targetStack->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string movedPanelId = panelNode.panel.panelId;
|
||||
const std::string movedStackNodeId = MakeUniqueNodeId(
|
||||
workspace,
|
||||
std::string(targetNodeId) + "__dock_" + movedPanelId + "_stack");
|
||||
UIEditorWorkspaceNode movedStack = {};
|
||||
movedStack.kind = UIEditorWorkspaceNodeKind::TabStack;
|
||||
movedStack.nodeId = movedStackNodeId;
|
||||
movedStack.selectedTabIndex = 0u;
|
||||
movedStack.children.push_back(std::move(panelNode));
|
||||
|
||||
UIEditorWorkspaceNode existingTarget = std::move(*targetStack);
|
||||
UIEditorWorkspaceNode primary = {};
|
||||
UIEditorWorkspaceNode secondary = {};
|
||||
if (IsLeadingDockPlacement(placement)) {
|
||||
primary = std::move(movedStack);
|
||||
secondary = std::move(existingTarget);
|
||||
} else {
|
||||
primary = std::move(existingTarget);
|
||||
secondary = std::move(movedStack);
|
||||
}
|
||||
|
||||
const float requestedRatio = ClampDockSplitRatio(splitRatio);
|
||||
const float resolvedSplitRatio =
|
||||
IsLeadingDockPlacement(placement)
|
||||
? requestedRatio
|
||||
: (1.0f - requestedRatio);
|
||||
*targetStack = BuildUIEditorWorkspaceSplit(
|
||||
MakeUniqueNodeId(
|
||||
workspace,
|
||||
std::string(targetNodeId) + "__dock_split"),
|
||||
ResolveDockSplitAxis(placement),
|
||||
resolvedSplitRatio,
|
||||
std::move(primary),
|
||||
std::move(secondary));
|
||||
workspace.activePanelId = movedPanelId;
|
||||
workspace = CanonicalizeUIEditorWorkspaceModel(std::move(workspace));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryMoveUIEditorWorkspaceTabToStack(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
const UIEditorWorkspaceSession& session,
|
||||
std::string_view sourceNodeId,
|
||||
std::string_view panelId,
|
||||
std::string_view targetNodeId,
|
||||
std::size_t targetVisibleInsertionIndex) {
|
||||
if (sourceNodeId.empty() ||
|
||||
panelId.empty() ||
|
||||
targetNodeId.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceNodeId == targetNodeId) {
|
||||
return TryReorderUIEditorWorkspaceTab(
|
||||
workspace,
|
||||
session,
|
||||
sourceNodeId,
|
||||
panelId,
|
||||
targetVisibleInsertionIndex);
|
||||
}
|
||||
|
||||
const UIEditorWorkspaceNode* targetNode =
|
||||
FindUIEditorWorkspaceNode(workspace, targetNodeId);
|
||||
if (targetNode == nullptr ||
|
||||
targetNode->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetVisibleInsertionIndex > CountVisibleChildren(*targetNode, session)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceNode extractedPanel = {};
|
||||
if (!TryExtractVisiblePanelFromTabStack(
|
||||
workspace,
|
||||
session,
|
||||
sourceNodeId,
|
||||
panelId,
|
||||
extractedPanel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryInsertUIEditorWorkspacePanelNodeToStack(
|
||||
workspace,
|
||||
session,
|
||||
std::move(extractedPanel),
|
||||
targetNodeId,
|
||||
targetVisibleInsertionIndex);
|
||||
}
|
||||
|
||||
bool TryDockUIEditorWorkspaceTabRelative(
|
||||
UIEditorWorkspaceModel& workspace,
|
||||
const UIEditorWorkspaceSession& session,
|
||||
std::string_view sourceNodeId,
|
||||
std::string_view panelId,
|
||||
std::string_view targetNodeId,
|
||||
UIEditorWorkspaceDockPlacement placement,
|
||||
float splitRatio) {
|
||||
if (placement == UIEditorWorkspaceDockPlacement::Center) {
|
||||
const UIEditorWorkspaceNode* targetNode =
|
||||
FindUIEditorWorkspaceNode(workspace, targetNodeId);
|
||||
if (targetNode == nullptr ||
|
||||
targetNode->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryMoveUIEditorWorkspaceTabToStack(
|
||||
workspace,
|
||||
session,
|
||||
sourceNodeId,
|
||||
panelId,
|
||||
targetNodeId,
|
||||
CountVisibleChildren(*targetNode, session));
|
||||
}
|
||||
|
||||
if (sourceNodeId.empty() ||
|
||||
panelId.empty() ||
|
||||
targetNodeId.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const UIEditorWorkspaceNode* sourceNode =
|
||||
FindUIEditorWorkspaceNode(workspace, sourceNodeId);
|
||||
const UIEditorWorkspaceNode* targetNode =
|
||||
FindUIEditorWorkspaceNode(workspace, targetNodeId);
|
||||
if (sourceNode == nullptr ||
|
||||
targetNode == nullptr ||
|
||||
sourceNode->kind != UIEditorWorkspaceNodeKind::TabStack ||
|
||||
targetNode->kind != UIEditorWorkspaceNodeKind::TabStack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceNodeId == targetNodeId &&
|
||||
sourceNode->children.size() <= 1u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceNode extractedPanel = {};
|
||||
if (!TryExtractVisiblePanelFromTabStack(
|
||||
workspace,
|
||||
session,
|
||||
sourceNodeId,
|
||||
panelId,
|
||||
extractedPanel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryDockUIEditorWorkspacePanelNodeRelative(
|
||||
workspace,
|
||||
session,
|
||||
std::move(extractedPanel),
|
||||
targetNodeId,
|
||||
placement,
|
||||
splitRatio);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor
|
||||
|
||||
Reference in New Issue
Block a user