imgui
imgui copied to clipboard
How to programmatically dock a window A to another window B?
My Issue/Question:
I can't figure out how to programmatically dock a window to another.
I have the following code which configures the global docking space. it works well:
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
window_flags |= ImGuiWindowFlags_NoBackground;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("DockSpace", nullptr, window_flags);
ImGui::PopStyleVar();
ImGui::PopStyleVar(2);
// DockSpace
ImGuiIO &io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
static auto first_time = true;
if (first_time)
{
first_time = false;
ImGui::DockBuilderRemoveNode(dockspace_id); // clear any previous layout
ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
auto dock_id_right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.25f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("Control Panel", dock_id_right);
ImGui::DockBuilderDockWindow("Relight window", dockspace_id);
ImGui::DockBuilderFinish(dockspace_id);
}
}
ImGui::End();
But I also want to dock a window A to another window B's docking area. I can't find any examples, nor could I find an API to get window B's docking area's ID. I tried the following:
bool open = false;
ImGui::SetNextWindowSize(ImVec2(m_initialWidth, m_initialHeight), ImGuiCond_Once);
ImGui::Begin(m_title.c_str() );
ImVec2 availableSize = ImGui::GetContentRegionAvail();
ImGuiID dockspace_id = ImGui::GetID("RelightDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
static auto first_time = true;
if (first_time)
{
first_time = false;
ImGui::DockBuilderRemoveNode(dockspace_id); // clear any previous layout
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspace_id, availableSize);
auto dock_id_right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.25f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("Debug window", dock_id_right);
ImGui::DockBuilderFinish(dockspace_id);
}
if ( ImGui::IsMouseReleased( ImGuiMouseButton_Left) || m_width == 0 ){
// std::cout << availableSize.x << " " << availableSize.y << std::endl;
// should I resize my FBO here
if (availableSize.x != m_width || availableSize.y != m_height)
{
m_width = availableSize.x;
m_height = availableSize.y;
m_isFrameBufferDirty = true;
onResize(m_width, m_height);
}
}
//draw FBO
ImGui::Image((void *)m_frameBufferTextureID, availableSize, ImVec2(0, 1),ImVec2(1, 0));
ImGui::End();
This doesn't work at all. I expect the window named "Debug window" should appear within the current window. But the above code seems to create another docking area for the current window, the new docking area pushes everything outside the window's boundary. and the "Debug window " is also not docked.
Screenshots/Video
What I want to achieve:

what I see with the above code:

