DockBuilder로 레이아웃 구성
메쉬·어셈블리·뷰포트가 있는 도구를 ImGui로 만들면, 창을 손으로 하나씩 배치하는 순간 레이아웃이 흔들립니다. 이 글에서는 Docking 을 켠 뒤 DockBuilder 로 Viewport / Hierarchy / Properties / Log 네 영역을 초기 레이아웃으로 고정하는 패턴을 정리합니다.
완성할 예제
예제는 다음을 한 프레임 안에서 구성합니다.
ImGuiConfigFlags_DockingEnable로 도킹 활성화
전체 뷰포트를 덮는 DockSpace 호스트
DockBuilderSplitNode로 좌·우·하단 분할
Viewport / Hierarchy / Properties / Log 창을 각 독에 고정
읽고 나면 “첫 실행 시 CAD형 셸”을 코드로 재현할 수 있습니다.
왜 DockBuilder가 필요한가
일반 사용자는 창을 드래그해 배치할 수 있지만, 첫 실행 기본 배치 는 제품 경험이 됩니다. 수동 SetNextWindowPos만으로는 도킹 관계가 생기지 않고, 리사이즈·모니터 변경에도 약합니다.
관심사는 대략 다음과 같습니다.
도킹 플래그 — IO에 DockingEnable
호스트 — DockSpace / DockSpaceOverViewport
트리 분할 — DockBuilderSplitNode
창 고정 — DockBuilderDockWindow
완료 — DockBuilderFinish
레이아웃 스케치
목표 배치는 전형적인 CAD 셸입니다.
Text
+------------------+------------------+ | | Hierarchy | | Viewport +------------------+ | | Properties | +------------------+------------------+ | Log | +-------------------------------------+
중앙·좌측이 뷰포트, 우측 상단이 계층, 우측 하단이 속성, 하단이 로그입니다.
도킹 활성화와 DockSpace
초기화 시점에 플래그를 켭니다. 매 프레임이 아니라 컨텍스트 생성 직후가 일반적입니다.
Cpp
ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // 필요 시: io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
프레임마다 호스트 윈도우와 DockSpace를 제출합니다.
Cpp
void ShowEditorDockSpace(bool* p_open) { const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); ImGuiWindowFlags host_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::Begin("EditorDockHost", p_open, host_flags); ImGui::PopStyleVar(2); const ImGuiID dockspace_id = ImGui::GetID("EditorDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode); ImGui::End(); // 최초 1회만 레이아웃 빌드 static bool layout_built = false; if (!layout_built) { BuildCadLayout(dockspace_id); layout_built = true; } }
DockBuilder로 분할하기
imgui_internal.h를 포함한 뒤, 노드를 만들고 비율로 자릅니다.
Cpp
#include "imgui_internal.h" void BuildCadLayout(ImGuiID dockspace_id) { ImGui::DockBuilderRemoveNode(dockspace_id); ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->Size); ImGuiID dock_main = dockspace_id; ImGuiID dock_right = ImGui::DockBuilderSplitNode( dock_main, ImGuiDir_Right, 0.28f, nullptr, &dock_main); ImGuiID dock_bottom = ImGui::DockBuilderSplitNode( dock_main, ImGuiDir_Down, 0.22f, nullptr, &dock_main); ImGuiID dock_right_bottom = ImGui::DockBuilderSplitNode( dock_right, ImGuiDir_Down, 0.45f, nullptr, &dock_right); // dock_main → Viewport // dock_right → Hierarchy // dock_right_bottom → Properties // dock_bottom → Log ImGui::DockBuilderDockWindow("Viewport", dock_main); ImGui::DockBuilderDockWindow("Hierarchy", dock_right); ImGui::DockBuilderDockWindow("Properties", dock_right_bottom); ImGui::DockBuilderDockWindow("Log", dock_bottom); ImGui::DockBuilderFinish(dockspace_id); }
창 이름은 이후에 ImGui::Begin("Viewport") 등과 문자열로 일치 해야 합니다. 공백·대소문자가 다르면 독에 붙지 않습니다.
네 창 본문
레이아웃만 잡고 내용은 최소로 둡니다.
Cpp
void DrawEditorWindows() { if (ImGui::Begin("Viewport")) { ImGui::TextUnformatted("3D view (texture / FBO goes here)"); } ImGui::End(); if (ImGui::Begin("Hierarchy")) { ImGui::TextUnformatted("Scene tree"); } ImGui::End(); if (ImGui::Begin("Properties")) { ImGui::TextUnformatted("Selected object fields"); } ImGui::End(); if (ImGui::Begin("Log")) { ImGui::TextUnformatted("Messages..."); } ImGui::End(); }
메인 루프에서는 NewFrame 이후 ShowEditorDockSpace → DrawEditorWindows 순서가 자연스럽습니다.
설계 체크리스트
docking 브랜치(또는 docking이 포함된 빌드)를 링크했는지 확인합니다.
DockingEnable을 컨텍스트 생성 직후 켭니다.
DockSpace 호스트를 도킹 창보다 먼저 제출합니다.
Begin 창 이름과 DockBuilderDockWindow 이름을 동일하게 둡니다.
레이아웃 빌드는 최초 1회 또는 명시적 Reset에서만 수행합니다.
중앙 노드에 뷰포트를 두고, 보조 패널은 가장자리로 보냅니다.
자주 발생하는 문제
창이 독에 붙지 않음
창 이름 불일치, 또는 DockSpace보다 먼저 Begin한 경우입니다. 제출 순서를 바꾸고 문자열을 대조합니다.
분할 비율이 기대와 다름
DockBuilderSplitNode의 비율은 잘려 나가는 쪽 기준입니다. 좌/우/상/하 방향을 바꾼 뒤 비율을 다시 맞춥니다.
매 실행마다 레이아웃이 초기화됨
layout_built 가드를 빼먹었거나, 매 프레임 RemoveNode를 호출하는 경우입니다. Reset 액션과 최초 빌드를 분리합니다.
중앙이 비어 보이거나 클릭이 통과함
PassthruCentralNode와 뷰포트 창의 배경/입력 처리를 함께 설계해야 합니다. 뷰포트에 실제 컨텐츠(Image 등)를 그리는 다음 글로 이어집니다.
공식 위키의 Docking 페이지와 imgui_demo.cpp의 Docking 섹션을 함께 보면 API 조합 예제를 빠르게 확인할 수 있습니다.