Build XCEditor dock host workspace shell compose
This commit is contained in:
@@ -8,6 +8,7 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
|
||||
test_ui_editor_menu_session.cpp
|
||||
test_ui_editor_panel_registry.cpp
|
||||
test_ui_editor_collection_primitives.cpp
|
||||
test_ui_editor_dock_host.cpp
|
||||
test_ui_editor_panel_chrome.cpp
|
||||
test_ui_editor_panel_frame.cpp
|
||||
test_ui_editor_tab_strip.cpp
|
||||
|
||||
209
tests/UI/Editor/unit/test_ui_editor_dock_host.cpp
Normal file
209
tests/UI/Editor/unit/test_ui_editor_dock_host.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
#include <XCEditor/Core/UIEditorPanelRegistry.h>
|
||||
#include <XCEditor/Core/UIEditorWorkspaceModel.h>
|
||||
#include <XCEditor/Core/UIEditorWorkspaceSession.h>
|
||||
#include <XCEditor/Widgets/UIEditorDockHost.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::UI::UIDrawCommandType;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceSession;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::TryHideUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::UIEditorPanelRegistry;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSession;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSplitAxis;
|
||||
using XCEngine::UI::Editor::Widgets::AppendUIEditorDockHostBackground;
|
||||
using XCEngine::UI::Editor::Widgets::AppendUIEditorDockHostForeground;
|
||||
using XCEngine::UI::Editor::Widgets::BuildUIEditorDockHostLayout;
|
||||
using XCEngine::UI::Editor::Widgets::FindUIEditorDockHostSplitterLayout;
|
||||
using XCEngine::UI::Editor::Widgets::HitTestUIEditorDockHost;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostHitTarget;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostHitTargetKind;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostLayout;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostMetrics;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorDockHostState;
|
||||
using XCEngine::UI::Editor::Widgets::UIEditorTabStripInvalidIndex;
|
||||
|
||||
UIEditorPanelRegistry BuildPanelRegistry() {
|
||||
UIEditorPanelRegistry registry = {};
|
||||
registry.panels = {
|
||||
{ "doc-a", "Document A", {}, true, true, true },
|
||||
{ "doc-b", "Document B", {}, true, true, true },
|
||||
{ "details", "Details", {}, true, true, true },
|
||||
{ "console", "Console", {}, true, true, true }
|
||||
};
|
||||
return registry;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceModel BuildWorkspace() {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.5f,
|
||||
BuildUIEditorWorkspaceTabStack(
|
||||
"document-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("doc-a-node", "doc-a", "Document A", true),
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
1u),
|
||||
BuildUIEditorWorkspaceSplit(
|
||||
"right-split",
|
||||
UIEditorWorkspaceSplitAxis::Vertical,
|
||||
0.6f,
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true),
|
||||
BuildUIEditorWorkspacePanel("console-node", "console", "Console", true)));
|
||||
workspace.activePanelId = "doc-b";
|
||||
return workspace;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorDockHostTest, LayoutComposesSplitTabStackAndStandalonePanelsFromWorkspaceTree) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
const UIEditorWorkspaceSession session =
|
||||
BuildDefaultUIEditorWorkspaceSession(registry, workspace);
|
||||
|
||||
const UIEditorDockHostLayout layout = BuildUIEditorDockHostLayout(
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
registry,
|
||||
workspace,
|
||||
session);
|
||||
|
||||
ASSERT_EQ(layout.splitters.size(), 2u);
|
||||
ASSERT_EQ(layout.tabStacks.size(), 1u);
|
||||
ASSERT_EQ(layout.panels.size(), 2u);
|
||||
|
||||
const auto* rootSplitter = FindUIEditorDockHostSplitterLayout(layout, "root-split");
|
||||
ASSERT_NE(rootSplitter, nullptr);
|
||||
EXPECT_FLOAT_EQ(rootSplitter->splitterLayout.handleRect.x, 395.0f);
|
||||
EXPECT_FLOAT_EQ(rootSplitter->splitterLayout.handleRect.width, 10.0f);
|
||||
|
||||
const auto& tabStack = layout.tabStacks.front();
|
||||
EXPECT_EQ(tabStack.nodeId, "document-tabs");
|
||||
EXPECT_EQ(tabStack.selectedPanelId, "doc-b");
|
||||
ASSERT_EQ(tabStack.items.size(), 2u);
|
||||
EXPECT_EQ(tabStack.items[0].panelId, "doc-a");
|
||||
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");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostTest, HiddenBranchCollapsesAndVisibleBranchUsesFullBounds) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
UIEditorWorkspaceSession session =
|
||||
BuildDefaultUIEditorWorkspaceSession(registry, workspace);
|
||||
|
||||
ASSERT_TRUE(TryHideUIEditorWorkspacePanel(registry, workspace, session, "details"));
|
||||
ASSERT_TRUE(TryHideUIEditorWorkspacePanel(registry, workspace, session, "console"));
|
||||
|
||||
const UIEditorDockHostLayout layout = BuildUIEditorDockHostLayout(
|
||||
UIRect(10.0f, 20.0f, 640.0f, 480.0f),
|
||||
registry,
|
||||
workspace,
|
||||
session);
|
||||
|
||||
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);
|
||||
EXPECT_FLOAT_EQ(layout.tabStacks.front().bounds.height, 480.0f);
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostTest, HitTestPrioritizesSplitterThenTabCloseThenPanelBody) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
const UIEditorWorkspaceSession session =
|
||||
BuildDefaultUIEditorWorkspaceSession(registry, workspace);
|
||||
|
||||
UIEditorDockHostState state = {};
|
||||
state.focused = true;
|
||||
const UIEditorDockHostLayout layout = BuildUIEditorDockHostLayout(
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
registry,
|
||||
workspace,
|
||||
session,
|
||||
state);
|
||||
|
||||
const auto splitterHit = HitTestUIEditorDockHost(
|
||||
layout,
|
||||
UIPoint(396.0f, 120.0f));
|
||||
EXPECT_EQ(splitterHit.kind, UIEditorDockHostHitTargetKind::SplitterHandle);
|
||||
EXPECT_EQ(splitterHit.nodeId, "root-split");
|
||||
|
||||
ASSERT_EQ(layout.tabStacks.size(), 1u);
|
||||
const auto& closeRect = layout.tabStacks.front().tabStripLayout.closeButtonRects[1];
|
||||
const auto tabCloseHit = HitTestUIEditorDockHost(
|
||||
layout,
|
||||
UIPoint(closeRect.x + closeRect.width * 0.5f, closeRect.y + closeRect.height * 0.5f));
|
||||
EXPECT_EQ(tabCloseHit.kind, UIEditorDockHostHitTargetKind::TabCloseButton);
|
||||
EXPECT_EQ(tabCloseHit.nodeId, "document-tabs");
|
||||
EXPECT_EQ(tabCloseHit.panelId, "doc-b");
|
||||
EXPECT_EQ(tabCloseHit.index, 1u);
|
||||
|
||||
const auto panelBodyHit = HitTestUIEditorDockHost(
|
||||
layout,
|
||||
UIPoint(40.0f, 90.0f));
|
||||
EXPECT_EQ(panelBodyHit.kind, UIEditorDockHostHitTargetKind::PanelBody);
|
||||
EXPECT_EQ(panelBodyHit.nodeId, "document-tabs");
|
||||
EXPECT_EQ(panelBodyHit.panelId, "doc-b");
|
||||
}
|
||||
|
||||
TEST(UIEditorDockHostTest, BackgroundAndForegroundEmitStableCompositeCommands) {
|
||||
const UIEditorPanelRegistry registry = BuildPanelRegistry();
|
||||
const UIEditorWorkspaceModel workspace = BuildWorkspace();
|
||||
const UIEditorWorkspaceSession session =
|
||||
BuildDefaultUIEditorWorkspaceSession(registry, workspace);
|
||||
|
||||
UIEditorDockHostState state = {};
|
||||
state.focused = true;
|
||||
state.hoveredTarget = UIEditorDockHostHitTarget{
|
||||
UIEditorDockHostHitTargetKind::TabCloseButton,
|
||||
"document-tabs",
|
||||
"doc-b",
|
||||
1u
|
||||
};
|
||||
state.activeSplitterNodeId = "root-split";
|
||||
|
||||
const UIEditorDockHostLayout layout = BuildUIEditorDockHostLayout(
|
||||
UIRect(0.0f, 0.0f, 800.0f, 600.0f),
|
||||
registry,
|
||||
workspace,
|
||||
session,
|
||||
state);
|
||||
|
||||
UIDrawList background("DockHostBackground");
|
||||
AppendUIEditorDockHostBackground(background, layout);
|
||||
EXPECT_GT(background.GetCommandCount(), 10u);
|
||||
EXPECT_EQ(background.GetCommands().front().type, UIDrawCommandType::FilledRect);
|
||||
|
||||
UIDrawList foreground("DockHostForeground");
|
||||
AppendUIEditorDockHostForeground(foreground, layout);
|
||||
EXPECT_GT(foreground.GetCommandCount(), 10u);
|
||||
|
||||
bool foundPlaceholderText = false;
|
||||
for (const auto& command : foreground.GetCommands()) {
|
||||
if (command.type == UIDrawCommandType::Text &&
|
||||
command.text == "DockHost tab content placeholder") {
|
||||
foundPlaceholderText = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(foundPlaceholderText);
|
||||
}
|
||||
@@ -14,7 +14,9 @@ using XCEngine::UI::Editor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::UI::Editor::CollectUIEditorWorkspaceVisiblePanels;
|
||||
using XCEngine::UI::Editor::ContainsUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::FindUIEditorWorkspaceActivePanel;
|
||||
using XCEngine::UI::Editor::FindUIEditorWorkspaceNode;
|
||||
using XCEngine::UI::Editor::TryActivateUIEditorWorkspacePanel;
|
||||
using XCEngine::UI::Editor::TrySetUIEditorWorkspaceSplitRatio;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceModel;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceNodeKind;
|
||||
using XCEngine::UI::Editor::UIEditorWorkspaceSplitAxis;
|
||||
@@ -141,3 +143,23 @@ TEST(UIEditorWorkspaceModelTest, ValidationRejectsActivePanelHiddenByCurrentTabS
|
||||
const auto result = ValidateUIEditorWorkspace(workspace);
|
||||
EXPECT_EQ(result.code, UIEditorWorkspaceValidationCode::InvalidActivePanelId);
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, SplitRatioMutationTargetsSplitNodeAndRejectsInvalidValues) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.62f,
|
||||
BuildUIEditorWorkspacePanel("left-node", "left", "Left", true),
|
||||
BuildUIEditorWorkspacePanel("right-node", "right", "Right", true));
|
||||
|
||||
ASSERT_TRUE(TrySetUIEditorWorkspaceSplitRatio(workspace, "root-split", 0.35f));
|
||||
const auto* splitNode = FindUIEditorWorkspaceNode(workspace, "root-split");
|
||||
ASSERT_NE(splitNode, nullptr);
|
||||
EXPECT_FLOAT_EQ(splitNode->splitRatio, 0.35f);
|
||||
|
||||
EXPECT_FALSE(TrySetUIEditorWorkspaceSplitRatio(workspace, "root-split", 0.35f));
|
||||
EXPECT_FALSE(TrySetUIEditorWorkspaceSplitRatio(workspace, "left-node", 0.5f));
|
||||
EXPECT_FALSE(TrySetUIEditorWorkspaceSplitRatio(workspace, "missing", 0.5f));
|
||||
EXPECT_FALSE(TrySetUIEditorWorkspaceSplitRatio(workspace, "root-split", 1.0f));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user