refactor(new_editor): tighten app dependency boundaries

This commit is contained in:
2026-04-19 02:48:41 +08:00
parent 7429f22fb1
commit c59cd83c38
86 changed files with 1754 additions and 1077 deletions

View File

@@ -46,7 +46,6 @@ using XCEngine::UI::UIPoint;
using XCEngine::UI::UIPointerButton;
using XCEngine::UI::UIRect;
using XCEngine::UI::Editor::AppendUIEditorShellInteraction;
using XCEngine::UI::Editor::BuildEditorFoundationShellAsset;
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceSession;
using XCEngine::UI::Editor::BuildEditorShellShortcutManager;
using XCEngine::UI::Editor::BuildDefaultUIEditorWorkspaceController;
@@ -407,7 +406,9 @@ UIEditorMenuModel BuildMenuModel() {
}
EditorShellAsset BuildScenarioShellAsset() {
EditorShellAsset asset = BuildEditorFoundationShellAsset(ResolveRepoRootPath());
EditorShellAsset asset = {};
asset.captureRootPath =
(ResolveRepoRootPath() / "new_editor/captures").lexically_normal();
asset.panelRegistry = BuildPanelRegistry();
asset.workspace = BuildWorkspace();
asset.workspaceSession =

View File

@@ -1,8 +1,5 @@
set(EDITOR_UI_UNIT_TEST_SOURCES
test_editor_host_command_bridge.cpp
test_editor_shell_asset_validation.cpp
test_input_modifier_tracker.cpp
test_structured_editor_shell.cpp
test_ui_editor_command_dispatcher.cpp
test_ui_editor_command_registry.cpp
test_ui_editor_dock_host_interaction.cpp
@@ -12,13 +9,12 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
test_ui_editor_menu_popup.cpp
test_ui_editor_panel_content_host.cpp
test_ui_editor_panel_host_lifecycle.cpp
test_ui_editor_panel_registry.cpp
test_ui_editor_property_grid.cpp
test_ui_editor_property_grid_interaction.cpp
test_ui_editor_shell_compose.cpp
test_editor_window_input_routing.cpp
test_ui_editor_shell_interaction.cpp
test_ui_editor_collection_primitives.cpp
test_ui_editor_drag_drop.cpp
test_ui_editor_field_row_layout.cpp
test_ui_editor_hosted_field_builders.cpp
test_ui_editor_bool_field.cpp
@@ -53,7 +49,6 @@ set(EDITOR_UI_UNIT_TEST_SOURCES
test_ui_editor_tab_strip_interaction.cpp
test_ui_editor_tree_view.cpp
test_ui_editor_tree_view_interaction.cpp
test_viewport_object_id_picker.cpp
test_ui_editor_viewport_input_bridge.cpp
test_ui_editor_viewport_shell.cpp
test_ui_editor_viewport_slot.cpp
@@ -79,7 +74,6 @@ target_link_libraries(editor_ui_tests
target_include_directories(editor_ui_tests
PRIVATE
${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}/include
${XCENGINE_EDITOR_UI_TESTS_EDITOR_ROOT}
${CMAKE_SOURCE_DIR}/engine/include
)
@@ -119,6 +113,15 @@ if(TARGET XCUIEditorAppLib)
)
endif()
list(APPEND EDITOR_APP_FEATURE_TEST_SOURCES
test_editor_host_command_bridge.cpp
test_editor_shell_asset_validation.cpp
test_structured_editor_shell.cpp
test_editor_window_input_routing.cpp
test_ui_editor_panel_registry.cpp
test_viewport_object_id_picker.cpp
)
add_executable(editor_app_feature_tests
${EDITOR_APP_FEATURE_TEST_SOURCES}
)
@@ -126,6 +129,7 @@ if(TARGET XCUIEditorAppLib)
target_link_libraries(editor_app_feature_tests
PRIVATE
XCUIEditorAppLib
XCUIEditorAppCore
XCUIEditorLib
XCUIEditorHost
GTest::gtest_main

View File

@@ -1,8 +1,8 @@
#include <gtest/gtest.h>
#include <XCEditor/App/EditorEditCommandRoute.h>
#include <XCEditor/App/EditorHostCommandBridge.h>
#include <XCEditor/App/EditorSession.h>
#include "Commands/EditorEditCommandRoute.h"
#include "Commands/EditorHostCommandBridge.h"
#include "State/EditorSession.h"
namespace {

View File

@@ -1,15 +1,15 @@
#include <gtest/gtest.h>
#include <XCEditor/Shell/UIEditorShellAsset.h>
#include "Composition/EditorShellAssetBuilder.h"
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
#include <XCEditor/Shell/UIEditorShellAsset.h>
#include <XCEngine/Input/InputTypes.h>
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::Editor::BuildEditorFoundationShellAsset;
using XCEngine::UI::Editor::App::BuildEditorApplicationShellAsset;
using XCEngine::UI::Editor::EditorShellAssetValidationCode;
using XCEngine::UI::Editor::FindUIEditorPanelDescriptor;
using XCEngine::UI::Editor::UIEditorCommandPanelSource;
@@ -20,6 +20,23 @@ using XCEngine::UI::UIInputEventType;
using XCEngine::UI::UIShortcutBinding;
using XCEngine::UI::UIShortcutScope;
XCEngine::UI::Editor::UIEditorWorkspaceNode* FindWorkspacePanelNode(
XCEngine::UI::Editor::UIEditorWorkspaceNode& node,
std::string_view panelId) {
if (node.kind == XCEngine::UI::Editor::UIEditorWorkspaceNodeKind::Panel &&
node.panel.panelId == panelId) {
return &node;
}
for (auto& child : node.children) {
if (auto* panel = FindWorkspacePanelNode(child, panelId); panel != nullptr) {
return panel;
}
}
return nullptr;
}
UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) {
UIShortcutBinding binding = {};
binding.commandId = std::move(commandId);
@@ -31,7 +48,7 @@ UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) {
}
TEST(EditorShellAssetValidationTest, DefaultShellAssetPassesValidation) {
const auto shellAsset = BuildEditorFoundationShellAsset(".");
const auto shellAsset = BuildEditorApplicationShellAsset(".");
const auto validation = ValidateEditorShellAsset(shellAsset);
EXPECT_TRUE(validation.IsValid()) << validation.message;
@@ -39,25 +56,24 @@ TEST(EditorShellAssetValidationTest, DefaultShellAssetPassesValidation) {
ASSERT_EQ(
shellAsset.shellDefinition.workspacePresentations.size(),
shellAsset.panelRegistry.panels.size());
ASSERT_EQ(shellAsset.shellDefinition.statusSegments.size(), 2u);
EXPECT_EQ(shellAsset.shellDefinition.statusSegments.front().label, "Editor Shell");
EXPECT_EQ(shellAsset.shellDefinition.statusSegments.back().label, "editor-foundation-root");
EXPECT_EQ(
shellAsset.shellDefinition.workspacePresentations.front().panelId,
"editor-foundation-root");
shellAsset.panelRegistry.panels.front().panelId);
EXPECT_EQ(
shellAsset.shellDefinition.workspacePresentations.front().kind,
shellAsset.panelRegistry.panels.front().presentationKind);
}
TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspacePanelMissingFromRegistry) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
auto shellAsset = BuildEditorApplicationShellAsset(".");
auto* documentPanel =
const_cast<XCEngine::UI::Editor::UIEditorPanelDescriptor*>(
FindUIEditorPanelDescriptor(shellAsset.panelRegistry, "editor-foundation-root"));
FindUIEditorPanelDescriptor(
shellAsset.panelRegistry,
shellAsset.panelRegistry.panels.front().panelId));
ASSERT_NE(documentPanel, nullptr);
documentPanel->panelId = "editor-foundation-root-renamed";
documentPanel->panelId = "renamed-panel";
const auto validation = ValidateEditorShellAsset(shellAsset);
EXPECT_EQ(validation.code, EditorShellAssetValidationCode::MissingPanelDescriptor)
@@ -65,24 +81,19 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspacePanelMissingFromR
}
TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspaceTitleDriftFromRegistry) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
shellAsset.workspace.activePanelId = "editor-foundation-root";
ASSERT_EQ(
shellAsset.workspace.root.kind,
XCEngine::UI::Editor::UIEditorWorkspaceNodeKind::TabStack);
ASSERT_EQ(shellAsset.workspace.root.children.size(), 1u);
ASSERT_EQ(
shellAsset.workspace.root.children.front().kind,
XCEngine::UI::Editor::UIEditorWorkspaceNodeKind::Panel);
shellAsset.workspace.root.children.front().panel.title = "Drifted Title";
auto shellAsset = BuildEditorApplicationShellAsset(".");
auto* scenePanel = FindWorkspacePanelNode(shellAsset.workspace.root, "scene");
ASSERT_NE(scenePanel, nullptr);
shellAsset.workspace.activePanelId = scenePanel->panel.panelId;
scenePanel->panel.title = "Drifted Title";
const auto validation = ValidateEditorShellAsset(shellAsset);
EXPECT_EQ(validation.code, EditorShellAssetValidationCode::PanelTitleMismatch);
}
TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidWorkspaceSessionState) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
ASSERT_EQ(shellAsset.workspaceSession.panelStates.size(), 1u);
auto shellAsset = BuildEditorApplicationShellAsset(".");
ASSERT_FALSE(shellAsset.workspaceSession.panelStates.empty());
shellAsset.workspaceSession.panelStates.front().open = false;
const auto validation = ValidateEditorShellAsset(shellAsset);
@@ -90,12 +101,12 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidWorkspaceSessionSta
}
TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationMissingFromRegistry) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
auto shellAsset = BuildEditorApplicationShellAsset(".");
ASSERT_EQ(
shellAsset.shellDefinition.workspacePresentations.size(),
shellAsset.panelRegistry.panels.size());
shellAsset.shellDefinition.workspacePresentations.front().panelId =
"editor-foundation-root-renamed";
"renamed-panel";
const auto validation = ValidateEditorShellAsset(shellAsset);
EXPECT_EQ(
@@ -104,7 +115,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationMissingFr
}
TEST(EditorShellAssetValidationTest, ValidationRejectsDuplicateShellPresentationPanelId) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
auto shellAsset = BuildEditorApplicationShellAsset(".");
ASSERT_EQ(
shellAsset.shellDefinition.workspacePresentations.size(),
shellAsset.panelRegistry.panels.size());
@@ -118,7 +129,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsDuplicateShellPresentation
}
TEST(EditorShellAssetValidationTest, ValidationRejectsMissingRequiredShellPresentation) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
auto shellAsset = BuildEditorApplicationShellAsset(".");
shellAsset.shellDefinition.workspacePresentations.clear();
const auto validation = ValidateEditorShellAsset(shellAsset);
@@ -128,7 +139,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsMissingRequiredShellPresen
}
TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShellMenuModel) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
auto shellAsset = BuildEditorApplicationShellAsset(".");
shellAsset.shellDefinition.menuModel.menus = {
{
"window",
@@ -153,17 +164,9 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShellMenuModel) {
}
TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShortcutConfiguration) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
shellAsset.shortcutAsset.commandRegistry.commands = {
{
"workspace.reset_layout",
"Reset Layout",
{ UIEditorWorkspaceCommandKind::ResetWorkspace, UIEditorCommandPanelSource::None, {} }
}
};
shellAsset.shortcutAsset.bindings = {
MakeBinding("missing.command", KeyCode::R)
};
auto shellAsset = BuildEditorApplicationShellAsset(".");
shellAsset.shortcutAsset.bindings.push_back(
MakeBinding("missing.command", KeyCode::R));
const auto validation = ValidateEditorShellAsset(shellAsset);
EXPECT_EQ(
@@ -172,7 +175,7 @@ TEST(EditorShellAssetValidationTest, ValidationRejectsInvalidShortcutConfigurati
}
TEST(EditorShellAssetValidationTest, ValidationRejectsShellPresentationKindMismatch) {
auto shellAsset = BuildEditorFoundationShellAsset(".");
auto shellAsset = BuildEditorApplicationShellAsset(".");
ASSERT_EQ(
shellAsset.shellDefinition.workspacePresentations.size(),
shellAsset.panelRegistry.panels.size());

View File

@@ -1,7 +1,7 @@
#include "Features/Project/ProjectPanel.h"
#include "Rendering/Assets/BuiltInIcons.h"
#include <XCEditor/App/EditorPanelIds.h>
#include "Composition/EditorPanelIds.h"
#include <gtest/gtest.h>

View File

@@ -4,7 +4,7 @@
#include "Rendering/Viewport/ViewportHostService.h"
#include "State/EditorSelectionStamp.h"
#include <XCEditor/App/EditorPanelIds.h>
#include "Composition/EditorPanelIds.h"
#include <XCEditor/Viewport/UIEditorViewportInputBridge.h>
#include <XCEditor/Viewport/UIEditorViewportSlot.h>

View File

@@ -1,5 +1,7 @@
#include <gtest/gtest.h>
#include "Composition/EditorShellAssetBuilder.h"
#include <XCEditor/Shell/UIEditorShellAsset.h>
#include <XCEditor/Shell/UIEditorStructuredShell.h>
#include <XCEditor/Shell/UIEditorShellInteraction.h>
@@ -17,7 +19,7 @@
namespace {
using XCEngine::Input::KeyCode;
using XCEngine::UI::Editor::BuildEditorFoundationShellAsset;
using XCEngine::UI::Editor::App::BuildEditorApplicationShellAsset;
using XCEngine::UI::Editor::BuildStructuredEditorShellBinding;
using XCEngine::UI::Editor::BuildStructuredEditorShellServices;
using XCEngine::UI::Editor::ResolveUIEditorShellInteractionModel;
@@ -124,7 +126,7 @@ UIShortcutBinding MakeBinding(std::string commandId, KeyCode keyCode) {
} // namespace
TEST(EditorUIStructuredShellTest, StructuredEditorShellDoesNotRequireRepositoryXCUIDocument) {
const auto shell = BuildEditorFoundationShellAsset(RepoRootPath());
const auto shell = BuildEditorApplicationShellAsset(RepoRootPath());
const auto binding = BuildStructuredEditorShellBinding(shell);
ASSERT_TRUE(binding.IsValid()) << binding.assetValidation.message;
@@ -134,8 +136,7 @@ TEST(EditorUIStructuredShellTest, StructuredEditorShellDoesNotRequireRepositoryX
}
TEST(EditorUIStructuredShellTest, StructuredShellBindingUsesEditorShellAssetAsSingleSource) {
auto shell = BuildEditorFoundationShellAsset(RepoRootPath());
shell.shellDefinition.statusSegments.front().label = "Asset Contract";
auto shell = BuildEditorApplicationShellAsset(RepoRootPath());
shell.shortcutAsset.commandRegistry.commands = {
{
"workspace.reset_layout",
@@ -187,7 +188,7 @@ TEST(EditorUIStructuredShellTest, StructuredShellBindingUsesEditorShellAssetAsSi
EXPECT_EQ(model.resolvedMenuModel.menus.front().items.front().label, "Reset Layout");
EXPECT_EQ(model.resolvedMenuModel.menus.front().items.front().shortcutText, "Ctrl+R");
ASSERT_EQ(model.statusSegments.size(), shell.shellDefinition.statusSegments.size());
EXPECT_EQ(model.statusSegments.front().label, shell.shellDefinition.statusSegments.front().label);
ASSERT_EQ(model.statusSegments.size(), shell.shellDefinition.statusSegments.size());
ASSERT_EQ(
model.workspacePresentations.size(),
shell.shellDefinition.workspacePresentations.size());

View File

@@ -0,0 +1,286 @@
#include <gtest/gtest.h>
#include <XCEditor/Collections/UIEditorGridDragDrop.h>
#include <XCEditor/Collections/UIEditorTreeDragDrop.h>
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
#include <string>
#include <string_view>
#include <vector>
namespace {
namespace GridDrag = XCEngine::UI::Editor::Collections::GridDragDrop;
namespace TreeDrag = XCEngine::UI::Editor::Collections::TreeDragDrop;
namespace Widgets = XCEngine::UI::Editor::Widgets;
using ::XCEngine::UI::UIInputEvent;
using ::XCEngine::UI::UIInputEventType;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIPointerButton;
using ::XCEngine::UI::UIRect;
UIInputEvent MakePointerButtonDown(float x, float y, UIPointerButton button) {
UIInputEvent event = {};
event.type = UIInputEventType::PointerButtonDown;
event.position = UIPoint(x, y);
event.pointerButton = button;
return event;
}
UIInputEvent MakePointerMove(float x, float y) {
UIInputEvent event = {};
event.type = UIInputEventType::PointerMove;
event.position = UIPoint(x, y);
return event;
}
UIInputEvent MakePointerButtonUp(float x, float y, UIPointerButton button) {
UIInputEvent event = {};
event.type = UIInputEventType::PointerButtonUp;
event.position = UIPoint(x, y);
event.pointerButton = button;
return event;
}
UIInputEvent MakeFocusLost() {
UIInputEvent event = {};
event.type = UIInputEventType::FocusLost;
return event;
}
struct GridCallbacks {
std::string committedDraggedItemId = {};
std::string committedDropTargetItemId = {};
int selectCount = 0;
int commitCount = 0;
bool selected = false;
std::string ResolveDraggableItem(const UIPoint& point) const {
return point.x <= 20.0f ? "AssetA" : "";
}
std::string ResolveDropTargetItem(
std::string_view,
const UIPoint& point) const {
return point.x >= 60.0f ? "FolderB" : "";
}
bool IsItemSelected(std::string_view) const {
return selected;
}
bool SelectDraggedItem(std::string_view) {
selected = true;
++selectCount;
return true;
}
bool CanDropOnItem(
std::string_view draggedItemId,
std::string_view targetItemId) const {
return !draggedItemId.empty() &&
targetItemId == "FolderB" &&
draggedItemId != targetItemId;
}
bool CommitDropOnItem(
std::string_view draggedItemId,
std::string_view targetItemId) {
committedDraggedItemId = std::string(draggedItemId);
committedDropTargetItemId = std::string(targetItemId);
++commitCount;
return true;
}
};
TEST(UIEditorGridDragDropTests, ProcessInputEventsCapturesAndCommitsDrop) {
GridDrag::State state = {};
GridCallbacks callbacks = {};
const GridDrag::ProcessResult result =
GridDrag::ProcessInputEvents(
state,
{
MakePointerButtonDown(10.0f, 12.0f, UIPointerButton::Left),
MakePointerMove(24.0f, 12.0f),
MakePointerMove(80.0f, 12.0f),
MakePointerButtonUp(80.0f, 12.0f, UIPointerButton::Left)
},
callbacks);
EXPECT_TRUE(result.selectionForced);
EXPECT_TRUE(result.dropCommitted);
EXPECT_EQ(result.draggedItemId, "AssetA");
EXPECT_EQ(result.dropTargetItemId, "FolderB");
EXPECT_EQ(callbacks.selectCount, 1);
EXPECT_EQ(callbacks.commitCount, 1);
EXPECT_EQ(callbacks.committedDraggedItemId, "AssetA");
EXPECT_EQ(callbacks.committedDropTargetItemId, "FolderB");
EXPECT_TRUE(state.requestPointerCapture);
EXPECT_TRUE(state.requestPointerRelease);
EXPECT_FALSE(state.armed);
EXPECT_FALSE(state.dragging);
EXPECT_FALSE(state.validDropTarget);
}
TEST(UIEditorGridDragDropTests, FocusLostWhileDraggingRequestsReleaseAndClearsState) {
GridDrag::State state = {};
GridCallbacks callbacks = {};
const GridDrag::ProcessResult result =
GridDrag::ProcessInputEvents(
state,
{
MakePointerButtonDown(10.0f, 12.0f, UIPointerButton::Left),
MakePointerMove(24.0f, 12.0f),
MakeFocusLost()
},
callbacks);
EXPECT_FALSE(result.dropCommitted);
EXPECT_TRUE(state.requestPointerRelease);
EXPECT_FALSE(state.requestPointerCapture);
EXPECT_FALSE(state.armed);
EXPECT_FALSE(state.dragging);
EXPECT_TRUE(state.armedItemId.empty());
EXPECT_TRUE(state.draggedItemId.empty());
EXPECT_TRUE(state.dropTargetItemId.empty());
}
struct TreeFixtureData {
std::vector<Widgets::UIEditorTreeViewItem> items = {};
::XCEngine::UI::Widgets::UIExpansionModel expansion = {};
Widgets::UIEditorTreeViewLayout layout = {};
};
TreeFixtureData BuildTreeFixture() {
TreeFixtureData fixture = {};
fixture.items = {
Widgets::UIEditorTreeViewItem{ .itemId = "NodeA", .label = "NodeA" },
Widgets::UIEditorTreeViewItem{ .itemId = "NodeB", .label = "NodeB" }
};
fixture.layout = Widgets::BuildUIEditorTreeViewLayout(
UIRect(0.0f, 0.0f, 200.0f, 120.0f),
fixture.items,
fixture.expansion);
return fixture;
}
TEST(UIEditorTreeDragDropTests, BuildInteractionInputEventsSuppressesTreeViewDragGesture) {
const TreeFixtureData fixture = BuildTreeFixture();
TreeDrag::State state = {};
ASSERT_GE(fixture.layout.rowRects.size(), 2u);
const UIRect sourceRow = fixture.layout.rowRects[0];
const UIRect targetRow = fixture.layout.rowRects[1];
const std::vector<UIInputEvent> filteredEvents =
TreeDrag::BuildInteractionInputEvents(
state,
fixture.layout,
fixture.items,
{
MakePointerButtonDown(
sourceRow.x + 12.0f,
sourceRow.y + sourceRow.height * 0.5f,
UIPointerButton::Left),
MakePointerMove(
sourceRow.x + 24.0f,
sourceRow.y + sourceRow.height * 0.5f),
MakePointerMove(
targetRow.x + 12.0f,
targetRow.y + targetRow.height * 0.5f),
MakePointerButtonUp(
targetRow.x + 12.0f,
targetRow.y + targetRow.height * 0.5f,
UIPointerButton::Left)
});
ASSERT_EQ(filteredEvents.size(), 1u);
EXPECT_EQ(filteredEvents[0].type, UIInputEventType::PointerButtonDown);
}
struct TreeCallbacks {
int selectCount = 0;
int commitToRootCount = 0;
bool selected = false;
bool IsItemSelected(std::string_view) const {
return selected;
}
bool SelectDraggedItem(std::string_view) {
selected = true;
++selectCount;
return true;
}
bool CanDropOnItem(std::string_view, std::string_view) const {
return false;
}
bool CanDropToRoot(std::string_view draggedItemId) const {
return !draggedItemId.empty();
}
bool CommitDropOnItem(std::string_view, std::string_view) {
return false;
}
bool CommitDropToRoot(std::string_view) {
++commitToRootCount;
return true;
}
};
TEST(UIEditorTreeDragDropTests, ProcessInputEventsSupportsDropToRoot) {
TreeFixtureData fixture = BuildTreeFixture();
fixture.items.resize(1u);
fixture.layout = Widgets::BuildUIEditorTreeViewLayout(
fixture.layout.bounds,
fixture.items,
fixture.expansion);
TreeDrag::State state = {};
TreeCallbacks callbacks = {};
ASSERT_FALSE(fixture.layout.rowRects.empty());
const UIRect sourceRow = fixture.layout.rowRects.front();
const UIPoint sourcePoint(
sourceRow.x + 12.0f,
sourceRow.y + sourceRow.height * 0.5f);
const UIPoint rootDropPoint(
fixture.layout.bounds.x + fixture.layout.bounds.width * 0.5f,
fixture.layout.bounds.y + fixture.layout.bounds.height - 8.0f);
const TreeDrag::ProcessResult result =
TreeDrag::ProcessInputEvents(
state,
fixture.layout,
fixture.items,
{
MakePointerButtonDown(sourcePoint.x, sourcePoint.y, UIPointerButton::Left),
MakePointerMove(rootDropPoint.x, rootDropPoint.y),
MakePointerButtonUp(rootDropPoint.x, rootDropPoint.y, UIPointerButton::Left)
},
fixture.layout.bounds,
callbacks);
EXPECT_TRUE(result.selectionForced);
EXPECT_TRUE(result.dropCommitted);
EXPECT_TRUE(result.droppedToRoot);
EXPECT_EQ(result.draggedItemId, "NodeA");
EXPECT_TRUE(result.dropTargetItemId.empty());
EXPECT_EQ(callbacks.selectCount, 1);
EXPECT_EQ(callbacks.commitToRootCount, 1);
EXPECT_TRUE(state.requestPointerCapture);
EXPECT_TRUE(state.requestPointerRelease);
EXPECT_FALSE(state.armed);
EXPECT_FALSE(state.dragging);
EXPECT_FALSE(state.dropToRoot);
EXPECT_FALSE(state.validDropTarget);
}
} // namespace

View File

@@ -1,10 +1,12 @@
#include <gtest/gtest.h>
#include "Composition/EditorShellAssetBuilder.h"
#include <XCEditor/Panels/UIEditorPanelRegistry.h>
namespace {
using XCEngine::UI::Editor::BuildEditorFoundationPanelRegistry;
using XCEngine::UI::Editor::App::BuildEditorApplicationShellAsset;
using XCEngine::UI::Editor::FindUIEditorPanelDescriptor;
using XCEngine::UI::Editor::UIEditorPanelDescriptor;
using XCEngine::UI::Editor::UIEditorPanelRegistry;
@@ -12,14 +14,17 @@ using XCEngine::UI::Editor::UIEditorPanelRegistryValidationCode;
using XCEngine::UI::Editor::ValidateUIEditorPanelRegistry;
TEST(UIEditorPanelRegistryTest, DefaultRegistryContainsShellDescriptors) {
const UIEditorPanelRegistry registry = BuildEditorFoundationPanelRegistry();
const UIEditorPanelRegistry registry =
BuildEditorApplicationShellAsset(".").panelRegistry;
ASSERT_EQ(registry.panels.size(), 1u);
ASSERT_EQ(registry.panels.size(), 6u);
const UIEditorPanelDescriptor* descriptor =
FindUIEditorPanelDescriptor(registry, "editor-foundation-root");
FindUIEditorPanelDescriptor(registry, "hierarchy");
ASSERT_NE(descriptor, nullptr);
EXPECT_FALSE(descriptor->canHide);
EXPECT_FALSE(descriptor->canClose);
EXPECT_NE(FindUIEditorPanelDescriptor(registry, "scene"), nullptr);
EXPECT_NE(FindUIEditorPanelDescriptor(registry, "project"), nullptr);
EXPECT_EQ(FindUIEditorPanelDescriptor(registry, "missing-panel"), nullptr);
}