refactor(new_editor): tighten app dependency boundaries
This commit is contained in:
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
286
tests/UI/Editor/unit/test_ui_editor_drag_drop.cpp
Normal file
286
tests/UI/Editor/unit/test_ui_editor_drag_drop.cpp
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user