Browse Source

Docking: work to allow programmatic control of dock nodes, various refactor + assert fix. Probably broke something (but I haven't found what yet!)

omar 7 years ago
parent
commit
291bfe6841
3 changed files with 186 additions and 63 deletions
  1. 179 62
      imgui.cpp
  2. 1 1
      imgui_demo.cpp
  3. 6 0
      imgui_internal.h

+ 179 - 62
imgui.cpp

@@ -9518,6 +9518,7 @@ void ImGui::EndDragDropTarget()
 // Docking: ImGuiDockNode
 // Docking: ImGuiDockNode Tree manipulation functions
 // Docking: Public Functions (Dockspace, SetWindowDock)
+// Docking: Public Builder Functions
 // Docking: Begin/End Functions (called from Begin/End)
 // Docking: Settings
 //-----------------------------------------------------------------------------
@@ -9553,15 +9554,16 @@ enum ImGuiDockRequestType
 {
     ImGuiDockRequestType_None = 0,
     ImGuiDockRequestType_Dock,
-    ImGuiDockRequestType_Undock
+    ImGuiDockRequestType_Undock,
+    ImGuiDockRequestType_Split                  // Split is the same as Dock but without a DockPayload
 };
 
 struct ImGuiDockRequest
 {
     ImGuiDockRequestType    Type;
-    ImGuiWindow*            DockTarget;           // Destination/Target window to dock into (may be a loose window or a DockNode)
-    ImGuiDockNode*          DockTargetNode;
-    ImGuiWindow*            DockPayload;          // Source/Payload window to dock (may be a loose window or a DockNode)
+    ImGuiWindow*            DockTargetWindow;   // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL)
+    ImGuiDockNode*          DockTargetNode;     // Destination/Target Node to dock into 
+    ImGuiWindow*            DockPayload;        // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
     ImGuiDir                DockSplitDir;
     float                   DockSplitRatio;
     bool                    DockSplitOuter;
@@ -9570,7 +9572,7 @@ struct ImGuiDockRequest
     ImGuiDockRequest()
     {
         Type = ImGuiDockRequestType_None;
-        DockTarget = DockPayload = UndockTarget = NULL;
+        DockTargetWindow = DockPayload = UndockTarget = NULL;
         DockTargetNode = NULL;
         DockSplitDir = ImGuiDir_None;
         DockSplitRatio = 0.5f;
@@ -9627,8 +9629,9 @@ namespace ImGui
     // ImGuiDockContext
     static ImGuiDockNode*   DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id);
     static ImGuiDockNode*   DockContextAddNode(ImGuiContext* ctx, ImGuiID id);
-    static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node);
+    static void             DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node);
     static void             DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer);
+    static void             DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node);
     static void             DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
     static void             DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window);
     static void             DockContextGcUnusedSettingsNodes(ImGuiContext* ctx);
@@ -9654,7 +9657,7 @@ namespace ImGui
     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 ImGuiDockNode*   DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; }
-    static int              DockNodeGetDepth(ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; }
+    static int              DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; }
     static int              DockNodeGetTabOrder(ImGuiWindow* window);
 
     // ImGuiDockNode tree manipulations
@@ -9717,6 +9720,12 @@ void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx)
     DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size);
 }
 
+void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references)
+{
+    DockBuilderRemoveNodeDockedWindows(ctx, root_id, clear_persistent_docking_references);
+    DockBuilderRemoveNodeChildNodes(ctx, root_id);
+}
+
 // This function also acts as a de-facto test to make sure we can rebuild from scratch without a glitch
 void ImGui::DockContextRebuild(ImGuiContext* ctx)
 {
@@ -9807,7 +9816,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
     return node;
 }
 
-static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node)
+static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node)
 {
     ImGuiContext& g = *ctx;
     ImGuiDockContext* dc = ctx->DockContext;
@@ -9815,12 +9824,14 @@ static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node)
     //printf("[%05d] RemoveNode 0x%04X\n", node->ID);
     IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
     IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL);
-    IM_ASSERT(node->Windows.Size == 1 || node->HostWindow->DockNodeAsHost == node);
+    IM_ASSERT(node->Windows.Size == 0);
 
     if (node->HostWindow)
         node->HostWindow->DockNodeAsHost = NULL;
 
-    if (ImGuiDockNode* parent_node = node->ParentNode)
+    ImGuiDockNode* parent_node = node->ParentNode;
+    const bool merge = (merge_sibling_into_parent_node && parent_node != NULL);
+    if (merge)
     {
         IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node);
         ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]);
