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

This commit is contained in:
omar 2018-09-17 18:32:10 +02:00
parent 35032d41fa
commit 291bfe6841
3 changed files with 186 additions and 63 deletions

241
imgui.cpp
View File

@ -9518,6 +9518,7 @@ void ImGui::EndDragDropTarget()
// Docking: ImGuiDockNode // Docking: ImGuiDockNode
// Docking: ImGuiDockNode Tree manipulation functions // Docking: ImGuiDockNode Tree manipulation functions
// Docking: Public Functions (Dockspace, SetWindowDock) // Docking: Public Functions (Dockspace, SetWindowDock)
// Docking: Public Builder Functions
// Docking: Begin/End Functions (called from Begin/End) // Docking: Begin/End Functions (called from Begin/End)
// Docking: Settings // Docking: Settings
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -9553,15 +9554,16 @@ enum ImGuiDockRequestType
{ {
ImGuiDockRequestType_None = 0, ImGuiDockRequestType_None = 0,
ImGuiDockRequestType_Dock, ImGuiDockRequestType_Dock,
ImGuiDockRequestType_Undock ImGuiDockRequestType_Undock,
ImGuiDockRequestType_Split // Split is the same as Dock but without a DockPayload
}; };
struct ImGuiDockRequest struct ImGuiDockRequest
{ {
ImGuiDockRequestType Type; ImGuiDockRequestType Type;
ImGuiWindow* DockTarget; // Destination/Target window to dock into (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; ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into
ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode) ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional]
ImGuiDir DockSplitDir; ImGuiDir DockSplitDir;
float DockSplitRatio; float DockSplitRatio;
bool DockSplitOuter; bool DockSplitOuter;
@ -9570,7 +9572,7 @@ struct ImGuiDockRequest
ImGuiDockRequest() ImGuiDockRequest()
{ {
Type = ImGuiDockRequestType_None; Type = ImGuiDockRequestType_None;
DockTarget = DockPayload = UndockTarget = NULL; DockTargetWindow = DockPayload = UndockTarget = NULL;
DockTargetNode = NULL; DockTargetNode = NULL;
DockSplitDir = ImGuiDir_None; DockSplitDir = ImGuiDir_None;
DockSplitRatio = 0.5f; DockSplitRatio = 0.5f;
@ -9627,8 +9629,9 @@ namespace ImGui
// ImGuiDockContext // ImGuiDockContext
static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id);
static ImGuiDockNode* DockContextAddNode(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 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 DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req);
static void DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window);
static void DockContextGcUnusedSettingsNodes(ImGuiContext* ctx); 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 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 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 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); static int DockNodeGetTabOrder(ImGuiWindow* window);
// ImGuiDockNode tree manipulations // ImGuiDockNode tree manipulations
@ -9717,6 +9720,12 @@ void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx)
DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); 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 // 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) void ImGui::DockContextRebuild(ImGuiContext* ctx)
{ {
@ -9807,7 +9816,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id)
return node; 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; ImGuiContext& g = *ctx;
ImGuiDockContext* dc = ctx->DockContext; ImGuiDockContext* dc = ctx->DockContext;
@ -9815,12 +9824,14 @@ static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node)
//printf("[%05d] RemoveNode 0x%04X\n", node->ID); //printf("[%05d] RemoveNode 0x%04X\n", node->ID);
IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node);
IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); 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) if (node->HostWindow)
node->HostWindow->DockNodeAsHost = NULL; 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); 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]); 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 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); dc->Nodes.SetVoidPtr(node->ID, NULL);
IM_DELETE(node); 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; ImGuiDockContext* dc = ctx->DockContext;
SaveIniSettingsToMemory(NULL);
// Clear references in windows ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL;
for (int n = 0; n < g.Windows.Size; n++) if (root_id && root_node == NULL)
{ return;
ImGuiWindow* window = g.Windows[n]; bool has_document_root = false;
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;
}
}
// Clear nodes ImVector<ImGuiDockNode*> nodes_to_remove;
for (int n = 0; n < dc->Nodes.Data.Size; n++) for (int n = 0; n < dc->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) 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) if (want_removal)
{ {
IM_DELETE(node); if (node->IsDocumentRoot)
dc->Nodes.Data[n].val_p = NULL; 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) if (root_id == 0)
{
dc->Nodes.Clear(); 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) static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx)
@ -9959,7 +10005,7 @@ void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDo
{ {
ImGuiDockRequest req; ImGuiDockRequest req;
req.Type = ImGuiDockRequestType_Dock; req.Type = ImGuiDockRequestType_Dock;
req.DockTarget = target; req.DockTargetWindow = target;
req.DockTargetNode = target_node; req.DockTargetNode = target_node;
req.DockPayload = payload; req.DockPayload = payload;
req.DockSplitDir = split_dir; req.DockSplitDir = split_dir;
@ -9976,26 +10022,41 @@ void ImGui::DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window)
ctx->DockContext->Requests.push_back(req); 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) 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; ImGuiContext& g = *ctx;
ImGuiWindow* payload_window = req->DockPayload; ImGuiWindow* payload_window = req->DockPayload; // Optional
ImGuiWindow* target_window = req->DockTarget; ImGuiWindow* target_window = req->DockTargetWindow;
ImGuiDockNode* target_node = req->DockTargetNode; ImGuiDockNode* target_node = req->DockTargetNode;
// Decide which Tab will be selected at the end of the operation // Decide which Tab will be selected at the end of the operation
ImGuiID next_selected_id = 0; ImGuiID next_selected_id = 0;
ImGuiDockNode* payload_node = payload_window->DockNodeAsHost; ImGuiDockNode* payload_node = NULL;
if (payload_node && !payload_node->IsSplitNode()) if (payload_window)
next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; {
if (payload_node == NULL) payload_node = payload_window->DockNodeAsHost;
next_selected_id = payload_window->ID; 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 // 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) if (target_node)
IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); 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); IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsDocumentRoot);
// Create new node and add existing window to it // 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) 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) if (target_node->Windows.Size > 0 && target_node->TabBar == NULL)
{ {
target_node->TabBar = IM_NEW(ImGuiTabBar)(); target_node->TabBar = IM_NEW(ImGuiTabBar)();
@ -10072,9 +10133,9 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
{ {
DockNodeMoveWindows(target_node, payload_node); DockNodeMoveWindows(target_node, payload_node);
} }
DockContextRemoveNode(ctx, payload_node); DockContextRemoveNode(ctx, payload_node, true);
} }
else else if (payload_window)
{ {
// Transfer single window // Transfer single window
target_node->VisibleWindow = payload_window; target_node->VisibleWindow = payload_window;
@ -10089,8 +10150,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req)
void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window) void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window)
{ {
ImGuiContext& g = *ctx; (void)ctx;
IM_ASSERT(window->LastFrameActive < g.FrameCount);
if (window->DockNode) if (window->DockNode)
DockNodeRemoveWindow(window->DockNode, window, 0); DockNodeRemoveWindow(window->DockNode, window, 0);
else else
@ -10192,24 +10252,25 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window
UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately
MarkIniSettingsDirty(); 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; bool erased = false;
for (int n = 0; n < node->Windows.Size; n++) for (int n = 0; n < node->Windows.Size; n++)
if (node->Windows[n] == window) if (node->Windows[n] == window)
{ {
node->Windows.erase(node->Windows.Data + n); node->Windows.erase(node->Windows.Data + n);
if (node->TabBar)
TabBarRemoveTab(node->TabBar, window->ID);
erased = true; erased = true;
break; break;
} }
IM_ASSERT(erased); 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) if (node->TabBar)
TabBarRemoveTab(node->TabBar, window->ID); 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) static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node)
{ {
// Insert tabs in the same orders as currently ordered (node->Windows isn't ordered) // 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; ImGuiTabBar* src_tab_bar = src_node->TabBar;
if (src_tab_bar != NULL) if (src_tab_bar != NULL)
IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size); 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]; ImGuiDockNode* child_1 = parent_node->ChildNodes[1];
IM_ASSERT(child_0 && child_1); IM_ASSERT(child_0 && child_1);
IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1);
IM_ASSERT(parent_node->TabBar == NULL); if (child_0->Windows.Size > 0 || child_1->Windows.Size > 0)
IM_ASSERT(parent_node->Windows.Size == 0); {
IM_ASSERT(parent_node->TabBar == NULL);
IM_ASSERT(parent_node->Windows.Size == 0);
}
ImVec2 backup_last_explicit_size = parent_node->LastExplicitSize; ImVec2 backup_last_explicit_size = parent_node->LastExplicitSize;
DockNodeMoveChildNodes(parent_node, merge_lead_child); 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); DockNodeApplyPosSizeToWindows(parent_node);
parent_node->InitFromFirstWindow = false; parent_node->InitFromFirstWindow = false;
parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->VisibleWindow = merge_lead_child->VisibleWindow;
@ -11376,6 +11441,57 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags do
End(); 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) // 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.ID = node->ID;
node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0; node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0;
node_settings.SelectedTabID = node->SelectedTabID; 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.Depth = (char)depth;
node_settings.IsExplicitRoot = (char)node->IsExplicitRoot; node_settings.IsExplicitRoot = (char)node->IsExplicitRoot;
node_settings.IsDocumentRoot = (char)node->IsDocumentRoot; node_settings.IsDocumentRoot = (char)node->IsDocumentRoot;
@ -11917,7 +12033,7 @@ void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
g.SettingsDirtyTimer = g.IO.IniSavingRate; g.SettingsDirtyTimer = g.IO.IniSavingRate;
} }
static ImGuiWindowSettings* CreateNewWindowSettings(const char* name) ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
g.SettingsWindows.push_back(ImGuiWindowSettings()); g.SettingsWindows.push_back(ImGuiWindowSettings());
@ -12056,7 +12172,7 @@ static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*
{ {
ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0));
if (!settings) if (!settings)
settings = CreateNewWindowSettings(name); settings = ImGui::CreateNewWindowSettings(name);
return (void*)settings; 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); ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID);
if (!settings) if (!settings)
{ {
settings = CreateNewWindowSettings(window->Name); settings = ImGui::CreateNewWindowSettings(window->Name);
window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings);
} }
IM_ASSERT(settings->ID == window->ID); 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) 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("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); buf->appendf("Collapsed=%d\n", settings->Collapsed);
if (settings->DockId != 0) if (settings->DockId != 0)
{ {

View File

@ -3821,7 +3821,7 @@ struct ExampleAppDocuments
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("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("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))); Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));

View File

@ -1474,6 +1474,12 @@ namespace ImGui
IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond);
IMGUI_API void ShowDockingDebug(); 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 // Drag and Drop
IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
IMGUI_API void ClearDragDrop(); IMGUI_API void ClearDragDrop();