Unify dock leaves around single-tab stacks
This commit is contained in:
@@ -586,7 +586,7 @@ private:
|
||||
|
||||
DrawCard(drawList, m_introRect, "这个测试验证什么功能?", "只验证 DockHost 基础交互 contract,不做 editor 业务面板。");
|
||||
drawList.AddText(UIPoint(m_introRect.x + 16.0f, m_introRect.y + 72.0f), "1. 验证 splitter drag 是否只通过 DockHostInteraction + WorkspaceController 完成。", kTextPrimary, 12.0f);
|
||||
drawList.AddText(UIPoint(m_introRect.x + 16.0f, m_introRect.y + 94.0f), "2. 验证 tab activate / tab close / standalone panel activate / panel close。", kTextPrimary, 12.0f);
|
||||
drawList.AddText(UIPoint(m_introRect.x + 16.0f, m_introRect.y + 94.0f), "2. 验证 unified dock:tab activate / tab close / single-tab body activate。", kTextPrimary, 12.0f);
|
||||
drawList.AddText(UIPoint(m_introRect.x + 16.0f, m_introRect.y + 116.0f), "3. 验证 active panel、visible panels、split ratio 是否统一收口到 controller。", kTextPrimary, 12.0f);
|
||||
drawList.AddText(UIPoint(m_introRect.x + 16.0f, m_introRect.y + 138.0f), "4. 验证 pointer capture / release 请求是否通过 contract 明确返回。", kTextPrimary, 12.0f);
|
||||
drawList.AddText(UIPoint(m_introRect.x + 16.0f, m_introRect.y + 162.0f), "建议操作:先拖中间 splitter,再点 Document A。", kTextWeak, 11.0f);
|
||||
|
||||
@@ -14,6 +14,7 @@ using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceSession;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::TryHideUIEditorWorkspacePanel;
|
||||
@@ -62,8 +63,8 @@ UIEditorWorkspaceModel BuildWorkspace() {
|
||||
"right-split",
|
||||
UIEditorWorkspaceSplitAxis::Vertical,
|
||||
0.6f,
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true),
|
||||
BuildUIEditorWorkspacePanel("console-node", "console", "Console", true)));
|
||||
BuildUIEditorWorkspaceSingleTabStack("details-node", "details", "Details", true),
|
||||
BuildUIEditorWorkspaceSingleTabStack("console-node", "console", "Console", true)));
|
||||
workspace.activePanelId = "doc-b";
|
||||
return workspace;
|
||||
}
|
||||
@@ -80,7 +81,7 @@ bool ContainsTextCommand(const UIDrawList& drawList, std::string_view text) {
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorDockHostTest, LayoutComposesSplitTabStackAndStandalonePanelsFromWorkspaceTree) {
|
||||
TEST(UIEditorDockHostTest, LayoutComposesOnlyUnifiedTabStacksFromWorkspaceTree) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
const UIEditorWorkspaceSession session =
|
||||
@@ -93,8 +94,7 @@ TEST(UIEditorDockHostTest, LayoutComposesSplitTabStackAndStandalonePanelsFromWor
|
||||
session);
|
||||
|
||||
ASSERT_EQ(layout.splitters.size(), 2u);
|
||||
ASSERT_EQ(layout.tabStacks.size(), 1u);
|
||||
ASSERT_EQ(layout.panels.size(), 2u);
|
||||
ASSERT_EQ(layout.tabStacks.size(), 3u);
|
||||
|
||||
const auto* rootSplitter = FindUIEditorDockHostSplitterLayout(layout, "root-split");
|
||||
ASSERT_NE(rootSplitter, nullptr);
|
||||
@@ -109,8 +109,10 @@ TEST(UIEditorDockHostTest, LayoutComposesSplitTabStackAndStandalonePanelsFromWor
|
||||
EXPECT_EQ(tabStack.items[1].panelId, "doc-b");
|
||||
EXPECT_EQ(tabStack.tabStripState.selectedIndex, 1u);
|
||||
|
||||
EXPECT_EQ(layout.panels[0].panelId, "details");
|
||||
EXPECT_EQ(layout.panels[1].panelId, "console");
|
||||
EXPECT_EQ(layout.tabStacks[1].nodeId, "details-node");
|
||||
EXPECT_EQ(layout.tabStacks[1].selectedPanelId, "details");
|
||||
EXPECT_EQ(layout.tabStacks[2].nodeId, "console-node");
|
||||
EXPECT_EQ(layout.tabStacks[2].selectedPanelId, "console");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostTest, HiddenBranchCollapsesAndVisibleBranchUsesFullBounds) {
|
||||
@@ -130,7 +132,6 @@ TEST(UIEditorDockHostTest, HiddenBranchCollapsesAndVisibleBranchUsesFullBounds)
|
||||
|
||||
EXPECT_TRUE(layout.splitters.empty());
|
||||
ASSERT_EQ(layout.tabStacks.size(), 1u);
|
||||
EXPECT_TRUE(layout.panels.empty());
|
||||
EXPECT_FLOAT_EQ(layout.tabStacks.front().bounds.x, 10.0f);
|
||||
EXPECT_FLOAT_EQ(layout.tabStacks.front().bounds.y, 20.0f);
|
||||
EXPECT_FLOAT_EQ(layout.tabStacks.front().bounds.width, 640.0f);
|
||||
@@ -158,7 +159,7 @@ TEST(UIEditorDockHostTest, HitTestPrioritizesSplitterThenTabCloseThenPanelBody)
|
||||
EXPECT_EQ(splitterHit.kind, UIEditorDockHostHitTargetKind::SplitterHandle);
|
||||
EXPECT_EQ(splitterHit.nodeId, "root-split");
|
||||
|
||||
ASSERT_EQ(layout.tabStacks.size(), 1u);
|
||||
ASSERT_EQ(layout.tabStacks.size(), 3u);
|
||||
const auto& closeRect = layout.tabStacks.front().tabStripLayout.closeButtonRects[1];
|
||||
const auto tabCloseHit = HitTestUIEditorDockHost(
|
||||
layout,
|
||||
@@ -209,7 +210,7 @@ TEST(UIEditorDockHostTest, BackgroundAndForegroundEmitStableCompositeCommands) {
|
||||
EXPECT_GT(foreground.GetCommandCount(), 10u);
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostTest, ForegroundByDefaultStillDrawsPlaceholderText) {
|
||||
TEST(UIEditorDockHostTest, ForegroundDrawsUnifiedTabTitlesAcrossAllLeafStacks) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
const UIEditorWorkspaceSession session =
|
||||
@@ -224,11 +225,13 @@ TEST(UIEditorDockHostTest, ForegroundByDefaultStillDrawsPlaceholderText) {
|
||||
UIDrawList foreground("DockHostForegroundDefault");
|
||||
AppendUIEditorDockHostForeground(foreground, layout);
|
||||
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "DockHost tab content placeholder"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "DockHost standalone panel"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Document A"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Document B"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Details"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Console"));
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostTest, ForegroundSkipsPlaceholderForExternalBodyPanelId) {
|
||||
TEST(UIEditorDockHostTest, ForegroundWithExternalBodyStillDrawsUnifiedTabTitles) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
const UIEditorWorkspaceSession session =
|
||||
@@ -245,6 +248,8 @@ TEST(UIEditorDockHostTest, ForegroundSkipsPlaceholderForExternalBodyPanelId) {
|
||||
options.externalBodyPanelIds = { "doc-b" };
|
||||
AppendUIEditorDockHostForeground(foreground, layout, options);
|
||||
|
||||
EXPECT_FALSE(ContainsTextCommand(foreground, "DockHost tab content placeholder"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "DockHost standalone panel"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Document A"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Document B"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Details"));
|
||||
EXPECT_TRUE(ContainsTextCommand(foreground, "Console"));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ using XCEngine::UI::UIPointerButton;
|
||||
using XCEngine::Input::KeyCode;
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceController;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::FindUIEditorPanelSessionState;
|
||||
@@ -24,6 +25,8 @@ using XCEngine::UI::Editor::UIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSplitAxis;
|
||||
using XCEngine::UI::Editor::UpdateUIEditorDockHostInteraction;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostHitTargetKind;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostLayout;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostTabStackLayout;
|
||||
|
||||
UIEditorPanelRegistry BuildPanelRegistry() {
|
||||
UIEditorPanelRegistry registry = {};
|
||||
@@ -53,8 +56,8 @@ UIEditorWorkspaceModel BuildWorkspace() {
|
||||
"right-split",
|
||||
UIEditorWorkspaceSplitAxis::Vertical,
|
||||
0.6f,
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true),
|
||||
BuildUIEditorWorkspacePanel("console-node", "console", "Console", true)));
|
||||
BuildUIEditorWorkspaceSingleTabStack("details-node", "details", "Details", true),
|
||||
BuildUIEditorWorkspaceSingleTabStack("console-node", "console", "Console", true)));
|
||||
workspace.activePanelId = "doc-b";
|
||||
return workspace;
|
||||
}
|
||||
@@ -99,6 +102,18 @@ UIPoint RectCenter(const UIRect& rect) {
|
||||
return UIPoint(rect.x + rect.width * 0.5f, rect.y + rect.height * 0.5f);
|
||||
}
|
||||
|
||||
const UIEditorDockHostTabStackLayout* FindTabStackByNodeId(
|
||||
const UIEditorDockHostLayout& layout,
|
||||
std::string_view nodeId) {
|
||||
for (const UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) {
|
||||
if (tabStack.nodeId == nodeId) {
|
||||
return &tabStack;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, SplitterDragUpdatesWorkspaceSplitRatio) {
|
||||
@@ -182,8 +197,9 @@ TEST(UIEditorDockHostInteractionTest, ClickingTabActivatesTargetPanel) {
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
const UIRect docARect = frame.layout.tabStacks.front().tabStripLayout.tabHeaderRects[0];
|
||||
const auto* documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
const UIRect docARect = documentStack->tabStripLayout.tabHeaderRects[0];
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
@@ -209,8 +225,9 @@ TEST(UIEditorDockHostInteractionTest, ClickingTabActivatesTargetPanel) {
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "doc-a");
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
EXPECT_EQ(frame.layout.tabStacks.front().selectedPanelId, "doc-a");
|
||||
documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
EXPECT_EQ(documentStack->selectedPanelId, "doc-a");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingTabCloseClosesPanelThroughController) {
|
||||
@@ -223,8 +240,9 @@ TEST(UIEditorDockHostInteractionTest, ClickingTabCloseClosesPanelThroughControll
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
const UIRect closeRect = frame.layout.tabStacks.front().tabStripLayout.closeButtonRects[1];
|
||||
const auto* documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
const UIRect closeRect = documentStack->tabStripLayout.closeButtonRects[1];
|
||||
const UIPoint closeCenter = RectCenter(closeRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
@@ -243,6 +261,14 @@ TEST(UIEditorDockHostInteractionTest, ClickingTabCloseClosesPanelThroughControll
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_FALSE(frame.result.commandExecuted);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerDown(closeCenter.x, closeCenter.y) });
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_FALSE(frame.result.commandExecuted);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
controller,
|
||||
@@ -268,8 +294,9 @@ TEST(UIEditorDockHostInteractionTest, FocusedTabStripHandlesKeyboardNavigationTh
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
const UIRect docARect = frame.layout.tabStacks.front().tabStripLayout.tabHeaderRects[0];
|
||||
const auto* documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
const UIRect docARect = documentStack->tabStripLayout.tabHeaderRects[0];
|
||||
const UIPoint docACenter = RectCenter(docARect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
@@ -302,8 +329,9 @@ TEST(UIEditorDockHostInteractionTest, FocusedTabStripHandlesKeyboardNavigationTh
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "doc-b");
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
EXPECT_EQ(frame.layout.tabStacks.front().selectedPanelId, "doc-b");
|
||||
documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
EXPECT_EQ(documentStack->selectedPanelId, "doc-b");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, BatchedPointerMoveDownUpActivatesTabInSameUpdateCall) {
|
||||
@@ -316,9 +344,10 @@ TEST(UIEditorDockHostInteractionTest, BatchedPointerMoveDownUpActivatesTabInSame
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
const auto* documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
const UIPoint docACenter =
|
||||
RectCenter(frame.layout.tabStacks.front().tabStripLayout.tabHeaderRects[0]);
|
||||
RectCenter(documentStack->tabStripLayout.tabHeaderRects[0]);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
state,
|
||||
@@ -332,11 +361,12 @@ TEST(UIEditorDockHostInteractionTest, BatchedPointerMoveDownUpActivatesTabInSame
|
||||
EXPECT_TRUE(frame.result.consumed);
|
||||
EXPECT_TRUE(frame.result.commandExecuted);
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "doc-a");
|
||||
ASSERT_EQ(frame.layout.tabStacks.size(), 1u);
|
||||
EXPECT_EQ(frame.layout.tabStacks.front().selectedPanelId, "doc-a");
|
||||
documentStack = FindTabStackByNodeId(frame.layout, "document-tabs");
|
||||
ASSERT_NE(documentStack, nullptr);
|
||||
EXPECT_EQ(documentStack->selectedPanelId, "doc-a");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelBodyActivatesTargetPanel) {
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingSingleTabStackBodyActivatesTargetPanel) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
@@ -346,8 +376,9 @@ TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelBodyActivatesTarget
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.panels.size(), 2u);
|
||||
const UIRect detailsBodyRect = frame.layout.panels[0].frameLayout.bodyRect;
|
||||
const auto* detailsStack = FindTabStackByNodeId(frame.layout, "details-node");
|
||||
ASSERT_NE(detailsStack, nullptr);
|
||||
const UIRect detailsBodyRect = detailsStack->contentFrameLayout.bodyRect;
|
||||
const UIPoint detailsBodyCenter = RectCenter(detailsBodyRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
@@ -368,7 +399,7 @@ TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelBodyActivatesTarget
|
||||
EXPECT_EQ(controller.GetWorkspace().activePanelId, "details");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelCloseClosesPanelThroughController) {
|
||||
TEST(UIEditorDockHostInteractionTest, ClickingSingleTabStackTabCloseClosesPanelThroughController) {
|
||||
auto controller =
|
||||
BuildDefaultUIEditorWorkspaceController(BuildPanelRegistry(), BuildWorkspace());
|
||||
UIEditorDockHostInteractionState state = {};
|
||||
@@ -378,8 +409,9 @@ TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelCloseClosesPanelThr
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{});
|
||||
ASSERT_EQ(frame.layout.panels.size(), 2u);
|
||||
const UIRect closeRect = frame.layout.panels[1].frameLayout.closeButtonRect;
|
||||
const auto* consoleStack = FindTabStackByNodeId(frame.layout, "console-node");
|
||||
ASSERT_NE(consoleStack, nullptr);
|
||||
const UIRect closeRect = consoleStack->tabStripLayout.closeButtonRects[0];
|
||||
const UIPoint closeCenter = RectCenter(closeRect);
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
@@ -387,7 +419,7 @@ TEST(UIEditorDockHostInteractionTest, ClickingStandalonePanelCloseClosesPanelThr
|
||||
controller,
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
{ MakePointerMove(closeCenter.x, closeCenter.y) });
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::PanelCloseButton);
|
||||
EXPECT_EQ(frame.result.hitTarget.kind, UIEditorDockHostHitTargetKind::TabCloseButton);
|
||||
EXPECT_EQ(frame.result.hitTarget.panelId, "console");
|
||||
|
||||
frame = UpdateUIEditorDockHostInteraction(
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace {
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceSession;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::CollectMountedUIEditorPanelContentHostPanelIds;
|
||||
@@ -50,7 +51,7 @@ UIEditorWorkspaceModel BuildWorkspace() {
|
||||
BuildUIEditorWorkspacePanel("console-node", "console", "Console", true)
|
||||
},
|
||||
1u),
|
||||
BuildUIEditorWorkspacePanel("inspector-node", "inspector", "Inspector"));
|
||||
BuildUIEditorWorkspaceSingleTabStack("inspector-node", "inspector", "Inspector"));
|
||||
workspace.activePanelId = "doc-b";
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceController;
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceSession;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceLayoutSnapshot;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::DeserializeUIEditorWorkspaceLayoutSnapshot;
|
||||
@@ -28,6 +29,7 @@ using XCEngine::UI::Editor::UIEditorWorkspaceCommandKind;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceCommandStatus;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSession;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceNodeKind;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSplitAxis;
|
||||
|
||||
UIEditorPanelRegistry BuildPanelRegistry() {
|
||||
@@ -53,7 +55,7 @@ UIEditorWorkspaceModel BuildWorkspace() {
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
0u),
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true));
|
||||
BuildUIEditorWorkspaceSingleTabStack("details-node", "details", "Details", true));
|
||||
workspace.activePanelId = "doc-a";
|
||||
return workspace;
|
||||
}
|
||||
@@ -128,6 +130,35 @@ TEST(UIEditorWorkspaceLayoutPersistenceTest, DeserializeRejectsMissingSessionRec
|
||||
EXPECT_EQ(loadResult.code, UIEditorWorkspaceLayoutLoadCode::InvalidWorkspaceSession);
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceLayoutPersistenceTest, DeserializeUpgradesLegacyStandalonePanelLeaves) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const std::string legacySerialized =
|
||||
"XCUI_EDITOR_WORKSPACE_LAYOUT 1\n"
|
||||
"active \"doc-a\"\n"
|
||||
"node_split \"root-split\" \"horizontal\" 0.66\n"
|
||||
"node_tabstack \"document-tabs\" 0 2\n"
|
||||
"node_panel \"doc-a-node\" \"doc-a\" \"Document A\" 1\n"
|
||||
"node_panel \"doc-b-node\" \"doc-b\" \"Document B\" 1\n"
|
||||
"node_panel \"details-node\" \"details\" \"Details\" 1\n"
|
||||
"session \"doc-a\" 1 1\n"
|
||||
"session \"doc-b\" 1 1\n"
|
||||
"session \"details\" 1 1\n";
|
||||
|
||||
const auto loadResult =
|
||||
DeserializeUIEditorWorkspaceLayoutSnapshot(registry, legacySerialized);
|
||||
|
||||
ASSERT_TRUE(loadResult.IsValid()) << loadResult.message;
|
||||
ASSERT_EQ(loadResult.snapshot.workspace.root.kind, UIEditorWorkspaceNodeKind::Split);
|
||||
ASSERT_EQ(loadResult.snapshot.workspace.root.children.size(), 2u);
|
||||
EXPECT_EQ(
|
||||
loadResult.snapshot.workspace.root.children[1].kind,
|
||||
UIEditorWorkspaceNodeKind::TabStack);
|
||||
ASSERT_EQ(loadResult.snapshot.workspace.root.children[1].children.size(), 1u);
|
||||
EXPECT_EQ(
|
||||
loadResult.snapshot.workspace.root.children[1].children[0].panel.panelId,
|
||||
"details");
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceLayoutPersistenceTest, RestoreSerializedLayoutRestoresSavedStateAfterFurtherMutations) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
UIEditorWorkspaceController controller =
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSingleTabStack;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::CanonicalizeUIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::CollectUIEditorWorkspaceVisiblePanels;
|
||||
using XCEngine::UI::Editor::ContainsUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::FindUIEditorWorkspaceActivePanel;
|
||||
@@ -73,7 +75,7 @@ TEST(UIEditorWorkspaceModelTest, VisiblePanelsOnlyIncludeSelectedTabsAcrossSplit
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.68f,
|
||||
BuildUIEditorWorkspacePanel("left-panel-node", "left-panel", "Left Panel", true),
|
||||
BuildUIEditorWorkspaceSingleTabStack("left-panel-node", "left-panel", "Left Panel", true),
|
||||
BuildUIEditorWorkspaceSplit(
|
||||
"right-split",
|
||||
UIEditorWorkspaceSplitAxis::Vertical,
|
||||
@@ -85,7 +87,11 @@ TEST(UIEditorWorkspaceModelTest, VisiblePanelsOnlyIncludeSelectedTabsAcrossSplit
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
1u),
|
||||
BuildUIEditorWorkspacePanel("bottom-panel-node", "bottom-panel", "Bottom Panel", true)));
|
||||
BuildUIEditorWorkspaceSingleTabStack(
|
||||
"bottom-panel-node",
|
||||
"bottom-panel",
|
||||
"Bottom Panel",
|
||||
true)));
|
||||
workspace.activePanelId = "doc-b";
|
||||
|
||||
const auto validation = ValidateUIEditorWorkspace(workspace);
|
||||
@@ -116,7 +122,7 @@ TEST(UIEditorWorkspaceModelTest, ActivatingHiddenPanelSelectsContainingTabAndUpd
|
||||
BuildUIEditorWorkspacePanel("doc-c-node", "doc-c", "Document C", true)
|
||||
},
|
||||
0u),
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true));
|
||||
BuildUIEditorWorkspaceSingleTabStack("details-node", "details", "Details", true));
|
||||
|
||||
ASSERT_TRUE(ContainsUIEditorWorkspacePanel(workspace, "doc-b"));
|
||||
ASSERT_TRUE(TryActivateUIEditorWorkspacePanel(workspace, "doc-b"));
|
||||
@@ -150,8 +156,8 @@ TEST(UIEditorWorkspaceModelTest, SplitRatioMutationTargetsSplitNodeAndRejectsInv
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.62f,
|
||||
BuildUIEditorWorkspacePanel("left-node", "left", "Left", true),
|
||||
BuildUIEditorWorkspacePanel("right-node", "right", "Right", true));
|
||||
BuildUIEditorWorkspaceSingleTabStack("left-node", "left", "Left", true),
|
||||
BuildUIEditorWorkspaceSingleTabStack("right-node", "right", "Right", true));
|
||||
|
||||
ASSERT_TRUE(TrySetUIEditorWorkspaceSplitRatio(workspace, "root-split", 0.35f));
|
||||
const auto* splitNode = FindUIEditorWorkspaceNode(workspace, "root-split");
|
||||
@@ -163,3 +169,29 @@ TEST(UIEditorWorkspaceModelTest, SplitRatioMutationTargetsSplitNodeAndRejectsInv
|
||||
EXPECT_FALSE(TrySetUIEditorWorkspaceSplitRatio(workspace, "missing", 0.5f));
|
||||
EXPECT_FALSE(TrySetUIEditorWorkspaceSplitRatio(workspace, "root-split", 1.0f));
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, CanonicalizeWrapsStandalonePanelsIntoSingleTabStacks) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.5f,
|
||||
BuildUIEditorWorkspacePanel("left-node", "left", "Left", true),
|
||||
BuildUIEditorWorkspacePanel("right-node", "right", "Right", true));
|
||||
|
||||
const UIEditorWorkspaceModel canonicalWorkspace =
|
||||
CanonicalizeUIEditorWorkspaceModel(workspace);
|
||||
|
||||
ASSERT_EQ(canonicalWorkspace.root.kind, UIEditorWorkspaceNodeKind::Split);
|
||||
ASSERT_EQ(canonicalWorkspace.root.children.size(), 2u);
|
||||
EXPECT_EQ(canonicalWorkspace.root.children[0].kind, UIEditorWorkspaceNodeKind::TabStack);
|
||||
EXPECT_EQ(canonicalWorkspace.root.children[0].nodeId, "left-node");
|
||||
ASSERT_EQ(canonicalWorkspace.root.children[0].children.size(), 1u);
|
||||
EXPECT_EQ(
|
||||
canonicalWorkspace.root.children[0].children[0].kind,
|
||||
UIEditorWorkspaceNodeKind::Panel);
|
||||
EXPECT_EQ(
|
||||
canonicalWorkspace.root.children[0].children[0].panel.panelId,
|
||||
"left");
|
||||
EXPECT_EQ(canonicalWorkspace.root.children[1].kind, UIEditorWorkspaceNodeKind::TabStack);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user