@@ -9828,45 +9839,80 @@ static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node)
     }
     else
     {
+        for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++)
+            if (parent_node->ChildNodes[n] == node)
+                node->ParentNode->ChildNodes[n] = NULL;
         dc->Nodes.SetVoidPtr(node->ID, NULL);
         IM_DELETE(node);
     }
 }
 
-void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references)
+static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs)
+{
+    const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs;
+    const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs;
+    return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a);
+}
+
+void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id)
 {
-    ImGuiContext& g = *ctx;
     ImGuiDockContext* dc = ctx->DockContext;
-    SaveIniSettingsToMemory(NULL);
 
-    // Clear references in windows
-    for (int n = 0; n < g.Windows.Size; n++)
-    {
-        ImGuiWindow* window = g.Windows[n];
-        bool want_removal = root_id == 0 || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
-        if (want_removal)
-        {
-            window->DockNode = window->DockNodeAsHost = NULL;
-            window->DockIsActive = false;
-            if (clear_persistent_docking_references)
-                window->DockId = 0;
-        }
-    }
+    ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL;
+    if (root_id && root_node == NULL)
+        return;
+    bool has_document_root = false;
 
-    // Clear nodes
+    ImVector<ImGuiDockNode*> nodes_to_remove;
     for (int n = 0; n < dc->Nodes.Data.Size; n++)
         if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p)
         {
-            bool want_removal = (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id);
+            bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id);
             if (want_removal)
             {
-                IM_DELETE(node);
-                dc->Nodes.Data[n].val_p = NULL;
+                if (node->IsDocumentRoot)
+                    has_document_root = true;
+                if (root_id != 0)
+                    DockContextQueueNotifyRemovedNode(ctx, node);
+                if (root_node)
+                    DockNodeMoveWindows(root_node, node);
+                nodes_to_remove.push_back(node);
             }
         }
+
+    // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes
+    if (nodes_to_remove.Size > 1)
+        ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst);
+    for (int n = 0; n < nodes_to_remove.Size; n++)
+        DockContextRemoveNode(ctx, nodes_to_remove[n], false);
+
     if (root_id == 0)
+    {
         dc->Nodes.Clear();
-    dc->Requests.clear();
+        dc->Requests.clear();
+    }
+    else if (has_document_root)
+    {
+        root_node->IsDocumentRoot = true;
+    }
+}
+
+void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references)
+{
+    // Clear references in windows
+    ImGuiContext& g = *ctx;
+    for (int n = 0; n < g.Windows.Size; n++)
+    {
+        ImGuiWindow* window = g.Windows[n];
+        bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id);
+        if (want_removal)
+        {
+            ImGuiID backup_dock_id = window->DockId;
+            DockContextProcessUndock(ctx, window);
+            if (!clear_persistent_docking_references)
+                window->DockId = backup_dock_id;
+        }
+    }
 }
 
 static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx)
@@ -9959,7 +10005,7 @@ void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDo
 {
     ImGuiDockRequest req;
     req.Type = ImGuiDockRequestType_Dock;
-    req.DockTarget = target;
+    req.DockTargetWindow = target;
     req.DockTargetNode = target_node;
     req.DockPayload = payload;
     req.DockSplitDir = split_dir;
@@ -9976,26 +10022,41 @@ void ImGui::DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window)
     ctx->DockContext->Requests.push_back(req);
 }
 
+void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node)
+{
+    ImGuiDockContext* dc = ctx->DockContext;
+    for (int n = 0; n < dc->Requests.Size; n++)
+        if (dc->Requests[n].DockTargetNode == node)
+            dc->Requests[n].Type = ImGuiDockRequestType_None;
+}
+
 void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
 {
+    IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL));
+    IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL);
+
     ImGuiContext& g = *ctx;
-    ImGuiWindow* payload_window = req->DockPayload;
-    ImGuiWindow* target_window = req->DockTarget;
+    ImGuiWindow* payload_window = req->DockPayload;     // Optional
+    ImGuiWindow* target_window = req->DockTargetWindow;
     ImGuiDockNode* target_node = req->DockTargetNode;
 
     // Decide which Tab will be selected at the end of the operation
     ImGuiID next_selected_id = 0;
