diff --git a/imgui.cpp b/imgui.cpp index cc715503..67812dd6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10896,11 +10896,11 @@ namespace ImGui static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); - static bool DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); + static void DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); static ImRect DockNodeCalcTabBarRect(const ImGuiDockNode* node); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); - static bool DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking); + static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } static int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } static int DockNodeGetTabOrder(ImGuiWindow* window); @@ -11439,6 +11439,25 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) MarkIniSettingsDirty(); } +// This is mostly used for automation. +bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) +{ + if (split_outer) + { + IM_ASSERT(0); + } + else + { + ImGuiDockPreviewData split_data; + DockNodePreviewDockCalc(target, target_node, payload, &split_data, false, split_outer); + if (split_data.DropRectsDraw[split_dir+1].IsInverted()) + return false; + *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); + return true; + } + return false; +} + //----------------------------------------------------------------------------- // Docking: ImGuiDockNode //----------------------------------------------------------------------------- @@ -12408,7 +12427,7 @@ void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& po } // Retrieve the drop rectangles for a given direction or for the center + perform hit testing. -bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking) +bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos) { ImGuiContext& g = *GImGui; @@ -12440,12 +12459,15 @@ bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& ou else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); } else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); } + if (test_mouse_pos == NULL) + return false; + ImRect hit_r = out_r; if (!outer_docking) { // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides hit_r.Expand(ImFloor(hs_w * 0.30f)); - ImVec2 mouse_delta = (g.IO.MousePos - c); + ImVec2 mouse_delta = (*test_mouse_pos - c); float mouse_delta_len2 = ImLengthSqr(mouse_delta); float r_threshold_center = hs_w * 1.4f; float r_threshold_sides = hs_w * (1.4f + 1.2f); @@ -12454,14 +12476,13 @@ bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& ou if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides) return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y)); } - return hit_r.Contains(g.IO.MousePos); + return hit_r.Contains(*test_mouse_pos); } // host_node may be NULL if the window doesn't have a DockNode already. -static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) +static void ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes // There is an edge case when docking into a dockspace which only has inactive nodes. // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. @@ -12503,7 +12524,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo continue; if (dir != ImGuiDir_None && !data->IsSidesAvailable) continue; - if (DockNodeCalcDropRects(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking)) + if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos)) { data->SplitDir = (ImGuiDir)dir; data->IsSplitDirExplicit = true; @@ -12531,13 +12552,12 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->FutureNode.Size = size_new; data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio); } - - return data->IsSplitDirExplicit; } static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data) { ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent. // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes. @@ -13684,8 +13704,11 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) ImGuiDockPreviewData* split_data = &split_inner; if (node && (node->ParentNode || node->IsCentralNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) - if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) + { + DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true); + if (split_outer.IsSplitDirExplicit) split_data = &split_outer; + } DockNodePreviewDockCalc(window, node, payload_window, &split_inner, is_explicit_target, false); if (split_data == &split_outer) split_inner.IsDropAllowed = false; diff --git a/imgui_internal.h b/imgui_internal.h index 86736088..d4692d4e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1678,6 +1678,7 @@ namespace ImGui IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); + IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window);