Imgui: Docking a window permanently

Created on 25 May 2019  路  17Comments  路  Source: ocornut/imgui

Version/Branch of Dear ImGui:

Version: 1.71
Branch: docking

Back-end/Renderer/OS:

Back-ends: imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp
Operating System: Windows 10

My Issue/Question:

I have been looking through some of the code, comments and issues but haven't found anything relevant to this particular feature, or maybe I missed it. Is there anyway to dock a node permanently? I am looking for a way to create a toolbar the will rest below the menu bar, so it would be nice to dock it on top of everything else and disallow un-docking for that particular window, while allowing all other windows to be moved around as usual.

docking

Most helpful comment

So while I was not able to find anything directly related to permanent docking I did manage to find a workaround.

What I did was to add ImGuiDockNodeFlags_NoTabBar to the LocalFlags of the ImGuiDockNode* that I was interested in, which has the side effect that you subsequently cannot un-dock the window, although it necessarily disables the tab bar if that's something you wanted to keep, which is why this is just a workaround.

My dockspace code looks like this now:

ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_None;
ImGuiID dockspaceID = ImGui::GetID(ID().c_str());
if (!ImGui::DockBuilderGetNode(dockspaceID)) {
    ImGui::DockBuilderRemoveNode(dockspaceID);
    ImGui::DockBuilderAddNode(dockspaceID, ImGuiDockNodeFlags_None);

    ImGuiID dock_main_id = dockspaceID;
    ImGuiID dock_up_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, 0.05f, nullptr, &dock_main_id);
    ImGuiID dock_right_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.2f, nullptr, &dock_main_id);
    ImGuiID dock_left_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.2f, nullptr, &dock_main_id);
    ImGuiID dock_down_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.2f, nullptr, &dock_main_id);
    ImGuiID dock_down_right_id = ImGui::DockBuilderSplitNode(dock_down_id, ImGuiDir_Right, 0.6f, nullptr, &dock_down_id);

    ImGui::DockBuilderDockWindow("Actions", dock_up_id);
    ImGui::DockBuilderDockWindow("Hierarchy", dock_right_id);
    ImGui::DockBuilderDockWindow("Inspector", dock_left_id);
    ImGui::DockBuilderDockWindow("Console", dock_down_id);
    ImGui::DockBuilderDockWindow("Project", dock_down_right_id);
    ImGui::DockBuilderDockWindow("Scene", dock_main_id);

        // Disable tab bar for custom toolbar
    ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_up_id);
    node->LocalFlags |= ImGuiDockNodeFlags_NoTabBar;

    ImGui::DockBuilderFinish(dock_main_id);
}
ImGui::DockSpace(dockspaceID, ImVec2(0.0f, 0.0f), dockspaceFlags);

Although now I am also wondering if there is any way to remove the tab bar menu arrow next to the tab for a window, as well as maybe the x exit button on the right of each tab window, in case we want to style the window a bit differently.

All 17 comments

So while I was not able to find anything directly related to permanent docking I did manage to find a workaround.

What I did was to add ImGuiDockNodeFlags_NoTabBar to the LocalFlags of the ImGuiDockNode* that I was interested in, which has the side effect that you subsequently cannot un-dock the window, although it necessarily disables the tab bar if that's something you wanted to keep, which is why this is just a workaround.

My dockspace code looks like this now:

ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_None;
ImGuiID dockspaceID = ImGui::GetID(ID().c_str());
if (!ImGui::DockBuilderGetNode(dockspaceID)) {
    ImGui::DockBuilderRemoveNode(dockspaceID);
    ImGui::DockBuilderAddNode(dockspaceID, ImGuiDockNodeFlags_None);

    ImGuiID dock_main_id = dockspaceID;
    ImGuiID dock_up_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, 0.05f, nullptr, &dock_main_id);
    ImGuiID dock_right_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.2f, nullptr, &dock_main_id);
    ImGuiID dock_left_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.2f, nullptr, &dock_main_id);
    ImGuiID dock_down_id = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.2f, nullptr, &dock_main_id);
    ImGuiID dock_down_right_id = ImGui::DockBuilderSplitNode(dock_down_id, ImGuiDir_Right, 0.6f, nullptr, &dock_down_id);

    ImGui::DockBuilderDockWindow("Actions", dock_up_id);
    ImGui::DockBuilderDockWindow("Hierarchy", dock_right_id);
    ImGui::DockBuilderDockWindow("Inspector", dock_left_id);
    ImGui::DockBuilderDockWindow("Console", dock_down_id);
    ImGui::DockBuilderDockWindow("Project", dock_down_right_id);
    ImGui::DockBuilderDockWindow("Scene", dock_main_id);

        // Disable tab bar for custom toolbar
    ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_up_id);
    node->LocalFlags |= ImGuiDockNodeFlags_NoTabBar;

    ImGui::DockBuilderFinish(dock_main_id);
}
ImGui::DockSpace(dockspaceID, ImVec2(0.0f, 0.0f), dockspaceFlags);

