2026-04-19 02:48:41 +08:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <XCEditor/Collections/UIEditorDragDropInteraction.h>
|
|
|
|
|
#include <XCEditor/Collections/UIEditorTreeView.h>
|
|
|
|
|
|
|
|
|
|
#include <XCEngine/UI/Types.h>
|
|
|
|
|
|
2026-04-23 14:09:28 +08:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <concepts>
|
2026-04-19 02:48:41 +08:00
|
|
|
#include <string>
|
|
|
|
|
#include <string_view>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine::UI::Editor::Collections::TreeDragDrop {
|
|
|
|
|
|
|
|
|
|
using ::XCEngine::UI::UIPoint;
|
|
|
|
|
using ::XCEngine::UI::UIRect;
|
|
|
|
|
using DragDropInteraction::kDefaultDragThreshold;
|
|
|
|
|
using Widgets::HitTestUIEditorTreeView;
|
|
|
|
|
using Widgets::UIEditorTreeViewHitTarget;
|
|
|
|
|
using Widgets::UIEditorTreeViewHitTargetKind;
|
|
|
|
|
using Widgets::UIEditorTreeViewInvalidIndex;
|
2026-04-23 14:09:28 +08:00
|
|
|
using ::XCEngine::UI::Editor::UIEditorTreeViewDropPreviewPlacement;
|
2026-04-19 02:48:41 +08:00
|
|
|
|
|
|
|
|
struct State : DragDropInteraction::State {
|
|
|
|
|
bool dropToRoot = false;
|
2026-04-23 14:09:28 +08:00
|
|
|
UIEditorTreeViewDropPreviewPlacement dropPlacement =
|
|
|
|
|
UIEditorTreeViewDropPreviewPlacement::None;
|
2026-04-19 02:48:41 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ProcessResult : DragDropInteraction::ProcessResult {
|
|
|
|
|
bool droppedToRoot = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline void ResetTransientRequests(State& state) {
|
|
|
|
|
DragDropInteraction::ResetTransientRequests(state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool HasActivePointerCapture(const State& state) {
|
|
|
|
|
return DragDropInteraction::HasActivePointerCapture(state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
|
|
|
|
|
return point.x >= rect.x &&
|
|
|
|
|
point.x <= rect.x + rect.width &&
|
|
|
|
|
point.y >= rect.y &&
|
|
|
|
|
point.y <= rect.y + rect.height;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-23 14:09:28 +08:00
|
|
|
template <typename Callbacks>
|
|
|
|
|
inline constexpr bool SupportsSiblingInsertion() {
|
|
|
|
|
return requires(
|
|
|
|
|
Callbacks& callbacks,
|
|
|
|
|
std::string_view draggedItemId,
|
|
|
|
|
std::string_view targetItemId) {
|
|
|
|
|
{ callbacks.CanInsertBeforeItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
|
|
|
|
|
{ callbacks.CanInsertAfterItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
|
|
|
|
|
{ callbacks.CommitInsertBeforeItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
|
|
|
|
|
{ callbacks.CommitInsertAfterItem(draggedItemId, targetItemId) } -> std::convertible_to<bool>;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline UIEditorTreeViewDropPreviewPlacement ResolveRowDropPlacement(
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout,
|
|
|
|
|
const UIEditorTreeViewHitTarget& hitTarget,
|
|
|
|
|
const UIPoint& point) {
|
|
|
|
|
if (hitTarget.visibleIndex >= layout.rowRects.size()) {
|
|
|
|
|
return UIEditorTreeViewDropPreviewPlacement::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIRect& rowRect = layout.rowRects[hitTarget.visibleIndex];
|
|
|
|
|
const float edgeBand =
|
|
|
|
|
(std::min)((std::max)(rowRect.height * 0.25f, 4.0f), 8.0f);
|
|
|
|
|
if (point.y <= rowRect.y + edgeBand) {
|
|
|
|
|
return UIEditorTreeViewDropPreviewPlacement::BeforeItem;
|
|
|
|
|
}
|
|
|
|
|
if (point.y >= rowRect.y + rowRect.height - edgeBand) {
|
|
|
|
|
return UIEditorTreeViewDropPreviewPlacement::AfterItem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return UIEditorTreeViewDropPreviewPlacement::OnItem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline const Widgets::UIEditorTreeViewItem* ResolveGapInsertionItem(
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout,
|
|
|
|
|
const std::vector<Widgets::UIEditorTreeViewItem>& items,
|
|
|
|
|
const UIPoint& point,
|
|
|
|
|
UIEditorTreeViewDropPreviewPlacement& outPlacement) {
|
|
|
|
|
outPlacement = UIEditorTreeViewDropPreviewPlacement::None;
|
|
|
|
|
if (layout.rowRects.empty()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (point.y < layout.rowRects.front().y) {
|
|
|
|
|
outPlacement = UIEditorTreeViewDropPreviewPlacement::BeforeItem;
|
|
|
|
|
const std::size_t itemIndex = layout.visibleItemIndices.front();
|
|
|
|
|
return itemIndex < items.size() ? &items[itemIndex] : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (std::size_t visibleIndex = 0u; visibleIndex + 1u < layout.rowRects.size(); ++visibleIndex) {
|
|
|
|
|
const UIRect& currentRow = layout.rowRects[visibleIndex];
|
|
|
|
|
const UIRect& nextRow = layout.rowRects[visibleIndex + 1u];
|
|
|
|
|
if (point.y > currentRow.y + currentRow.height &&
|
|
|
|
|
point.y < nextRow.y) {
|
|
|
|
|
outPlacement = UIEditorTreeViewDropPreviewPlacement::BeforeItem;
|
|
|
|
|
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex + 1u];
|
|
|
|
|
return itemIndex < items.size() ? &items[itemIndex] : nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIRect& lastRow = layout.rowRects.back();
|
|
|
|
|
if (point.y > lastRow.y + lastRow.height) {
|
|
|
|
|
outPlacement = UIEditorTreeViewDropPreviewPlacement::AfterItem;
|
|
|
|
|
const std::size_t itemIndex = layout.visibleItemIndices.back();
|
|
|
|
|
return itemIndex < items.size() ? &items[itemIndex] : nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 02:48:41 +08:00
|
|
|
inline const Widgets::UIEditorTreeViewItem* ResolveHitItem(
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout,
|
|
|
|
|
const std::vector<Widgets::UIEditorTreeViewItem>& items,
|
|
|
|
|
const UIPoint& point,
|
|
|
|
|
UIEditorTreeViewHitTarget* hitTargetOutput = nullptr) {
|
|
|
|
|
const UIEditorTreeViewHitTarget hitTarget = HitTestUIEditorTreeView(layout, point);
|
|
|
|
|
if (hitTargetOutput != nullptr) {
|
|
|
|
|
*hitTargetOutput = hitTarget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hitTarget.itemIndex >= items.size()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &items[hitTarget.itemIndex];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::size_t FindVisibleIndexForItemId(
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout,
|
|
|
|
|
const std::vector<Widgets::UIEditorTreeViewItem>& items,
|
|
|
|
|
std::string_view itemId) {
|
|
|
|
|
for (std::size_t visibleIndex = 0u; visibleIndex < layout.visibleItemIndices.size(); ++visibleIndex) {
|
|
|
|
|
const std::size_t itemIndex = layout.visibleItemIndices[visibleIndex];
|
|
|
|
|
if (itemIndex < items.size() && items[itemIndex].itemId == itemId) {
|
|
|
|
|
return visibleIndex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return UIEditorTreeViewInvalidIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 18:37:05 +08:00
|
|
|
inline bool IsSuppressedPointerInputEvent(
|
|
|
|
|
const DragDropInteraction::UIInputEvent& event) {
|
|
|
|
|
switch (event.type) {
|
|
|
|
|
case ::XCEngine::UI::UIInputEventType::PointerMove:
|
|
|
|
|
case ::XCEngine::UI::UIInputEventType::PointerButtonDown:
|
|
|
|
|
case ::XCEngine::UI::UIInputEventType::PointerButtonUp:
|
|
|
|
|
case ::XCEngine::UI::UIInputEventType::PointerWheel:
|
|
|
|
|
case ::XCEngine::UI::UIInputEventType::PointerEnter:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::vector<DragDropInteraction::UIInputEvent> FilterPointerInputEvents(
|
|
|
|
|
const std::vector<DragDropInteraction::UIInputEvent>& inputEvents,
|
|
|
|
|
bool suppressPointerInput) {
|
|
|
|
|
if (!suppressPointerInput) {
|
|
|
|
|
return inputEvents;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<DragDropInteraction::UIInputEvent> filteredEvents = {};
|
|
|
|
|
filteredEvents.reserve(inputEvents.size());
|
|
|
|
|
for (const DragDropInteraction::UIInputEvent& event : inputEvents) {
|
|
|
|
|
if (!IsSuppressedPointerInputEvent(event)) {
|
|
|
|
|
filteredEvents.push_back(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filteredEvents;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 02:48:41 +08:00
|
|
|
inline std::vector<DragDropInteraction::UIInputEvent> BuildInteractionInputEvents(
|
|
|
|
|
const State& state,
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout,
|
|
|
|
|
const std::vector<Widgets::UIEditorTreeViewItem>& items,
|
|
|
|
|
const std::vector<DragDropInteraction::UIInputEvent>& rawEvents,
|
2026-04-22 18:37:05 +08:00
|
|
|
float dragThreshold = kDefaultDragThreshold,
|
|
|
|
|
bool suppressPointerInput = false) {
|
2026-04-19 02:48:41 +08:00
|
|
|
auto resolveDraggableItem =
|
|
|
|
|
[&layout, &items](const UIPoint& point) -> std::string {
|
|
|
|
|
UIEditorTreeViewHitTarget hitTarget = {};
|
|
|
|
|
const Widgets::UIEditorTreeViewItem* hitItem =
|
|
|
|
|
ResolveHitItem(layout, items, point, &hitTarget);
|
|
|
|
|
if (hitItem != nullptr &&
|
|
|
|
|
hitTarget.kind == UIEditorTreeViewHitTargetKind::Row) {
|
|
|
|
|
return hitItem->itemId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DragDropInteraction::PreviewState preview =
|
|
|
|
|
DragDropInteraction::BuildPreviewState(state);
|
2026-04-22 18:37:05 +08:00
|
|
|
const std::vector<DragDropInteraction::UIInputEvent> inputEvents =
|
|
|
|
|
FilterPointerInputEvents(rawEvents, suppressPointerInput);
|
2026-04-19 02:48:41 +08:00
|
|
|
std::vector<DragDropInteraction::UIInputEvent> filteredEvents = {};
|
2026-04-22 18:37:05 +08:00
|
|
|
filteredEvents.reserve(inputEvents.size());
|
|
|
|
|
for (const DragDropInteraction::UIInputEvent& event : inputEvents) {
|
2026-04-19 02:48:41 +08:00
|
|
|
if (!DragDropInteraction::ProcessPreviewEvent(
|
|
|
|
|
preview,
|
|
|
|
|
event,
|
|
|
|
|
resolveDraggableItem,
|
|
|
|
|
dragThreshold)) {
|
|
|
|
|
filteredEvents.push_back(event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filteredEvents;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Callbacks>
|
|
|
|
|
ProcessResult ProcessInputEvents(
|
|
|
|
|
State& state,
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout,
|
|
|
|
|
const std::vector<Widgets::UIEditorTreeViewItem>& items,
|
|
|
|
|
const std::vector<DragDropInteraction::UIInputEvent>& inputEvents,
|
|
|
|
|
const UIRect& bounds,
|
|
|
|
|
Callbacks& callbacks,
|
2026-04-22 18:37:05 +08:00
|
|
|
float dragThreshold = kDefaultDragThreshold,
|
|
|
|
|
bool suppressPointerInput = false) {
|
2026-04-19 02:48:41 +08:00
|
|
|
struct AdaptedCallbacks {
|
|
|
|
|
const Widgets::UIEditorTreeViewLayout& layout;
|
|
|
|
|
const std::vector<Widgets::UIEditorTreeViewItem>& items;
|
|
|
|
|
const UIRect& bounds;
|
|
|
|
|
Callbacks& callbacks;
|
|
|
|
|
bool droppedToRoot = false;
|
|
|
|
|
|
|
|
|
|
std::string ResolveDraggableItem(const UIPoint& point) const {
|
|
|
|
|
UIEditorTreeViewHitTarget hitTarget = {};
|
|
|
|
|
const Widgets::UIEditorTreeViewItem* hitItem =
|
|
|
|
|
ResolveHitItem(layout, items, point, &hitTarget);
|
|
|
|
|
if (hitItem != nullptr &&
|
|
|
|
|
hitTarget.kind == UIEditorTreeViewHitTargetKind::Row) {
|
|
|
|
|
return hitItem->itemId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResetDropTarget(State& state) const {
|
|
|
|
|
state.dropTargetItemId.clear();
|
|
|
|
|
state.dropToRoot = false;
|
2026-04-23 14:09:28 +08:00
|
|
|
state.dropPlacement = UIEditorTreeViewDropPreviewPlacement::None;
|
2026-04-19 02:48:41 +08:00
|
|
|
state.validDropTarget = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateDropTarget(
|
|
|
|
|
State& state,
|
|
|
|
|
std::string_view draggedItemId,
|
|
|
|
|
const UIPoint& point) const {
|
|
|
|
|
UIEditorTreeViewHitTarget hitTarget = {};
|
|
|
|
|
const Widgets::UIEditorTreeViewItem* hitItem =
|
|
|
|
|
ResolveHitItem(layout, items, point, &hitTarget);
|
|
|
|
|
if (hitItem != nullptr &&
|
|
|
|
|
(hitTarget.kind == UIEditorTreeViewHitTargetKind::Row ||
|
|
|
|
|
hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure)) {
|
2026-04-23 14:09:28 +08:00
|
|
|
UIEditorTreeViewDropPreviewPlacement placement =
|
|
|
|
|
UIEditorTreeViewDropPreviewPlacement::OnItem;
|
|
|
|
|
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
|
|
|
|
|
placement =
|
|
|
|
|
hitTarget.kind == UIEditorTreeViewHitTargetKind::Disclosure
|
|
|
|
|
? UIEditorTreeViewDropPreviewPlacement::OnItem
|
|
|
|
|
: ResolveRowDropPlacement(layout, hitTarget, point);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 02:48:41 +08:00
|
|
|
state.dropTargetItemId = hitItem->itemId;
|
2026-04-23 14:09:28 +08:00
|
|
|
state.dropPlacement = placement;
|
|
|
|
|
switch (placement) {
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::BeforeItem:
|
|
|
|
|
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
|
|
|
|
|
state.validDropTarget =
|
|
|
|
|
callbacks.CanInsertBeforeItem(
|
|
|
|
|
draggedItemId,
|
|
|
|
|
state.dropTargetItemId);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::AfterItem:
|
|
|
|
|
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
|
|
|
|
|
state.validDropTarget =
|
|
|
|
|
callbacks.CanInsertAfterItem(
|
|
|
|
|
draggedItemId,
|
|
|
|
|
state.dropTargetItemId);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::OnItem:
|
|
|
|
|
state.validDropTarget =
|
|
|
|
|
callbacks.CanDropOnItem(draggedItemId, state.dropTargetItemId);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-04-19 02:48:41 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-23 14:09:28 +08:00
|
|
|
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
|
|
|
|
|
UIEditorTreeViewDropPreviewPlacement gapPlacement =
|
|
|
|
|
UIEditorTreeViewDropPreviewPlacement::None;
|
|
|
|
|
const Widgets::UIEditorTreeViewItem* gapItem =
|
|
|
|
|
ResolveGapInsertionItem(layout, items, point, gapPlacement);
|
|
|
|
|
if (gapItem != nullptr &&
|
|
|
|
|
gapPlacement != UIEditorTreeViewDropPreviewPlacement::None) {
|
|
|
|
|
state.dropTargetItemId = gapItem->itemId;
|
|
|
|
|
state.dropPlacement = gapPlacement;
|
|
|
|
|
state.validDropTarget =
|
|
|
|
|
gapPlacement == UIEditorTreeViewDropPreviewPlacement::BeforeItem
|
|
|
|
|
? callbacks.CanInsertBeforeItem(
|
|
|
|
|
draggedItemId,
|
|
|
|
|
state.dropTargetItemId)
|
|
|
|
|
: callbacks.CanInsertAfterItem(
|
|
|
|
|
draggedItemId,
|
|
|
|
|
state.dropTargetItemId);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 02:48:41 +08:00
|
|
|
if (ContainsPoint(bounds, point)) {
|
|
|
|
|
state.dropToRoot = true;
|
2026-04-23 14:09:28 +08:00
|
|
|
state.dropPlacement = UIEditorTreeViewDropPreviewPlacement::Root;
|
2026-04-19 02:48:41 +08:00
|
|
|
state.validDropTarget = callbacks.CanDropToRoot(draggedItemId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsItemSelected(std::string_view itemId) const {
|
|
|
|
|
return callbacks.IsItemSelected(itemId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SelectDraggedItem(std::string_view itemId) const {
|
|
|
|
|
return callbacks.SelectDraggedItem(itemId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CommitDrop(State& state, DragDropInteraction::ProcessResult&) {
|
|
|
|
|
droppedToRoot = state.dropToRoot;
|
2026-04-23 14:09:28 +08:00
|
|
|
switch (state.dropPlacement) {
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::BeforeItem:
|
|
|
|
|
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
|
|
|
|
|
return callbacks.CommitInsertBeforeItem(
|
|
|
|
|
state.draggedItemId,
|
|
|
|
|
state.dropTargetItemId);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::AfterItem:
|
|
|
|
|
if constexpr (SupportsSiblingInsertion<Callbacks>()) {
|
|
|
|
|
return callbacks.CommitInsertAfterItem(
|
|
|
|
|
state.draggedItemId,
|
|
|
|
|
state.dropTargetItemId);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::Root:
|
|
|
|
|
return callbacks.CommitDropToRoot(state.draggedItemId);
|
|
|
|
|
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::OnItem:
|
|
|
|
|
return callbacks.CommitDropOnItem(
|
2026-04-19 02:48:41 +08:00
|
|
|
state.draggedItemId,
|
|
|
|
|
state.dropTargetItemId);
|
2026-04-23 14:09:28 +08:00
|
|
|
|
|
|
|
|
case UIEditorTreeViewDropPreviewPlacement::None:
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-04-19 02:48:41 +08:00
|
|
|
}
|
|
|
|
|
} adaptedCallbacks{ layout, items, bounds, callbacks };
|
|
|
|
|
|
|
|
|
|
ProcessResult result = {};
|
2026-04-22 18:37:05 +08:00
|
|
|
const std::vector<DragDropInteraction::UIInputEvent> filteredInputEvents =
|
|
|
|
|
FilterPointerInputEvents(inputEvents, suppressPointerInput);
|
2026-04-19 02:48:41 +08:00
|
|
|
const DragDropInteraction::ProcessResult interactionResult =
|
|
|
|
|
DragDropInteraction::ProcessInputEvents(
|
|
|
|
|
state,
|
2026-04-22 18:37:05 +08:00
|
|
|
filteredInputEvents,
|
2026-04-19 02:48:41 +08:00
|
|
|
adaptedCallbacks,
|
|
|
|
|
dragThreshold);
|
|
|
|
|
result.selectionForced = interactionResult.selectionForced;
|
|
|
|
|
result.dropCommitted = interactionResult.dropCommitted;
|
|
|
|
|
result.draggedItemId = interactionResult.draggedItemId;
|
|
|
|
|
result.dropTargetItemId = interactionResult.dropTargetItemId;
|
|
|
|
|
result.droppedToRoot = adaptedCallbacks.droppedToRoot;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace XCEngine::UI::Editor::Collections::TreeDragDrop
|