From e7cc53436706ea1d25ec68f9ed5bb9a7cb08de8e Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Sep 2021 17:57:47 +0200 Subject: [PATCH] Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 43 ++++++++++++++++++++++++++++++++++++++----- imgui_internal.h | 1 + 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0cd8dfc0c..9bd2f0d0f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. +- Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. + The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) - Docking: Fixed manually created floating node with a central node from not hiding when windows are gone. - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() diff --git a/imgui.cpp b/imgui.cpp index 8c7d7c52b..7a0f081f6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12627,6 +12627,7 @@ namespace ImGui static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateForRootNode(ImGuiDockNode* node); static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); + static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); @@ -13301,6 +13302,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - DockNodeFindInfo() // - DockNodeFindWindowByID() // - DockNodeUpdateFlagsAndCollapse() +// - DockNodeUpdateHasCentralNodeFlag() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() @@ -13338,7 +13340,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; - IsFocused = HasCloseButton = HasWindowMenuButton = false; + IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; MarkedForPosSizeWrite = false; } @@ -13625,6 +13627,7 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) + node->HasCentralNodeChild = false; if (node->ChildNodes[0]) DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); if (node->ChildNodes[1]) @@ -13684,6 +13687,25 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) DockNodeUpdateVisibleFlag(node); } +// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames. +static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node) +{ + node->HasCentralNodeChild = false; + if (node->ChildNodes[0]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]); + if (node->IsRootNode()) + { + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } + } +} + static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag @@ -13733,6 +13755,13 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) break; } } + + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } } static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window) @@ -14791,6 +14820,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node)); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) @@ -14914,12 +14944,12 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_0->SizeRef[axis] != 0.0f && child_1->IsCentralNode()) + else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_1->SizeRef[axis] != 0.0f && child_0->IsCentralNode()) + else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -15696,6 +15726,7 @@ void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_docks } } +// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node. void ImGui::DockBuilderFinish(ImGuiID root_id) { ImGuiContext* ctx = GImGui; @@ -15759,6 +15790,7 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu ancestor_node = ancestor_node->ParentNode; } IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); } @@ -16993,11 +17025,12 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) DebugNodeWindow(node->HostWindow, "HostWindow"); DebugNodeWindow(node->VisibleWindow, "VisibleWindow"); BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); - BulletText("Misc:%s%s%s%s%s", + BulletText("Misc:%s%s%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", - node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); + node->WantLockSizeOnce ? " WantLockSizeOnce" : "", + node->HasCentralNodeChild ? " HasCentralNodeChild" : ""); if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) { if (BeginTable("flags", 4)) diff --git a/imgui_internal.h b/imgui_internal.h index a2da0a607..ae0c4efb3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1377,6 +1377,7 @@ struct IMGUI_API ImGuiDockNode bool IsFocused :1; bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; + bool HasCentralNodeChild :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window