-    ImGuiDockNode* payload_node = payload_window->DockNodeAsHost;
-    if (payload_node && !payload_node->IsSplitNode())
-        next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
-    if (payload_node == NULL)
-        next_selected_id = payload_window->ID;
+    ImGuiDockNode* payload_node = NULL;
+    if (payload_window)
+    {
+        payload_node = payload_window->DockNodeAsHost;
+        if (payload_node && !payload_node->IsSplitNode())
+            next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId;
+        if (payload_node == NULL)
+            next_selected_id = payload_window->ID;
+    }
 
     // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well
 
     if (target_node)
         IM_ASSERT(target_node->LastFrameAlive < g.FrameCount);
-    if (target_node && target_node == target_window->DockNodeAsHost)
+    if (target_node && target_window && target_node == target_window->DockNodeAsHost)
         IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsDocumentRoot);
 
     // Create new node and add existing window to it
@@ -10037,7 +10098,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
 
     if (target_node != payload_node)
     {
-        // Create tab bar before we call DockNoveMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)
+        // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!)
         if (target_node->Windows.Size > 0 && target_node->TabBar == NULL)
         {
             target_node->TabBar = IM_NEW(ImGuiTabBar)();
@@ -10072,9 +10133,9 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
             {
                 DockNodeMoveWindows(target_node, payload_node);
             }
-            DockContextRemoveNode(ctx, payload_node);
+            DockContextRemoveNode(ctx, payload_node, true);
         }
-        else
+        else if (payload_window)
         {
             // Transfer single window
             target_node->VisibleWindow = payload_window;
@@ -10089,8 +10150,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
 
 void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window)
 {
-    ImGuiContext& g = *ctx;
-    IM_ASSERT(window->LastFrameActive < g.FrameCount);
+    (void)ctx;
     if (window->DockNode)
         DockNodeRemoveWindow(window->DockNode, window, 0);
     else
@@ -10192,24 +10252,25 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
     UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately
     MarkIniSettingsDirty();
 
-    if (node->Windows.Size == 1 && !node->IsDocumentRoot && window->DockId != node->ID)
-    {
-        // Automatic dock node delete themselves if they are not holding at least one tab
-        IM_ASSERT(node->Windows[0] == window);
-        DockContextRemoveNode(&g, node);
-        return;
-    }
-
     bool erased = false;
     for (int n = 0; n < node->Windows.Size; n++)
         if (node->Windows[n] == window)
         {
             node->Windows.erase(node->Windows.Data + n);
+            if (node->TabBar)
+                TabBarRemoveTab(node->TabBar, window->ID);
             erased = true;
             break;
         }
     IM_ASSERT(erased);
 
+    if (node->Windows.Size == 0 && !node->IsDocumentRoot && window->DockId != node->ID)
+    {
+        // Automatic dock node delete themselves if they are not holding at least one tab
+        DockContextRemoveNode(&g, node, true);
+        return;
+    }
+
     if (node->TabBar)
         TabBarRemoveTab(node->TabBar, window->ID);
 
@@ -10254,7 +10315,7 @@ static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode
 static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
 {
     // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered)
-    IM_ASSERT(dst_node != src_node);
+    IM_ASSERT(src_node && dst_node && dst_node != src_node);
     ImGuiTabBar* src_tab_bar = src_node->TabBar;
     if (src_tab_bar != NULL)
         IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size);
@@ -11060,12 +11121,16 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG
     ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
     IM_ASSERT(child_0 && child_1);
     IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
-    IM_ASSERT(parent_node->TabBar == NULL);
-    IM_ASSERT(parent_node->Windows.Size == 0);
+    if (child_0->Windows.Size > 0 || child_1->Windows.Size > 0)
+    {
+        IM_ASSERT(parent_node->TabBar == NULL);
+        IM_ASSERT(parent_node->Windows.Size == 0);
+    }
 
     ImVec2 backup_last_explicit_size = parent_node->LastExplicitSize;
     DockNodeMoveChildNodes(parent_node, merge_lead_child);
-    DockNodeMoveWindows(parent_node, merge_lead_child);
+    DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows
+    DockNodeMoveWindows(parent_node, child_1);
     DockNodeApplyPosSizeToWindows(parent_node);
     parent_node->InitFromFirstWindow = false;
     parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
@@ -11376,6 +11441,57 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags do
     End();
 }
 
+//-----------------------------------------------------------------------------
+// Docking: Builder Functions
+//-----------------------------------------------------------------------------
+// Very early end-user API to manipulate dock nodes.
+//-----------------------------------------------------------------------------
+
+void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiID node_id)
+{
+    ImGuiID window_id = ImHash(window_name, 0);
+    if (ImGuiWindow* window = FindWindowByID(window_id))
+        SetWindowDock(window, node_id, ImGuiCond_Always);
+    else if (ImGuiWindowSettings* settings = FindWindowSettings(window_id))
+        settings->DockId = node_id;
+    else if (ImGuiWindowSettings* settings = CreateNewWindowSettings(window_name))
+        settings->DockId = node_id;
+}
+
+ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other)
+{
+    IM_ASSERT(split_dir != ImGuiDir_None);
+
+    ImGuiDockNode* node = DockContextFindNodeByID(ctx, id);
+    if (node == NULL)
+        return 0;
+
+    IM_ASSERT(!node->IsSplitNode());    // Already Split
+
+    ImGuiDockRequest req;
+    req.Type = ImGuiDockRequestType_Split;
+    req.DockTargetWindow = NULL;
+    req.DockTargetNode = node;
+    req.DockPayload = NULL;
+    req.DockSplitDir = split_dir;
+    req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir);
+    req.DockSplitOuter = false;
+    DockContextProcessDock(ctx, &req);
+
+    ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID;
+    ImGuiID id_other = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID;
+    if (out_id_at_dir)
+        *out_id_at_dir = id_at_dir;
+    if (out_id_other)
+        *out_id_other = id_other;
+    return id_at_dir;
+}
+
+void ImGui::DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id)
+{
+    DockContextBuildAddWindowsToNodes(ctx, root_id);
+}
+
 //-----------------------------------------------------------------------------
 // Docking: Begin/End Functions (called from Begin/End)
 //-----------------------------------------------------------------------------
