#include "Platform/Win32/WindowManager/TabDragDropTarget.h" #include namespace XCEngine::UI::Editor::App::Internal { namespace { using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; bool IsPointInsideRect(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; } std::size_t ResolveDropInsertionIndex( const Widgets::UIEditorDockHostTabStackLayout& tabStack, const UIPoint& point) { if (!IsPointInsideRect(tabStack.tabStripLayout.headerRect, point)) { return Widgets::UIEditorTabStripInvalidIndex; } std::size_t insertionIndex = 0u; for (const UIRect& rect : tabStack.tabStripLayout.tabHeaderRects) { const float midpoint = rect.x + rect.width * 0.5f; if (point.x > midpoint) { ++insertionIndex; } } return insertionIndex; } UIEditorWorkspaceDockPlacement ResolveDropPlacement( const Widgets::UIEditorDockHostTabStackLayout& tabStack, const UIPoint& point) { if (IsPointInsideRect(tabStack.tabStripLayout.headerRect, point)) { return UIEditorWorkspaceDockPlacement::Center; } const float leftDistance = point.x - tabStack.bounds.x; const float rightDistance = tabStack.bounds.x + tabStack.bounds.width - point.x; const float topDistance = point.y - tabStack.bounds.y; const float bottomDistance = tabStack.bounds.y + tabStack.bounds.height - point.y; const float minHorizontalThreshold = tabStack.bounds.width * 0.25f; const float minVerticalThreshold = tabStack.bounds.height * 0.25f; const float nearestEdge = (std::min)( (std::min)(leftDistance, rightDistance), (std::min)(topDistance, bottomDistance)); if (nearestEdge == leftDistance && leftDistance <= minHorizontalThreshold) { return UIEditorWorkspaceDockPlacement::Left; } if (nearestEdge == rightDistance && rightDistance <= minHorizontalThreshold) { return UIEditorWorkspaceDockPlacement::Right; } if (nearestEdge == topDistance && topDistance <= minVerticalThreshold) { return UIEditorWorkspaceDockPlacement::Top; } if (nearestEdge == bottomDistance && bottomDistance <= minVerticalThreshold) { return UIEditorWorkspaceDockPlacement::Bottom; } return UIEditorWorkspaceDockPlacement::Center; } } // namespace EditorWindowTabDragDropTarget ResolveEditorWindowTabDragDropTarget( const Widgets::UIEditorDockHostLayout& layout, const UIPoint& point) { EditorWindowTabDragDropTarget result = {}; if (!IsPointInsideRect(layout.bounds, point)) { return result; } const Widgets::UIEditorDockHostHitTarget hitTarget = Widgets::HitTestUIEditorDockHost(layout, point); for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { if ((!hitTarget.nodeId.empty() && tabStack.nodeId != hitTarget.nodeId) || !IsPointInsideRect(tabStack.bounds, point)) { continue; } result.valid = true; result.nodeId = tabStack.nodeId; result.placement = ResolveDropPlacement(tabStack, point); if (result.placement == UIEditorWorkspaceDockPlacement::Center) { result.insertionIndex = ResolveDropInsertionIndex(tabStack, point); if (result.insertionIndex == Widgets::UIEditorTabStripInvalidIndex) { result.insertionIndex = tabStack.items.size(); } } return result; } for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { if (!IsPointInsideRect(tabStack.bounds, point)) { continue; } result.valid = true; result.nodeId = tabStack.nodeId; result.placement = ResolveDropPlacement(tabStack, point); if (result.placement == UIEditorWorkspaceDockPlacement::Center) { result.insertionIndex = tabStack.items.size(); } return result; } return result; } } // namespace XCEngine::UI::Editor::App::Internal