ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
58.04k stars 9.94k forks source link

Having trouble understanding how to pre-dock windows. #5778

Open antrwells opened 1 year ago

antrwells commented 1 year ago

Hi,

I am writing an app in C++, and I can't seem to understand how to pre-dock windows. I can drag and dock just fine, but that's all.

Here is a small bit of code,

ImGui::DockSpaceOverViewport(ImGui::GetMainViewport());

which enables docking. I can drag and drop any of my windows properly. But I can't figure out how to make them docked at first. My windows code is quite simple.

`if (!mSGF) {

    ImGui::SetNextWindowPos(mSceneGraphPos);
    ImGui::SetNextWindowSize(mSceneGraphSize);
    mSGF = true;
    ImGui::SetNextWindowDockID(ImGui::GetWindowDockID(), ImGuiDir_Right);
}

ImGui::Begin("Scene Graph", &mSceneGraphOpen, ImGuiWindowFlags_MenuBar);
`

Can you point me to an example of how to achieve this?

antrwells commented 1 year ago

Btw, by predocking, I mean docking the scene graph to the left, the content to the bottom, the editor to the right, and the viewport to central node. I've been searching for hours, but to no avail.

ocornut commented 1 year ago

In pseudo code you tried to write should look more like:

ImGuiID my_dockspace = ImGui::DockspaceOverViewport(…);
…
ImGui::SetNextWindowDockID(my_dockspace, ImGuiCond_Once);

(GetWindowDockID() gets the dock node id of the current window which is undefined in your snippet (and likely is the implicit/hidden “Debug” window))

However that api doesn’t allow any form of splitting. There is a wip DockBuilderXXX api in imgui_internal.h for that.

ocornut commented 1 year ago

Also you shouldn’t call SetNextWindowPos/Size along with SetNextWindowDockID(), the earlier two will undock the window.

JerryYan97 commented 1 year ago

Hi, it looks like I am also facing a similar problem with predocking/initial docking. With the code below, it looks like the text in the window cannot be rendered out.

...
ImGuiID dockspace_id = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::DockBuilderRemoveNodeChildNodes(dockspace_id); // clear any previous layout
ImGuiID dock_id_left, dock_id_right;

ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.5f, &dock_id_left, &dock_id_right);

ImGui::Begin("Window_1", &open, 0);
ImGui::Text("Text 1");
ImGui::End();

ImGui::Begin("Window_2", &open, 0);
ImGui::Text("Text 2");
ImGui::End();

ImGui::DockBuilderDockWindow("Window_1", dock_id_left);
ImGui::DockBuilderDockWindow("Window_2", dock_id_right);

ImGui::DockBuilderFinish(dockspace_id);
...

image

Please let me know if there is anything wrong with my code/usage. I have spent quite a bit of my time on reading previous issues relating to create such a window but it looks like they are too old for the API now.😄 It would also be helpful if you can point me to some up-to-dated examples with such functionalities. (I believe the current docking example in the imgui_demo doesn't have such demonstration for splitting)

JerryYan97 commented 1 year ago

Hi, sorry for bothering. It looks like this issue maybe helpful: https://github.com/ocornut/imgui/issues/4033

In general, the strategy here can be different. Instead of using the BuildDockSplit(), we can just use the .ini format to do the pre-docking. This .ini can be created manually beforehand.

E.g. This works for me:

const char myLayout[] = 
"[Window][DockSpaceViewport_11111111]\n\
Pos=0,0\n\
Size=1280,640\n\
Collapsed=0\n\
\n\
[Window][Debug##Default]\n\
Pos=60,60\n\
Size=400,400\n\
Collapsed=0\n\
\n\
[Window][Window 1]\n\
Pos=0,0\n\
Size=637,640\n\
Collapsed=0\n\
DockId=0x00000001,0\n\
\n\
[Window][Window 2]\n\
Pos=639,0\n\
Size=641,640\n\
Collapsed=0\n\
DockId=0x00000002,0\n\
\n\
DockSpace   ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,0 Size=1280,640 Split=X Selected=0x82C5531C\n\
  DockNode  ID = 0x00000001 Parent = 0x8B93E3BD SizeRef = 637, 640 Selected = 0xC56529CC\n\
  DockNode  ID = 0x00000002 Parent = 0x8B93E3BD SizeRef = 641, 640 CentralNode = 1 Selected = 0x82C5531C";

...

ImGui::LoadIniSettingsFromMemory(myLayout);

...

ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();

...

Hopefully this can be helpful. Besides, I am thinking this maybe worth to be mentioned in the docking Wiki because this functionality is really attractive to docking users and it would take people some time to figure this out if people are relatively new to the Dear ImGUI.

Btw, the .ini file is automatically generated by default by the Dear ImGUI.

ocornut commented 1 year ago

Please let me know if there is anything wrong with my code/usage.

ImGuiID dockspace_id = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::DockBuilderRemoveNodeChildNodes(dockspace_id); // clear any previous layout

You are killing and recreating the docking setup every frame, preventing any interactions with the docking system. You need to create layout once e.g. on init. Basically you can use e.g. DockBuilderGetNode(id) and only create layout if this doesn't exist.

I don't think you can use DockSpaceOverViewport() because it doesn't allow you to get the dockspace ID BEFORE creating. So instead

Hopefully this can be helpful. Besides, I am thinking this maybe worth to be mentioned in the docking Wiki because this functionality is really attractive to docking users and it would take people some time to figure this out if people are relatively new to the Dear ImGUI.

I disagree. This is a XY Solution and dead-end, people are misunderstanding and underestimating the details of what it means to have a long term persist layout. Even I haven't cracked the correct model for it (may need to put an emphase on linking new window to an existing window instead of a node, so occasional windows don't have to be setup ahead of time). Please use the dock builder and I simultaneously agree the API needs to be reworked and improved.

lambwheit commented 1 year ago

Hi, i don't know if you still need this but here is working code that solves your issue, you only have to init the nodes once

...
    ImGuiID dockspace_id = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
    static bool init = true;
    ImGuiID dock_id_left, dock_id_right;
    if (init) {
        init = false;
        ImGui::DockBuilderRemoveNode(dockspace_id);
        ImGui::DockBuilderAddNode(dockspace_id);
        ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->Size);

        ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.5f, &dock_id_left, &dock_id_right);
        ImGui::DockBuilderDockWindow("Window_1", dock_id_left);
        ImGui::DockBuilderDockWindow("Window_2", dock_id_right);

        ImGui::DockBuilderFinish(dockspace_id);
    }
    ImGui::Begin("Window_1");
    ImGui::Text("Text 1");
    ImGui::End();
    ImGui::Begin("Window_2");
    ImGui::Text("Text 2");
    ImGui::End();
...

image