#pragma once #include #include #include #include namespace XCEngine::UI { struct UIPoint; } namespace XCEngine::UI::Editor::App::Win32::Internal { struct CrossWindowDockDropTarget { bool valid = false; std::string nodeId = {}; UIEditorWorkspaceDockPlacement placement = UIEditorWorkspaceDockPlacement::Center; std::size_t insertionIndex = Widgets::UIEditorTabStripInvalidIndex; }; inline bool TryResolveCrossWindowDockDropTarget( const Widgets::UIEditorDockHostLayout& layout, const ::XCEngine::UI::UIPoint& point, CrossWindowDockDropTarget& outTarget) { using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; const auto isPointInsideRect = [](const UIRect& rect, const UIPoint& targetPoint) { return targetPoint.x >= rect.x && targetPoint.x <= rect.x + rect.width && targetPoint.y >= rect.y && targetPoint.y <= rect.y + rect.height; }; const auto resolveInsertionIndex = [&](const Widgets::UIEditorDockHostTabStackLayout& tabStack) { 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; }; const auto resolvePlacement = [&](const Widgets::UIEditorDockHostTabStackLayout& tabStack) { 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; }; outTarget = {}; if (!isPointInsideRect(layout.bounds, point)) { return false; } 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; } outTarget.valid = true; outTarget.nodeId = tabStack.nodeId; outTarget.placement = resolvePlacement(tabStack); if (outTarget.placement == UIEditorWorkspaceDockPlacement::Center) { outTarget.insertionIndex = resolveInsertionIndex(tabStack); if (outTarget.insertionIndex == Widgets::UIEditorTabStripInvalidIndex) { outTarget.insertionIndex = tabStack.items.size(); } } return true; } for (const Widgets::UIEditorDockHostTabStackLayout& tabStack : layout.tabStacks) { if (isPointInsideRect(tabStack.bounds, point)) { outTarget.valid = true; outTarget.nodeId = tabStack.nodeId; outTarget.placement = resolvePlacement(tabStack); if (outTarget.placement == UIEditorWorkspaceDockPlacement::Center) { outTarget.insertionIndex = tabStack.items.size(); } return true; } } return false; } } // namespace XCEngine::UI::Editor::App::Win32::Internal