Lay groundwork for detached editor windows

This commit is contained in:
2026-04-14 15:07:52 +08:00
parent 804e5138d7
commit 3f871a4f45
21 changed files with 1820 additions and 97 deletions

View File

@@ -839,6 +839,145 @@ bool TryReorderUIEditorWorkspaceTab(
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,
@@ -882,30 +1021,12 @@ bool TryMoveUIEditorWorkspaceTabToStack(
return false;
}
UIEditorWorkspaceNode* targetStack =
FindMutableNodeRecursive(workspace.root, targetNodeId);
if (targetStack == nullptr ||
targetStack->kind != UIEditorWorkspaceNodeKind::TabStack) {
return false;
}
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(extractedPanel));
targetStack->selectedTabIndex = actualInsertionIndex;
workspace.activePanelId = std::string(panelId);
workspace = CanonicalizeUIEditorWorkspaceModel(std::move(workspace));
return true;
return TryInsertUIEditorWorkspacePanelNodeToStack(
workspace,
session,
std::move(extractedPanel),
targetNodeId,
targetVisibleInsertionIndex);
}
bool TryDockUIEditorWorkspaceTabRelative(
@@ -965,49 +1086,13 @@ bool TryDockUIEditorWorkspaceTabRelative(
return false;
}
UIEditorWorkspaceNode* targetStack =
FindMutableNodeRecursive(workspace.root, targetNodeId);
if (targetStack == nullptr ||
targetStack->kind != UIEditorWorkspaceNodeKind::TabStack) {
return false;
}
const std::string movedStackNodeId = MakeUniqueNodeId(
return TryDockUIEditorWorkspacePanelNodeRelative(
workspace,
std::string(targetNodeId) + "__dock_" + std::string(panelId) + "_stack");
UIEditorWorkspaceNode movedStack = {};
movedStack.kind = UIEditorWorkspaceNodeKind::TabStack;
movedStack.nodeId = movedStackNodeId;
movedStack.selectedTabIndex = 0u;
movedStack.children.push_back(std::move(extractedPanel));
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 = std::string(panelId);
workspace = CanonicalizeUIEditorWorkspaceModel(std::move(workspace));
return true;
session,
std::move(extractedPanel),
targetNodeId,
placement,
splitRatio);
}
} // namespace XCEngine::UI::Editor