feat(xcui): add tab strip and workspace compose foundations
This commit is contained in:
@@ -2,6 +2,7 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
|
||||
test_input_modifier_tracker.cpp
|
||||
test_editor_validation_registry.cpp
|
||||
test_structured_editor_shell.cpp
|
||||
test_ui_editor_workspace_model.cpp
|
||||
# Migration bridge: editor-facing XCUI primitive tests still reuse the
|
||||
# legacy source location until they are relocated under tests/UI/Editor/unit.
|
||||
${CMAKE_SOURCE_DIR}/tests/Core/UI/test_ui_editor_collection_primitives.cpp
|
||||
|
||||
@@ -17,19 +17,27 @@ TEST(EditorValidationRegistryTest, KnownEditorValidationScenariosResolveToExisti
|
||||
const auto* keyboardScenario = FindEditorValidationScenario("editor.input.keyboard_focus");
|
||||
const auto* shortcutScenario = FindEditorValidationScenario("editor.input.shortcut_scope");
|
||||
const auto* splitterScenario = FindEditorValidationScenario("editor.layout.splitter_resize");
|
||||
const auto* tabStripScenario = FindEditorValidationScenario("editor.layout.tab_strip_selection");
|
||||
const auto* workspaceScenario = FindEditorValidationScenario("editor.layout.workspace_compose");
|
||||
|
||||
ASSERT_NE(pointerScenario, nullptr);
|
||||
ASSERT_NE(keyboardScenario, nullptr);
|
||||
ASSERT_NE(shortcutScenario, nullptr);
|
||||
ASSERT_NE(splitterScenario, nullptr);
|
||||
ASSERT_NE(tabStripScenario, nullptr);
|
||||
ASSERT_NE(workspaceScenario, nullptr);
|
||||
EXPECT_EQ(pointerScenario->domain, UIValidationDomain::Editor);
|
||||
EXPECT_EQ(keyboardScenario->domain, UIValidationDomain::Editor);
|
||||
EXPECT_EQ(shortcutScenario->domain, UIValidationDomain::Editor);
|
||||
EXPECT_EQ(splitterScenario->domain, UIValidationDomain::Editor);
|
||||
EXPECT_EQ(tabStripScenario->domain, UIValidationDomain::Editor);
|
||||
EXPECT_EQ(workspaceScenario->domain, UIValidationDomain::Editor);
|
||||
EXPECT_EQ(pointerScenario->categoryId, "input");
|
||||
EXPECT_EQ(keyboardScenario->categoryId, "input");
|
||||
EXPECT_EQ(shortcutScenario->categoryId, "input");
|
||||
EXPECT_EQ(splitterScenario->categoryId, "layout");
|
||||
EXPECT_EQ(tabStripScenario->categoryId, "layout");
|
||||
EXPECT_EQ(workspaceScenario->categoryId, "layout");
|
||||
EXPECT_TRUE(std::filesystem::exists(pointerScenario->documentPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(pointerScenario->themePath));
|
||||
EXPECT_TRUE(std::filesystem::exists(keyboardScenario->documentPath));
|
||||
@@ -38,6 +46,10 @@ TEST(EditorValidationRegistryTest, KnownEditorValidationScenariosResolveToExisti
|
||||
EXPECT_TRUE(std::filesystem::exists(shortcutScenario->themePath));
|
||||
EXPECT_TRUE(std::filesystem::exists(splitterScenario->documentPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(splitterScenario->themePath));
|
||||
EXPECT_TRUE(std::filesystem::exists(tabStripScenario->documentPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(tabStripScenario->themePath));
|
||||
EXPECT_TRUE(std::filesystem::exists(workspaceScenario->documentPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(workspaceScenario->themePath));
|
||||
}
|
||||
|
||||
TEST(EditorValidationRegistryTest, DefaultScenarioPointsToKeyboardFocusBatch) {
|
||||
|
||||
143
tests/UI/Editor/unit/test_ui_editor_workspace_model.cpp
Normal file
143
tests/UI/Editor/unit/test_ui_editor_workspace_model.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCNewEditor/Editor/UIEditorWorkspaceModel.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::NewEditor::BuildUIEditorWorkspacePanel;
|
||||
using XCEngine::NewEditor::BuildUIEditorWorkspaceSplit;
|
||||
using XCEngine::NewEditor::BuildUIEditorWorkspaceTabStack;
|
||||
using XCEngine::NewEditor::CollectUIEditorWorkspaceVisiblePanels;
|
||||
using XCEngine::NewEditor::ContainsUIEditorWorkspacePanel;
|
||||
using XCEngine::NewEditor::FindUIEditorWorkspaceActivePanel;
|
||||
using XCEngine::NewEditor::TryActivateUIEditorWorkspacePanel;
|
||||
using XCEngine::NewEditor::UIEditorWorkspaceModel;
|
||||
using XCEngine::NewEditor::UIEditorWorkspaceNodeKind;
|
||||
using XCEngine::NewEditor::UIEditorWorkspaceSplitAxis;
|
||||
using XCEngine::NewEditor::UIEditorWorkspaceValidationCode;
|
||||
using XCEngine::NewEditor::ValidateUIEditorWorkspace;
|
||||
|
||||
std::vector<std::string> CollectVisiblePanelIds(const UIEditorWorkspaceModel& workspace) {
|
||||
const auto panels = CollectUIEditorWorkspaceVisiblePanels(workspace);
|
||||
std::vector<std::string> ids = {};
|
||||
ids.reserve(panels.size());
|
||||
for (const auto& panel : panels) {
|
||||
ids.push_back(panel.panelId);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, ValidationRejectsSplitWithoutExactlyTwoChildren) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root.kind = UIEditorWorkspaceNodeKind::Split;
|
||||
workspace.root.nodeId = "root-split";
|
||||
workspace.root.splitAxis = UIEditorWorkspaceSplitAxis::Horizontal;
|
||||
workspace.root.splitRatio = 0.5f;
|
||||
workspace.root.children.push_back(
|
||||
BuildUIEditorWorkspacePanel("panel-a-node", "panel-a", "Panel A", true));
|
||||
|
||||
const auto result = ValidateUIEditorWorkspace(workspace);
|
||||
EXPECT_EQ(result.code, UIEditorWorkspaceValidationCode::InvalidSplitChildCount);
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, ValidationRejectsTabStackWithNestedSplitChild) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceTabStack(
|
||||
"root-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("panel-a-node", "panel-a", "Panel A", true),
|
||||
BuildUIEditorWorkspaceSplit(
|
||||
"nested-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.5f,
|
||||
BuildUIEditorWorkspacePanel("panel-b-node", "panel-b", "Panel B", true),
|
||||
BuildUIEditorWorkspacePanel("panel-c-node", "panel-c", "Panel C", true))
|
||||
},
|
||||
0u);
|
||||
|
||||
const auto result = ValidateUIEditorWorkspace(workspace);
|
||||
EXPECT_EQ(result.code, UIEditorWorkspaceValidationCode::NonPanelTabChild);
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, VisiblePanelsOnlyIncludeSelectedTabsAcrossSplitTree) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.68f,
|
||||
BuildUIEditorWorkspacePanel("left-panel-node", "left-panel", "Left Panel", true),
|
||||
BuildUIEditorWorkspaceSplit(
|
||||
"right-split",
|
||||
UIEditorWorkspaceSplitAxis::Vertical,
|
||||
0.74f,
|
||||
BuildUIEditorWorkspaceTabStack(
|
||||
"document-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("doc-a-node", "doc-a", "Document A", true),
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
1u),
|
||||
BuildUIEditorWorkspacePanel("bottom-panel-node", "bottom-panel", "Bottom Panel", true)));
|
||||
workspace.activePanelId = "doc-b";
|
||||
|
||||
const auto validation = ValidateUIEditorWorkspace(workspace);
|
||||
ASSERT_TRUE(validation.IsValid()) << validation.message;
|
||||
|
||||
const auto visibleIds = CollectVisiblePanelIds(workspace);
|
||||
ASSERT_EQ(visibleIds.size(), 3u);
|
||||
EXPECT_EQ(visibleIds[0], "left-panel");
|
||||
EXPECT_EQ(visibleIds[1], "doc-b");
|
||||
EXPECT_EQ(visibleIds[2], "bottom-panel");
|
||||
|
||||
const auto* activePanel = FindUIEditorWorkspaceActivePanel(workspace);
|
||||
ASSERT_NE(activePanel, nullptr);
|
||||
EXPECT_EQ(activePanel->panelId, "doc-b");
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, ActivatingHiddenPanelSelectsContainingTabAndUpdatesActivePanel) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceSplit(
|
||||
"root-split",
|
||||
UIEditorWorkspaceSplitAxis::Horizontal,
|
||||
0.62f,
|
||||
BuildUIEditorWorkspaceTabStack(
|
||||
"document-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("doc-a-node", "doc-a", "Document A", true),
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true),
|
||||
BuildUIEditorWorkspacePanel("doc-c-node", "doc-c", "Document C", true)
|
||||
},
|
||||
0u),
|
||||
BuildUIEditorWorkspacePanel("details-node", "details", "Details", true));
|
||||
|
||||
ASSERT_TRUE(ContainsUIEditorWorkspacePanel(workspace, "doc-b"));
|
||||
ASSERT_TRUE(TryActivateUIEditorWorkspacePanel(workspace, "doc-b"));
|
||||
EXPECT_EQ(workspace.activePanelId, "doc-b");
|
||||
ASSERT_EQ(workspace.root.children.front().selectedTabIndex, 1u);
|
||||
|
||||
const auto visibleIds = CollectVisiblePanelIds(workspace);
|
||||
ASSERT_EQ(visibleIds.size(), 2u);
|
||||
EXPECT_EQ(visibleIds[0], "doc-b");
|
||||
EXPECT_EQ(visibleIds[1], "details");
|
||||
}
|
||||
|
||||
TEST(UIEditorWorkspaceModelTest, ValidationRejectsActivePanelHiddenByCurrentTabSelection) {
|
||||
UIEditorWorkspaceModel workspace = {};
|
||||
workspace.root = BuildUIEditorWorkspaceTabStack(
|
||||
"document-tabs",
|
||||
{
|
||||
BuildUIEditorWorkspacePanel("doc-a-node", "doc-a", "Document A", true),
|
||||
BuildUIEditorWorkspacePanel("doc-b-node", "doc-b", "Document B", true)
|
||||
},
|
||||
0u);
|
||||
workspace.activePanelId = "doc-b";
|
||||
|
||||
const auto result = ValidateUIEditorWorkspace(workspace);
|
||||
EXPECT_EQ(result.code, UIEditorWorkspaceValidationCode::InvalidActivePanelId);
|
||||
}
|
||||
Reference in New Issue
Block a user