@@ -11654,7 +11770,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo
     node_settings.ID = node->ID;
     node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0;
     node_settings.SelectedTabID = node->SelectedTabID;
-    node_settings.SplitAxis = (char)node->SplitAxis;
+    node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None;
     node_settings.Depth = (char)depth;
     node_settings.IsExplicitRoot = (char)node->IsExplicitRoot;
     node_settings.IsDocumentRoot = (char)node->IsDocumentRoot;
@@ -11917,7 +12033,7 @@ void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
             g.SettingsDirtyTimer = g.IO.IniSavingRate;
 }
 
-static ImGuiWindowSettings* CreateNewWindowSettings(const char* name)
+ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
 {
     ImGuiContext& g = *GImGui;
     g.SettingsWindows.push_back(ImGuiWindowSettings());
@@ -12056,7 +12172,7 @@ static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*
 {
     ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
     if (!settings)
-        settings = CreateNewWindowSettings(name);
+        settings = ImGui::CreateNewWindowSettings(name);
     return (void*)settings;
 }
 
@@ -12091,7 +12207,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
         ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
         if (!settings)
         {
-            settings = CreateNewWindowSettings(window->Name);
+            settings = ImGui::CreateNewWindowSettings(window->Name);
             window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
         }
         IM_ASSERT(settings->ID == window->ID);
@@ -12121,7 +12237,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
         }
         if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID)
             buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
-        buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
+        if (settings->Size.x != 0.0f || settings->Size.y != 0.0f)
+            buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
         buf->appendf("Collapsed=%d\n", settings->Collapsed);
         if (settings->DockId != 0)
         {

+ 1 - 1
imgui_demo.cpp

@@ -3821,7 +3821,7 @@ struct ExampleAppDocuments
 
     ExampleAppDocuments()
     {
-        Documents.push_back(MyDocument("Radish",              true,  ImVec4(1.0f, 1.0f, 1.0f, 1.0f)));
+        Documents.push_back(MyDocument("Lettuce",             true,  ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
         Documents.push_back(MyDocument("Eggplant",            true,  ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
         Documents.push_back(MyDocument("Carrot",              true,  ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
         Documents.push_back(MyDocument("Tomato",              false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));

+ 6 - 0
imgui_internal.h

@@ -1474,6 +1474,12 @@ namespace ImGui
     IMGUI_API void          SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond);
     IMGUI_API void          ShowDockingDebug();
 
+    IMGUI_API void          DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references = true);
+    IMGUI_API void          DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id);
+    IMGUI_API void          DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id);
+    IMGUI_API ImGuiID       DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other);
+    IMGUI_API void          DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id);
+
     // Drag and Drop
     IMGUI_API bool          BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
     IMGUI_API void          ClearDragDrop();