Although now I am also wondering if there is any way to remove the tab bar menu arrow next to the tab for a window, as well as maybe the x exit button on the right of each tab window, in case we want to style the window a bit differently.

Hello,

The workaround looks appropriate.

If you wanted to show tab-bar this would widen the question: do you want to disable undocking on a per-docking-node basis (anything docked on this tab-bar), or disable undocking of a particular window (maybe through a window flag).

I'm happy with you using the workaround, considering that DockBuilderXXX is current draft for what ideally would become a public API.

Although now I am also wondering if there is any way to remove the tab bar menu arrow next to the tab for a window, as well as maybe the x exit button on the right of each tab window, in case we want to style the window a bit differently.

I am not sure I understand, you would want to show the tab-bar with multiple docked windows _but_ disable the docking/tab menu? At this point if undocking is not permitted you may as well disable the docking tab bar (as you did) and create a tab bar yourself without those elements. But I appreciate this would necessate using a different API (BeginTabBar()) explicitely.

I could add flags to disable the Docking/Menu button and Close button on a per DockNode basis.

PS: I think to clarify that currently you are supposed to call DockBuilderSetNodeSize() on your initial node, at the moment if you don't, then DockBuilderSplitNode() will split a little differently than what you may be expecting (not getting into details here but I'll work on fixing this).

I have pushed two flags in imgui_internal.h to do what is discussed above.

ImGuiDockNodeFlags_NoWindowMenuButton
ImGuiDockNodeFlags_NoCloseButton

(There was quite a few inconsistency in wording "collapse button" vs "docking button" vs "tab list button" which I have been rewording as "window menu button", a term that is compatible with the Master branch and will allow us to add features in both. Basically in a docking node the window menu button replace the collapse button).

Thanks for looking into this Omar.

I guess for this specific situation I would only need to disable undocking for an entire window, since this window is not meant to be docked into (it should only have a single tab) so disabling undocking on a per docking-node basis would not make much sense here.

I think hiding the tab bar for this particular window is the way to go, but I also noticed that it only works as expected when first launching the application. After first launch and after the imgui.ini file has been created, even after setting the ImGuiDockNodeFlags_NoTabBar flag in the application the tab bar mysteriously appears again. I'm thinking the .ini settings are overriding any of the set docking node flags. Tried setting the ImGuiWindowFlags_NoSavedSettings flag on the window but it didn't seem to do the trick.

Either way thanks for adding the discussed window menu button and close button flags, they can definitely be useful in some cases.

PS: I think to clarify that currently you are supposed to call DockBuilderSetNodeSize() on your initial node, at the moment if you don't, then DockBuilderSplitNode() will split a little differently than what you may be expecting (not getting into details here but I'll work on fixing this).

From what I understood here DockBuilderSplitNode() allows you to split a node with a given resizing ratio, which is what I am currently trying out, although DockBuilderSetNodeSize() might give a bit more granularity in the resizing, but currently the ratio parameter seems to do a good enough job.

I guess for this specific situation I would only need to disable undocking for an entire window, since this window is not meant to be docked into (it should only have a single tab) so disabling undocking on a per docking-node basis would not make much sense here.

It's easier to do it on a per-node basis (mostly because window flags are too scarce to be nilly-willy allocated) and your setup requires you to setup a docking node tho?

I think hiding the tab bar for this particular window is the way to go, but I also noticed that it only works as expected when first launching the application. After first launch and after the imgui.ini file has been created, even after setting the ImGuiDockNodeFlags_NoTabBar flag in the application the tab bar mysteriously appears again. I'm thinking the .ini settings are overriding any of the set docking node flags. Tried setting the ImGuiWindowFlags_NoSavedSettings flag on the window but it didn't seem to do the trick.

Sorry, we indeed don't save any of those flag in the .ini file presently. I completely ommitted that fact. I can start adding some of those flags for saving.

Either way thanks for adding the discussed window menu button and close button flags, they can definitely be useful in some cases.

Hmm, you asked for them above? If they are not useful to you I'll tempted to remove them.

From what I understood here DockBuilderSplitNode() allows you to split a node with a given resizing ratio, which is what I am currently trying out, although DockBuilderSetNodeSize() might give a bit more granularity in the resizing, but currently the ratio parameter seems to do a good enough job.

There's an issue bug somewhere related to how we handle central node and minimum size and I'm pretty sure in some situation it would give you results that are very off the ratio you gave. If you ever stumble on that issue it'll probably be the reason.

