refactor(new_editor): tighten app dependency boundaries
This commit is contained in:
201
new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h
Normal file
201
new_editor/include/XCEditor/Collections/UIEditorTreeDragDrop.h
Normal file
@@ -0,0 +1,201 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user