From 291bfe68418abe19bc7acb24a9dd2fb6d60368db Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 17 Sep 2018 18:32:10 +0200 Subject: [PATCH] Docking: work to allow programmatic control of dock nodes, various refactor + assert fix. Probably broke something (but I haven't found what yet!) --- imgui.cpp | 241 +++++++++++++++++++++++++++++++++++------------ imgui_demo.cpp | 2 +- imgui_internal.h | 6 ++ 3 files changed, 186 insertions(+), 63 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 04dd1b24..f924df56 100644 --- a/imgui.cpp +++ b/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 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) { diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d49bf797..6cbf0cdb 100644 --- a/imgui_demo.cpp +++ b/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))); diff --git a/imgui_internal.h b/imgui_internal.h index 975eba98..ef0fee62 100644 --- a/imgui_internal.h +++ b/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();