Files
XCEngine/new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h

202 lines
7.0 KiB
C++

#pragma once
#include <XCEditor/Collections/UIEditorDragDropInteraction.h>
#include <XCEditor/Collections/UIEditorTreeView.h>
#include <XCEngine/UI/Types.h>
#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;
struct State : DragDropInteraction::State {
bool dropToRoot = false;
};
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;
}
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;
}
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,
float dragThreshold = kDefaultDragThreshold) {
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);
std::vector<DragDropInteraction::UIInputEvent> filteredEvents = {};
filteredEvents.reserve(rawEvents.size());
for (const DragDropInteraction::UIInputEvent& event : rawEvents) {
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,
float dragThreshold = kDefaultDragThreshold) {
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;
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)) {
state.dropTargetItemId = hitItem->itemId;
state.validDropTarget =
callbacks.CanDropOnItem(draggedItemId, state.dropTargetItemId);
return;
}
if (ContainsPoint(bounds, point)) {
state.dropToRoot = true;
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;
return state.dropToRoot
? callbacks.CommitDropToRoot(state.draggedItemId)
: callbacks.CommitDropOnItem(
state.draggedItemId,
state.dropTargetItemId);
}
} adaptedCallbacks{ layout, items, bounds, callbacks };
ProcessResult result = {};
const DragDropInteraction::ProcessResult interactionResult =
DragDropInteraction::ProcessInputEvents(
state,
inputEvents,
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