There are other things I can't make sense,
why do we need to remove existing layout when creating a docking area when the docking area is freshly created?
Do you mean something like this?
//--------------------------------------------------------------
void ofApp::dockingReset1()
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
//ImGuiID dockspace_id = ImGui::GetID("DockSpace");
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode;
ImGui::DockBuilderRemoveNode(dockspace_id); // clear any previous layout
ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
// split the dockspace into 2 nodes --
// DockBuilderSplitNode takes in the following args in the following order
// window ID to split, direction, fraction (between 0 and 1),
// the final two setting let's us choose which id we want (which ever one we DON'T set as NULL,
// will be returned by the function)
// out_id_at_dir is the id of the node in the direction we specified earlier,
// out_id_at_opposite_dir is in the opposite direction
auto dock_id_top = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.2f, nullptr, &dockspace_id);
auto dock_id_down = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Down, 0.25f, nullptr, &dockspace_id);
auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.2f, nullptr, &dockspace_id);
auto dock_id_right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.15f, nullptr, &dockspace_id);
//auto dock_id_left2 = ImGui::DockBuilderSplitNode(dock_id_left, ImGuiDir_Down, 0.2f, nullptr, &dock_id_left);
//auto dock_id_down2 = ImGui::DockBuilderSplitNode(dock_id_down, ImGuiDir_Right, 0.15f, nullptr, &dock_id_down);
// we now dock our windows into the docking node we made above
ImGui::DockBuilderDockWindow("Window 1", dock_id_top);
ImGui::DockBuilderDockWindow("Window 2", dock_id_right);
ImGui::DockBuilderDockWindow("Window 3", dock_id_left);
ImGui::DockBuilderDockWindow("Window 4", dock_id_down);
ImGui::DockBuilderDockWindow("Window 0", dock_id_top);
ImGui::DockBuilderFinish(dockspace_id);
}
From: https://github.com/moebiussurfing/ofxSurfingImGui/blob/master/3_Docking/3_0_Layout_Docking2/src/ofApp.cpp#L324-L361
A Guide for Docking: https://gist.github.com/moebiussurfing/d7e6ec46a44985dd557d7678ddfeda99
I don't quite understand this line
ImGuiViewport* viewport = ImGui::GetMainViewport();
I want to dock a window a within another window b. is MainViewport the global docking area? or the dockspace of the window b?
Sorry, viewport is not being used here.... I think there's more than one way of creating the main container. (borderless, transparent etc), using ImGui::DockSpace( or the ImGuiDockNodeFlags_PassthruCentralNode flag.
The point to splitting is here:
auto dock_id_top = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.2f, nullptr, &dockspace_id);
auto dock_id_down = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Down, 0.25f, nullptr, &dockspace_id);
auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.2f, nullptr, &dockspace_id);
auto dock_id_right = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.15f, nullptr, &dockspace_id);
//auto dock_id_left2 = ImGui::DockBuilderSplitNode(dock_id_left, ImGuiDir_Down, 0.2f, nullptr, &dock_id_left);
//auto dock_id_down2 = ImGui::DockBuilderSplitNode(dock_id_down, ImGuiDir_Right, 0.15f, nullptr, &dock_id_down);
// we now dock our windows into the docking node we made above
ImGui::DockBuilderDockWindow("Window 1", dock_id_top);
ImGui::DockBuilderDockWindow("Window 2", dock_id_right);
ImGui::DockBuilderDockWindow("Window 3", dock_id_left);
ImGui::DockBuilderDockWindow("Window 4", dock_id_down);
ImGui::DockBuilderDockWindow("Window 0", dock_id_top);
"Window 1" "Window 2"... etc are the ImGui window names that you will create after.
Just try some of the guide examples: https://gist.github.com/moebiussurfing/8dbc7fef5964adcd29428943b78e45d2 https://gist.github.com/moebiussurfing/d7e6ec46a44985dd557d7678ddfeda99
Hi @ocornut, reading here: https://discourse.dearimgui.org/t/init-a-docked-imgui-window/323/2 as well as here: https://github.com/ocornut/imgui/issues/2109
I'm questioning if this is still not do-able using the builder API?
I used some of @moebiussurfing examples and I'm unsuccessful docking a child window to another child node programmatically.
Here is my current approach for doing regular docking into the main view port (that works):
static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
ImGuiViewport * viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) {
window_flags |= ImGuiWindowFlags_NoBackground;
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("DockSpace", nullptr, window_flags);
ImGui::PopStyleVar();
ImGui::PopStyleVar(2);
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
//ImGuiID dockspace_id = ImGui::GetMainViewport()->ID;
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
static auto first_time = true;
if (first_time) {
first_time = false;
ImGui::DockBuilderRemoveNode(dockspace_id);
ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.2f, nullptr, &dockspace_id);
auto dock_id_down = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Down, 0.25f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("Left", dock_id_left);
ImGui::DockBuilderDockWindow("Down", dock_id_down);
ImGui::DockBuilderFinish(dockspace_id);
}
ImGui::End();
ImGui::Begin("Left");
ImGui::Text("Hello, left!");
ImGui::End();
ImGui::Begin("Down");
ImGui::Text("Hello, down!");
ImGui::End();
If I want, I can also dock one window into another doing this change:
auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.2f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("Left", dock_id_left);
ImGui::DockBuilderDockWindow("Down", dock_id_left);
That works. But what If I want to dock a child window into another window that is not docked into the main viewport? I tried changing the dockspace to target the child window like this:
ImGuiID dockspace_id = ImGui::GetID("Left");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
static auto first_time = true;
if (first_time) {
first_time = false;
ImGui::DockBuilderRemoveNode(dockspace_id);
ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size);
auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.2f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("Down", dock_id_left);
ImGui::DockBuilderFinish(dockspace_id);
}
ImGui::End();
This doesn't work unfortunately. I was under the assumption that every window has its own dockspace?
I found a workaround, but still isn't great...:
ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
//ImGuiID dockspace_id = ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(300, 400));
ImGui::Begin("Dashboard");
ImGui::PopStyleVar();
ImGuiID dockspace_id = ImGui::GetID("DockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f));
static auto first_time = true;
if (first_time) {
first_time = false;
ImGui::DockBuilderRemoveNode(dockspace_id);
ImGui::DockBuilderAddNode(dockspace_id);
ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->Size);
auto dock_id_up = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Up, 0.5f, nullptr, &dockspace_id);
auto dock_id_down = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Down, 0.5f, nullptr, &dockspace_id);
ImGui::DockBuilderDockWindow("Up", dock_id_up);
ImGui::DockBuilderDockWindow("Down", dock_id_down);
ImGui::DockBuilderFinish(dockspace_id);
}
ImGui::End();
ImGui::Begin("Up");
ImGui::Text("Hello, up!");
ImGui::End();
ImGui::Begin("Down");
ImGui::Text("Hello, down!");
ImGui::End();
So instead of docking one child window into another child window, what I'm doing here is creating a "Dashboard" like dockspace in addition to the dockspace in the main viewport. That allows me to dock child windows in there.
@ocornut, generally I think it would have been much friendlier if every window was having its own dockspace by default when docking is enabled (ImGuiConfigFlags_DockingEnable).