Sorry, we indeed don't save any of those flag in the .ini file presently. I completely ommitted that fact. I can start adding some of those flags for saving.

I have now pushed code to save the NoTabBar, NoWindowMenuButton, NoCloseButton flags in the .ini file. They were simply overlooked as all those flags have been recently added.

Note that in the Metrics window under Docking you can manipulate the local flags of dock node to test around the behavior of each:

image

Yes I think I'll definitely find use for the newly added NoWindowMenuButton/NoCloseButton flags. Thanks again for that.

There's an issue bug somewhere related to how we handle central node and minimum size and I'm pretty sure in some situation it would give you results that are very off the ratio you gave. If you ever stumble on that issue it'll probably be the reason.

Good to know I didn't know this might happen. In that case I'll probably start using the DockBuilderSetNodeSize() calls, it seems like a more flexible solution to boot.

I have now pushed code to save the NoTabBar, NoWindowMenuButton, NoCloseButton flags in the .ini file. They were simply overlooked as all those flags have been recently added.

And thanks for looking into this. I shall pull the latest version and test this out.

I have now pushed code to save the NoTabBar, NoWindowMenuButton, NoCloseButton flags in the .ini file. They were simply overlooked as all those flags have been recently added.

By the way, as an addendum, I noticed that the ImGuiDockNodeFlags_NoResize flag is also not saved. Can you also add this one to the .ini file?

Thanks again for everything!

The ImGuiDockNodeFlags_NoResize when passed to DockSpace() propagates from the root node so normally you would set it at runtime and it would apply to all nodes.

You can indeed set it manually for a single node by writing in LocalFlags but I have a suspicion it isn't easy to use in this manner as the node tree isn't always obvious to the end-user. I added code to save _NoResize in LocalFlags, I would appreciate if you have feedback of whether it works in your context.

Just pulled and tested out the new commit but it seems the _NoResize flag isn't getting saved to the .ini file. After setting it for a node in the application and looking in the .ini file this is what I see, for that specific node
DockNode ID=0x00000001 Parent=0xB863DB2D SizeRef=0,47 NoTabBar=1 HiddenTabBar=1

In my case (at least for now) I have a very specific setup where the custom top toolbar should not be un-docked or resized. Works perfectly on first run but the only thing missing now is the resizing on subsequent runs.

Looking at the previous commit for the NoTabBar/NoWindowMenuButton/NoCloseButton flag additions, and comparing to the current commit, I believe you are missing these lines

somewhere after line 14094:

    if (sscanf(line, " NoResize=%d%n", &x, &r) == 1)                { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; }

and somewhere after line 14166:

if (node_settings->Flags & ImGuiDockNodeFlags_NoResize)
            buf->appendf(" NoResize=1");

That's exactly what the latest commit in docking adds so I'm not sure you've pulled latest?

Ah my mistake must have been looking at another commit. Yes I pulled the latest and I do see it has those changes, but it seems the _NoResize flag is still not ending up in the .ini file, even after doing
node->LocalFlags |= ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_NoResize;

It is for me and when testing with the Metrics>Docking section so there must be something different in your case. As a rule of thumb, anything related to docking is always much more complicated that in appears to be, so careful repro needs to be done.

Skimming at the code I just found a bug (I think unrelated to yours since you are not splitting your node, but DockBuilderRemoveNode() overwrite whole parent LocalFlags when removing a child node that's the central node, which is incorrect, should be move the CentralNode flag, not overwrite the whole sets of flag. Anyway this should not affect you).

Yes my mistake again, I forgot to copy in imgui_internal.h into my project when I pulled, which had the ImGuiDockNodeFlags_NoResize added to the _SavedFlagsMask_. Thank you, everything's working as expected now (y)

I forgot to copy in imgui_internal.h into my project when I pulled

(Ouch, you should probably avoid using such error-prone process in the first place. Pulling should be pulling and done, not some manual error-prone sync step!)

Closing this now!

Hello @gitbetter,

I will probably need to redesign some of the things that were added for this thread, and I'll need
references of how they are used. Is your main project the one publicly hosted at https://github.com/gitbetter/Zenith ?
Namely: https://github.com/gitbetter/Zenith/blob/23533b7de21fc8a6fd4303659da12381a112b1a4/Editor/_Source/ZEditor.cpp#L310

Can you think of any other more advanced use you made of the system?

Hey @ocornut,

Yes that's the project in question. At the moment, that is really the only place where I was in need of the docking behavior. Haven't come up with any more complex use cases as of yet. Feel free to use the project as is.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

the-lay picture the-lay  路  3Comments

KaungZawHtet picture KaungZawHtet  路  3Comments

ILoveImgui picture ILoveImgui  路  3Comments

noche-x picture noche-x  路  3Comments

NPatch picture NPatch  路  3Comments