chore: checkpoint current workspace changes

This commit is contained in:
2026-04-11 22:14:02 +08:00
parent 3e55f8c204
commit 8848cfd958
227 changed files with 34027 additions and 6711 deletions

View File

@@ -4,6 +4,7 @@ set(CORE_UI_TEST_SOURCES
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_layout_engine.cpp
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_ui_core.cpp
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_ui_draw_data.cpp
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_ui_drag_drop_interaction.cpp
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_ui_expansion_model.cpp
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_ui_flat_hierarchy_helpers.cpp
${CMAKE_SOURCE_DIR}/tests/UI/Core/unit/test_ui_input_dispatcher.cpp

View File

@@ -0,0 +1,164 @@
#include <gtest/gtest.h>
#include <XCEngine/UI/Widgets/UIDragDropInteraction.h>
#include <array>
namespace {
using XCEngine::UI::UIPoint;
using XCEngine::UI::Widgets::BeginUIDragDrop;
using XCEngine::UI::Widgets::CancelUIDragDrop;
using XCEngine::UI::Widgets::DoesUIDragDropPayloadTypeMatch;
using XCEngine::UI::Widgets::EndUIDragDrop;
using XCEngine::UI::Widgets::HasResolvedUIDragDropTarget;
using XCEngine::UI::Widgets::IsUIDragDropInProgress;
using XCEngine::UI::Widgets::ResolveUIDragDropOperation;
using XCEngine::UI::Widgets::UIDragDropOperation;
using XCEngine::UI::Widgets::UIDragDropPayload;
using XCEngine::UI::Widgets::UIDragDropResult;
using XCEngine::UI::Widgets::UIDragDropSourceDescriptor;
using XCEngine::UI::Widgets::UIDragDropState;
using XCEngine::UI::Widgets::UIDragDropTargetDescriptor;
using XCEngine::UI::Widgets::UpdateUIDragDropPointer;
using XCEngine::UI::Widgets::UpdateUIDragDropTarget;
UIDragDropSourceDescriptor BuildSource() {
UIDragDropSourceDescriptor descriptor = {};
descriptor.ownerId = 101u;
descriptor.sourceId = "project.asset.texture";
descriptor.pointerDownPosition = UIPoint(24.0f, 32.0f);
descriptor.payload = UIDragDropPayload{ "asset.texture", "tex-001", "Checker" };
descriptor.allowedOperations =
UIDragDropOperation::Copy |
UIDragDropOperation::Move;
descriptor.activationDistance = 5.0f;
return descriptor;
}
UIDragDropTargetDescriptor BuildTarget() {
static constexpr std::array<std::string_view, 1> kAcceptedTypes = {
"asset.texture"
};
UIDragDropTargetDescriptor target = {};
target.ownerId = 202u;
target.targetId = "project.browser";
target.acceptedPayloadTypes = kAcceptedTypes;
target.acceptedOperations =
UIDragDropOperation::Copy |
UIDragDropOperation::Link;
target.preferredOperation =
UIDragDropOperation::Copy |
UIDragDropOperation::Link;
return target;
}
void ActivateDrag(UIDragDropState& state) {
ASSERT_TRUE(BeginUIDragDrop(BuildSource(), state));
ASSERT_TRUE(UpdateUIDragDropPointer(state, UIPoint(40.0f, 48.0f)));
ASSERT_TRUE(state.active);
}
} // namespace
TEST(UIDragDropInteractionTest, PayloadTypeMatchAndOperationResolutionStayWithinSupportedSingleOperation) {
constexpr std::array<std::string_view, 2> acceptedTypes = {
"asset.texture",
"asset.material"
};
EXPECT_TRUE(DoesUIDragDropPayloadTypeMatch("asset.texture", acceptedTypes));
EXPECT_FALSE(DoesUIDragDropPayloadTypeMatch("scene.entity", acceptedTypes));
EXPECT_TRUE(DoesUIDragDropPayloadTypeMatch("anything", {}));
const UIDragDropTargetDescriptor target = BuildTarget();
EXPECT_EQ(
ResolveUIDragDropOperation(UIDragDropOperation::Copy, target),
UIDragDropOperation::Copy);
}
TEST(UIDragDropInteractionTest, PointerMustCrossActivationDistanceBeforeDragBecomesActive) {
UIDragDropState state = {};
ASSERT_TRUE(BeginUIDragDrop(BuildSource(), state));
EXPECT_TRUE(IsUIDragDropInProgress(state));
EXPECT_TRUE(state.armed);
EXPECT_FALSE(state.active);
UIDragDropResult result = {};
EXPECT_TRUE(UpdateUIDragDropPointer(state, UIPoint(27.0f, 35.0f), &result));
EXPECT_FALSE(result.activated);
EXPECT_FALSE(state.active);
EXPECT_TRUE(UpdateUIDragDropPointer(state, UIPoint(31.0f, 39.0f), &result));
EXPECT_TRUE(result.activated);
EXPECT_TRUE(state.active);
}
TEST(UIDragDropInteractionTest, TargetUpdatesReturnConsistentAcceptedAndRejectedSnapshots) {
UIDragDropState state = {};
ActivateDrag(state);
const UIDragDropTargetDescriptor acceptedTarget = BuildTarget();
UIDragDropResult result = {};
EXPECT_TRUE(UpdateUIDragDropTarget(state, &acceptedTarget, &result));
EXPECT_TRUE(result.targetChanged);
EXPECT_EQ(result.targetId, "project.browser");
EXPECT_EQ(result.operation, UIDragDropOperation::Copy);
EXPECT_TRUE(HasResolvedUIDragDropTarget(state));
EXPECT_TRUE(UpdateUIDragDropTarget(state, &acceptedTarget, &result));
EXPECT_FALSE(result.targetChanged);
EXPECT_EQ(result.targetId, "project.browser");
EXPECT_EQ(result.operation, UIDragDropOperation::Copy);
static constexpr std::array<std::string_view, 1> kRejectedTypes = {
"scene.entity"
};
UIDragDropTargetDescriptor rejectedTarget = acceptedTarget;
rejectedTarget.acceptedPayloadTypes = kRejectedTypes;
EXPECT_FALSE(UpdateUIDragDropTarget(state, &rejectedTarget, &result));
EXPECT_TRUE(result.targetChanged);
EXPECT_EQ(result.targetOwnerId, 0u);
EXPECT_TRUE(result.targetId.empty());
EXPECT_EQ(result.operation, UIDragDropOperation::None);
EXPECT_FALSE(HasResolvedUIDragDropTarget(state));
}
TEST(UIDragDropInteractionTest, ReleaseOverResolvedTargetCompletesAndResetsState) {
UIDragDropState state = {};
ActivateDrag(state);
const UIDragDropTargetDescriptor target = BuildTarget();
ASSERT_TRUE(UpdateUIDragDropTarget(state, &target));
UIDragDropResult result = {};
ASSERT_TRUE(EndUIDragDrop(state, result));
EXPECT_TRUE(result.completed);
EXPECT_FALSE(result.cancelled);
EXPECT_EQ(result.sourceId, "project.asset.texture");
EXPECT_EQ(result.targetId, "project.browser");
EXPECT_EQ(result.payloadTypeId, "asset.texture");
EXPECT_EQ(result.payloadItemId, "tex-001");
EXPECT_EQ(result.operation, UIDragDropOperation::Copy);
EXPECT_FALSE(IsUIDragDropInProgress(state));
}
TEST(UIDragDropInteractionTest, CancelOrReleaseWithoutResolvedTargetCancelsAndResetsState) {
UIDragDropState state = {};
ActivateDrag(state);
UIDragDropResult result = {};
ASSERT_TRUE(EndUIDragDrop(state, result));
EXPECT_FALSE(result.completed);
EXPECT_TRUE(result.cancelled);
EXPECT_EQ(result.sourceId, "project.asset.texture");
EXPECT_FALSE(IsUIDragDropInProgress(state));
ASSERT_TRUE(BeginUIDragDrop(BuildSource(), state));
ASSERT_TRUE(CancelUIDragDrop(state, &result));
EXPECT_TRUE(result.cancelled);
EXPECT_EQ(result.sourceId, "project.asset.texture");
EXPECT_FALSE(IsUIDragDropInProgress(state));
}

View File

@@ -59,6 +59,23 @@ TEST(UISelectionModelTest, MultiSelectionTracksMembershipAndPrimarySelection) {
EXPECT_FALSE(selection.SetPrimarySelection("missing"));
}
TEST(UISelectionModelTest, ToggleSelectionMembershipAddsAndRemovesWithoutDroppingOthers) {
UISelectionModel selection = {};
EXPECT_TRUE(selection.SetSelections({ "camera", "lights" }, "lights"));
EXPECT_TRUE(selection.ToggleSelectionMembership("scene"));
EXPECT_TRUE(selection.IsSelected("camera"));
EXPECT_TRUE(selection.IsSelected("lights"));
EXPECT_TRUE(selection.IsSelected("scene"));
EXPECT_EQ(selection.GetSelectedId(), "scene");
EXPECT_TRUE(selection.ToggleSelectionMembership("lights"));
EXPECT_TRUE(selection.IsSelected("camera"));
EXPECT_FALSE(selection.IsSelected("lights"));
EXPECT_TRUE(selection.IsSelected("scene"));
EXPECT_EQ(selection.GetSelectedId(), "scene");
}
TEST(UISelectionModelTest, SetSelectionsNormalizesDuplicatesAndKeepsRequestedPrimary) {
UISelectionModel selection = {};