From 51d1a72e9b2261084fa918a1d33c65afc73e3ad3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 16 Oct 2023 15:41:14 +0200 Subject: [PATCH] Docking: Revised undocking logic to reduce accidental whole-node undocking: Amend bb2aa5e77. Revert to normal threshold in StartMouseMovingWindowOrNode(). Added tooltip when hovering the collapse/window menu button. --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 28 ++++++++++++++-------------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 2 +- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f7f564c9..5535c440 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -157,6 +157,14 @@ Docking+Viewports Branch: - Viewports: Fixed window inner clipping rectangle off by one when window is located on a monitor with negative coordinates. While it is expected that other small issues with arise from this situation, at the moment we are fixing the most noticeable one. (#6861, #2884) [@Vuhdo, @alektron] +- Docking: revised undocking to reduce accidental whole-node undocking: + - cannot undock a whole node by dragging from empty space in tab-bar. + - can undock whole node by dragging from window/collapse menu button. + - can undock single window by dragging from its tab. + - can still move (but not undock) whole node or whole hierarchy when node is part of a + floating hierarchy. + - added tooltip when hovering the collapse/window menu button, to faciliate understanding + that whole dock node may be undocked or grabbed from here. - Docking: Fixed an issue leading to incorrect restoration of selected tab in dock nodes that don't carry the currently focused window. (#2304) - Docking: added ImGuiDockNodeFlags_NoUndocking. (#2999, #6823, #6780, #3492) diff --git a/imgui.cpp b/imgui.cpp index 145994e4..00949985 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3657,7 +3657,8 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { ImGuiLocKey_WindowingPopup, "(Popup)" }, { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, { ImGuiLocKey_DockingHideTabBar, "Hide tab bar###HideTabBar" }, - { ImGuiLocKey_DockingHoldShiftToDock, "Hold SHIFT to enable Docking window."}, + { ImGuiLocKey_DockingHoldShiftToDock, "Hold SHIFT to enable Docking window." }, + { ImGuiLocKey_DockingDragToUndockOrMoveNode,"Click and drag to move or undock whole node." }, }; void ImGui::Initialize() @@ -4489,26 +4490,23 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) g.MovingWindow = window; } -// We use 'undock_floating_node == false' when dragging from title bar to allow moving groups of floating nodes without undocking them. -// - undock_floating_node == true: when dragging from a floating node within a hierarchy, always undock the node. -// - undock_floating_node == false: when dragging from a floating node within a hierarchy, move root window. -void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node) +// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them. +void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock) { ImGuiContext& g = *GImGui; bool can_undock_node = false; - if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0) + if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0) { // Can undock if: - // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy) - // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows) + // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window) + // - part of a dockspace node hierarchy: so we can undock the last single visible node too (trivia: undocking from a fixed/central node will create a new node and copy windows) ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that. - if (undock_floating_node || root_node->IsDockSpace()) - can_undock_node = true; + can_undock_node = true; } const bool clicked = IsMouseClicked(0); - const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f); + const bool dragging = IsMouseDragging(0); if (can_undock_node && dragging) DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window) @@ -6445,7 +6443,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren)) node->WantHiddenTabBarToggle = true; else if (held && IsMouseDragging(0)) - StartMouseMovingWindowOrNode(window, node, true); + StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); @@ -16887,6 +16885,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w OpenPopup("#WindowMenu"); if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f) + SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode)); } // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value @@ -17015,7 +17015,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiID title_bar_id = host_window->GetID("#TITLEBAR"); if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) { - // AllowItem mode required for appending into dock node tab bar, + // AllowOverlap mode required for appending into dock node tab bar, // otherwise dragging window will steal HoveredId and amended tabs cannot get them. bool held; KeepAliveID(title_bar_id); @@ -17031,7 +17031,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Forward moving request to selected window if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) - StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); + StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space } } diff --git a/imgui_internal.h b/imgui_internal.h index 5062519c..36d54676 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1929,6 +1929,7 @@ enum ImGuiLocKey : int ImGuiLocKey_WindowingUntitled, ImGuiLocKey_DockingHideTabBar, ImGuiLocKey_DockingHoldShiftToDock, + ImGuiLocKey_DockingDragToUndockOrMoveNode, ImGuiLocKey_COUNT }; @@ -3162,7 +3163,7 @@ namespace ImGui IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs); IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); - IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node); + IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 01d14327..a2ee8a7e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -862,7 +862,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) - StartMouseMovingWindowOrNode(window, dock_node, true); + StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button return pressed; }