Here is the outcome:

and snippet I call right after setting imgui frame:
void CN3UIDebug::RenderDockSpace() {
ImGui::DockSpaceOverViewport(ImGui::GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);
ImGui::Begin(IMGUI_WND_ID_DASHBOARD);
ImGuiID dsId = ImGui::GetID("DashboardDS");
if (!ImGui::DockBuilderGetNode(dsId)) {
ImGui::SetWindowSize(ImVec2(370.0f, 680.0f));
ImGui::SetWindowPos(ImVec2(640.0f, 55.0f));
ImGui::DockBuilderRemoveNode(dsId);
ImGui::DockBuilderAddNode(dsId);
ImGuiID dsIdCopy = dsId;
ImGuiID dsIdUp = ImGui::DockBuilderSplitNode(dsIdCopy, ImGuiDir_Up, 0.34f, NULL, &dsIdCopy);
ImGuiID dsIdDown = ImGui::DockBuilderSplitNode(dsIdCopy, ImGuiDir_Down, 0.0f, NULL, &dsIdCopy);
ImGui::DockBuilderDockWindow(IMGUI_WND_ID_FPS, dsIdUp);
ImGui::DockBuilderDockWindow(IMGUI_WND_ID_METRICS, dsIdDown);
ImGui::DockBuilderDockWindow(IMGUI_WND_ID_DEMO, dsIdDown);
ImGui::DockBuilderFinish(dsId);
}
ImGui::DockSpace(dsId);
ImGui::End();
}
Note that I call ImGui::DockSpace last in order to check if the user changed the default docking layout (imgui.ini). That way if the user first launch the app, it will setup the default layout unless it was changed by the user.
I think this might be the answer:
void split()
{
ImGuiID parent_node=ImGui::DockBuilderAddNode();
ImGui::DockBuilderSetNodePos(parent_node, ImGui::GetWindowPos());
ImGui::DockBuilderSetNodeSize(parent_node, ImGui::GetWindowSize());
ImGuiID nodeA;
ImGuiID nodeB;
ImGui::DockBuilderSplitNode(parent_node, ImGuiDir_Up, 0.8f, &nodeB, &nodeA);
ImGui::DockBuilderDockWindow("A", nodeA);
ImGui::DockBuilderDockWindow("B", nodeB);
}
This code should be called inside Begin End of window B, or you need to provide size and position somehow.