Docking: Misc rework/rename toward being able to rebuild a branch selectively, so we can honor settings changes on a per Dockspace basis. + Comments

This commit is contained in:
omar 2018-09-14 14:56:36 +02:00
parent e32256b4b4
commit ba7b68798d

122
imgui.cpp
View File

@ -3350,7 +3350,7 @@ void ImGui::NewFrame()
g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
// Undocking
// (needs to be before UpdateMovingWindow so the window is already offset and following the mouse on the detaching frame)
// (needs to be before UpdateMouseMovingWindow so the window is already offset and following the mouse on the detaching frame)
DockContextNewFrameUpdateUndocking(g.DockContext);
// Find hovered window
@ -9614,9 +9614,8 @@ struct ImGuiDockContext
ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes
ImVector<ImGuiDockRequest> Requests;
ImVector<ImGuiDockNodeSettings> SettingsNodes;
int SettingsMaxDepth;
bool WantFullRebuild;
ImGuiDockContext() { SettingsMaxDepth = 0; WantFullRebuild = false; }
ImGuiDockContext() { WantFullRebuild = false; }
};
//-----------------------------------------------------------------------------
@ -9632,10 +9631,10 @@ namespace ImGui
static void DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer);
static void DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req);
static void DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window);
static void DockContextClearNodes(ImGuiDockContext* ctx, bool clear_references);
static void DockContextGcUnusedNodes(ImGuiDockContext* ctx);
static void DockContextBuildNodesFromSettings(ImGuiDockContext* ctx);
static void DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx);
static void DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx);
static void DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all
static void DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count);
static void DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID root_id); // Use root_id==0 to add all
// ImGuiDockNode
static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window);
@ -9666,7 +9665,7 @@ namespace ImGui
static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos);
// Settings
static void DockSettingsRemoveAllReferencesToNode(ImGuiID node_id);
static void DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count);
static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiDockContext* ctx, ImGuiID node_id);
static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
@ -9676,6 +9675,13 @@ namespace ImGui
//-----------------------------------------------------------------------------
// Docking: ImGuiDockContext
//-----------------------------------------------------------------------------
// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings,
// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active.
// At boot time only, we run a simple GC to remove nodes that have no references.
// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures),
// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does).
// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler setttings data.
//-----------------------------------------------------------------------------
void ImGui::DockContextInitialize(ImGuiContext* imgui_context)
{
@ -9699,10 +9705,7 @@ void ImGui::DockContextShutdown(ImGuiContext* imgui_context)
ImGuiDockContext* ctx = g.DockContext;
for (int n = 0; n < ctx->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p)
{
node->ChildNodes[0] = node->ChildNodes[1] = NULL;
IM_DELETE(node);
}
IM_DELETE(g.DockContext);
g.DockContext = NULL;
}
@ -9711,25 +9714,29 @@ void ImGui::DockContextOnLoadSettings()
{
ImGuiContext& g = *GImGui;
ImGuiDockContext* ctx = g.DockContext;
DockContextGcUnusedNodes(ctx);
DockContextBuildNodesFromSettings(ctx);
DockContextGcUnusedSettingsNodes(ctx);
DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size);
}
// This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch
void ImGui::DockContextRebuild(ImGuiDockContext* ctx)
{
//IMGUI_DEBUG_LOG("[docking] full rebuild\n");
SaveIniSettingsToMemory();
DockContextClearNodes(ctx, false);
DockContextBuildNodesFromSettings(ctx);
DockContextBuildAddWindowsToNodes(ctx);
ImGuiID root_id = 0; // Rebuild all
DockContextClearNodes(ctx, root_id, false);
DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size);
DockContextBuildAddWindowsToNodes(ctx, root_id);
}
// Docking context update function, called by NewFrame()
void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx)
{
ImGuiContext& g = *GImGui;
if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
{
if (ctx->Nodes.Data.Size > 0 || ctx->Requests.Size > 0)
DockContextClearNodes(ctx, true);
DockContextClearNodes(ctx, 0, true);
return;
}
@ -9743,12 +9750,13 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx)
ctx->WantFullRebuild = false;
}
// Process Undocking requests (called from NewFrame before UpdateMovingWindow)
// Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindow call in NewFrame)
for (int n = 0; n < ctx->Requests.Size; n++)
if (ctx->Requests[n].Type == ImGuiDockRequestType_Undock)
DockContextProcessUndock(ctx, ctx->Requests[n].UndockTarget);
}
// Docking context update function, called by NewFrame()
void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx)
{
ImGuiContext& g = *GImGui;
@ -9769,6 +9777,7 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx)
DockNodeUpdate(node);
}
// Docking context update function, called by EndFrame()
void ImGui::DockContextEndFrame(ImGuiDockContext* ctx)
{
(void)ctx;
@ -9784,7 +9793,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiDockContext* ctx, ImGuiID i
// Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window.
if (id == (ImGuiID)-1)
{
// FIXME-OPT: This is very suboptimal, however the node count is small enough not to be a worry.
// FIXME-OPT: This is suboptimal, even if the node count is small enough not to be a worry. We could poke in ctx->Nodes to find a suitable ID faster.
id = 0x0001;
while (DockContextFindNodeByID(ctx, id) != NULL)
id++;
@ -9819,30 +9828,46 @@ static void ImGui::DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* n
}
}
// Stress/functional test to make sure we can rebuild from scratch without a glitch
void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, bool clear_references)
void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references)
{
SaveIniSettingsToMemory(NULL);
ImGuiContext& g = *GImGui;
for (int n = 0; n < ctx->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p)
IM_DELETE(node);
ctx->Nodes.Clear();
ctx->Requests.clear();
// 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_references)
if (clear_persistent_docking_references)
window->DockId = 0;
}
}
static void ImGui::DockContextGcUnusedNodes(ImGuiDockContext* ctx)
// Clear nodes
for (int n = 0; n < ctx->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p)
{
bool want_removal = (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id);
if (want_removal)
{
IM_DELETE(node);
ctx->Nodes.Data[n].val_p = NULL;
}
}
if (root_id == 0)
ctx->Nodes.Clear();
ctx->Requests.clear();
}
static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx)
{
ImGuiContext& g = *GImGui;
IM_ASSERT(g.Windows.Size == 0);
// Count reference to dock ids from window settings
ImGuiStorage ref_count_map; // Map dock_id -> counter
@ -9870,19 +9895,19 @@ static void ImGui::DockContextGcUnusedNodes(ImGuiDockContext* ctx)
remove |= (ref_count == 0 && settings->ParentID == 0 && is_parent_map.GetInt(settings->ID, 0) == 0); // Leaf nodes with 0 window
if (remove)
{
DockSettingsRemoveAllReferencesToNode(settings->ID);
DockSettingsRemoveReferencesToNodes(&settings->ID, 1);
settings->ID = 0;
}
}
}
}
static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx)
static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count)
{
// Build nodes
for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++)
for (int node_n = 0; node_n < node_settings_count; node_n++)
{
ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n];
ImGuiDockNodeSettings* node_settings = &node_settings_array[node_n];
if (node_settings->ID == 0)
continue;
ImGuiDockNode* node = DockContextAddNode(ctx, node_settings->ID);
@ -9902,19 +9927,21 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx)
}
}
void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx)
void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID root_id)
{
// Rebuild nodes (they can also lazily rebuild but we'll have a visible glitch during the first frame)
// Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame)
ImGuiContext& g = *GImGui;
for (int n = 0; n < g.Windows.Size; n++)
{
ImGuiWindow* window = g.Windows[n];
if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1)
continue;
if (window->DockNode != NULL)
continue;
ImGuiDockNode* dock_node = DockContextFindNodeByID(ctx, window->DockId);
if (dock_node == NULL)
dock_node = DockContextAddNode(ctx, window->DockId);
IM_ASSERT(dock_node != NULL); // This should have been called after DockContextBuildNodesFromSettings()
if (root_id == 0 || DockNodeGetRootNode(dock_node)->ID == root_id)
DockNodeAddWindow(dock_node, window);
}
}
@ -10089,9 +10116,9 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id)
ImGuiDockNode::~ImGuiDockNode()
{
IM_ASSERT(ChildNodes[0] == NULL && ChildNodes[1] == NULL);
IM_DELETE(TabBar);
TabBar = NULL;
ChildNodes[0] = ChildNodes[1] = NULL;
}
int ImGui::DockNodeGetTabOrder(ImGuiWindow* window)
@ -11546,17 +11573,22 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window)
// Docking: Settings
//-----------------------------------------------------------------------------
static void ImGui::DockSettingsRemoveAllReferencesToNode(ImGuiID node_id)
// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings
static void ImGui::DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count)
{
ImGuiContext& g = *GImGui;
int found = 0;
for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) // FIXME-OPT: We could remove this loop by storing the index in the map
{
ImGuiWindowSettings* window_settings = &g.SettingsWindows[settings_n];
if (window_settings->DockId == node_id)
for (int node_n = 0; node_n < node_ids_count; node_n++)
if (window_settings->DockId == node_ids[node_n])
{
window_settings->DockId = 0;
window_settings->DockOrder = -1;
if (++found < node_ids_count)
break;
return;
}
}
}
@ -11617,6 +11649,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSe
static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiDockNode* node, int depth)
{
ImGuiDockNodeSettings node_settings;
IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3)));
node_settings.ID = node->ID;
node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0;
node_settings.SelectedTabID = node->SelectedTabID;
@ -11629,7 +11662,6 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiD
node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y);
node_settings.LastExplicitSize = ImVec2ih((short)node->LastExplicitSize.x, (short)node->LastExplicitSize.y);
ctx->SettingsNodes.push_back(node_settings);
ctx->SettingsMaxDepth = ImMax(ctx->SettingsMaxDepth, depth);
if (node->ChildNodes[0])
DockSettingsHandler_DockNodeToSettings(ctx, node->ChildNodes[0], depth + 1);
if (node->ChildNodes[1])
@ -11639,21 +11671,25 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiD
static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
{
// Gather settings data
// (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer)
ImGuiDockContext* ctx = imgui_ctx->DockContext;
ctx->SettingsNodes.resize(0);
ctx->SettingsNodes.reserve(ctx->Nodes.Data.Size);
ctx->SettingsMaxDepth = 0;
for (int n = 0; n < ctx->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p)
if (node->IsRootNode())
DockSettingsHandler_DockNodeToSettings(ctx, node, 0);
int max_depth = 0;
for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++)
max_depth = ImMax((int)ctx->SettingsNodes[node_n].Depth, max_depth);
// Write to text buffer
buf->appendf("[%s][Data]\n", handler->TypeName);
for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++)
{
const ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n];
buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (ImMax(ctx->SettingsMaxDepth,0) - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file
buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file
buf->appendf(" ID=0x%08X", node_settings->ID);
if (node_settings->ParentID)
buf->appendf(" Parent=0x%08X LastExplicitSize=%d,%d", node_settings->ParentID, node_settings->LastExplicitSize.x, node_settings->LastExplicitSize.y);
@ -12561,7 +12597,7 @@ void ImGui::ShowDockingDebug()
if (ImGui::TreeNode("Dock nodes"))
{
if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, true); }
if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, 0, true); }
ImGui::SameLine();
if (ImGui::SmallButton("Rebuild all")) { ctx->WantFullRebuild = true; }
for (int n = 0; n < ctx->Nodes.Data.Size; n++)