Docking
ImGui’s native docking lets the user grab a window’s tab and drop it into a
side pane, the bottom pane, or even pop it out to a free-floating window —
all preserved across runs through imgui.ini. The dasImgui boost layer
wraps the C++ surface in two macros: dockspace (the full-viewport
dockable region) and dock_window (each dockable panel inside it). A
small DockBuilder helper seeds a 3-pane initial layout so first-run
users see something meaningful before they start dragging tabs around.
Source: examples/tutorial/docking.das.
Walkthrough
1options gen2
2
3require imgui
4require imgui_app
5require glfw/glfw_boost
6require opengl/opengl_boost
7require live/glfw_live
8require live/live_api
9require live/live_commands
10require live/live_vars
11require live/opengl_live
12require live_host
13require imgui/imgui_live
14require imgui/imgui_boost_runtime
15require imgui/imgui_boost_v2
16require imgui/imgui_widgets_builtin
17require imgui/imgui_docking_builtin
18require imgui/imgui_visual_aids
19
20// =============================================================================
21// TUTORIAL: docking — full-viewport dockspace + draggable Begin/End windows.
22//
23// Three things compose to make dockable UI:
24// 1. `io.ConfigFlags |= ImGuiConfigFlags.DockingEnable` in `init()` — turns
25// on ImGui's docking machinery for the whole session.
26// 2. `dockspace(DOCK_ROOT, (flags=...))` in the frame loop — wraps
27// DockSpaceOverViewport so the whole window becomes a dock target, and
28// captures the returned `state.dock_id` for the layout helper to use.
29// 3. `dock_window(NAME, (text=..., closable=..., flags=...))` per panel —
30// Begin/End-wrapped windows that ImGui dynamically docks/undocks based
31// on the user dragging their tab.
32//
33// The DockBuilder helper (`setup_default_layout`) seeds a 3-pane layout on
34// the first frame so the user sees an arranged UI without having to drag
35// tabs manually. After the first run, dock state persists via ImGui's
36// `imgui.ini` — drag a tab anywhere and the layout sticks.
37//
38// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/docking.das
39// LIVE: daslang-live modules/dasImgui/examples/tutorial/docking.das
40//
41// DRIVE (when running live):
42// curl -X POST -d '{"name":"imgui_undock","args":{"target":"DOCK_ROOT/OUTPUT"}}' localhost:9090/command
43// curl -X POST -d '{"name":"imgui_set_window_pos","args":{"target":"DOCK_ROOT/OUTPUT","value":{"x":580,"y":220,"w":360,"h":220}}}' localhost:9090/command
44// curl -X POST -d '{"name":"imgui_dock_reset","args":{"target":"DOCK_ROOT"}}' localhost:9090/command
45// curl -X POST -d '{"name":"imgui_close","args":{"target":"DOCK_ROOT/OUTPUT"}}' localhost:9090/command
46//
47// Note: dockspace is a [container] that pushes its identifier onto the registry
48// path, so dock_window targets are path-qualified (``DOCK_ROOT/<name>``).
49// =============================================================================
50
51def setup_default_layout(dock_id : uint) {
52 //! Seed a 3-pane layout on the first frame:
53 //! +-----------+--------------------+
54 //! | | |
55 //! | | Source |
56 //! | Explorer | |
57 //! | +--------------------+
58 //! | | |
59 //! | | Output |
60 //! +-----------+--------------------+
61 //! Each pane's window-title string MUST match the corresponding
62 //! `dock_window`'s `text=` field; that's how ImGui ties dockable windows
63 //! to dock nodes.
64 DockBuilderRemoveNode(dock_id)
65 DockBuilderAddDockSpaceNode(dock_id, ImGuiDockNodeFlags.None)
66 let vp = GetMainViewport()
67 DockBuilderSetNodeSize(dock_id, ImVec2(vp.Size.x, vp.Size.y))
68 var left_id : uint = 0u
69 var right_id : uint = 0u
70 DockBuilderSplitNode(dock_id, ImGuiDir.Left, 0.25f, left_id, right_id)
71 var top_id : uint = 0u
72 var bottom_id : uint = 0u
73 DockBuilderSplitNode(right_id, ImGuiDir.Up, 0.6f, top_id, bottom_id)
74 DockBuilderDockWindow("Explorer", left_id)
75 DockBuilderDockWindow("Source", top_id)
76 DockBuilderDockWindow("Output", bottom_id)
77 DockBuilderFinish(dock_id)
78}
79
80[export]
81def init() {
82 live_create_window("dasImgui docking tutorial", 1024, 720)
83 live_imgui_init(live_window)
84 var io & = unsafe(GetIO())
85 io.FontGlobalScale = 1.5
86 // The single flag that turns docking on for the whole session. Without
87 // this, DockSpace() / DockSpaceOverViewport() render nothing.
88 io.ConfigFlags |= ImGuiConfigFlags.DockingEnable
89}
90
91[export]
92def update() {
93 if (!live_begin_frame()) return
94 begin_frame()
95
96 ImGui_ImplOpenGL3_NewFrame()
97 ImGui_ImplGlfw_NewFrame()
98 apply_synth_io_override()
99 NewFrame()
100
101 // Full-viewport dockspace. PassthruCentralNode lets the OS-window
102 // background show through the unsplit center (irrelevant here — we
103 // split everything — but it's the conventional default).
104 dockspace(DOCK_ROOT, (flags = ImGuiDockNodeFlags.PassthruCentralNode)) {
105 // Seed the layout once per session. has_initial_layout flips false on
106 // imgui_dock_reset; the renderer then re-runs setup. dock_id is
107 // captured by the dockspace wrapper before this block runs.
108 if (!DOCK_ROOT.has_initial_layout && DOCK_ROOT.dock_id != 0u) {
109 setup_default_layout(DOCK_ROOT.dock_id)
110 DOCK_ROOT.has_initial_layout = true
111 }
112 dock_window(EXPLORER, (text = "Explorer", closable = false,
113 flags = ImGuiWindowFlags.None)) {
114 text("Files")
115 button(REFRESH_BTN, (text = "Refresh"))
116 }
117 dock_window(SOURCE, (text = "Source", closable = false,
118 flags = ImGuiWindowFlags.None)) {
119 text("// drag tabs to rearrange")
120 text("// — layout sticks via imgui.ini")
121 button(SAVE_BTN, (text = "Save"))
122 }
123 dock_window(OUTPUT, (text = "Output", closable = true,
124 flags = ImGuiWindowFlags.None)) {
125 text("> ready.")
126 button(CLEAR_BTN, (text = "Clear"))
127 }
128 }
129
130 end_of_frame()
131 Render()
132 var w, h : int
133 live_get_framebuffer_size(w, h)
134 glViewport(0, 0, w, h)
135 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
136 glClear(GL_COLOR_BUFFER_BIT)
137 ImGui_ImplOpenGL3_RenderDrawData(GetDrawData())
138
139 live_end_frame()
140}
141
142[export]
143def shutdown() {
144 live_imgui_shutdown()
145 live_destroy_window()
146}
147
148[export]
149def main() {
150 init()
151 while (!exit_requested()) {
152 update()
153 }
154 shutdown()
155}
Requires
Same backend + boost layer as Layout, but layout helpers are replaced by the docking module:
imgui/imgui_docking_builtin— thedockspaceanddock_windowmacros, plus theDockBuilder*bindings cherry-picked from ImGui’s internal API.
The flag that lights it up
ImGui docking is gated by a single io flag. Without it, DockSpace calls
render nothing and dock_window panels behave like ordinary windows:
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable
This goes in init() once per session.
Seeding the layout
ImGui will happily start with every dockable window stacked in a single
tab-bar — the user is expected to drag tabs into place. For a tutorial we
ship a default arrangement via DockBuilder:
DockBuilderRemoveNode(dock_id) // clear any prior state
DockBuilderAddDockSpaceNode(dock_id, flags) // fresh dockspace root
DockBuilderSetNodeSize(dock_id, ImVec2(vp.Size.x, vp.Size.y))
var left_id, right_id : uint
DockBuilderSplitNode(dock_id, ImGuiDir.Left, 0.25f, left_id, right_id)
var top_id, bottom_id : uint
DockBuilderSplitNode(right_id, ImGuiDir.Up, 0.6f, top_id, bottom_id)
DockBuilderDockWindow("Explorer", left_id)
DockBuilderDockWindow("Source", top_id)
DockBuilderDockWindow("Output", bottom_id)
DockBuilderFinish(dock_id)
DockBuilderDockWindow matches by window title string — the same string
you pass to dock_window(NAME, (text = "Explorer")). The boost macro
doesn’t auto-derive the title from the identifier so the binding is
explicit.
The setup is gated on state.has_initial_layout so it runs once per
session — or after imgui_dock_reset flips the flag back to false.
The frame loop
The dockspace(DOCK_ROOT, ...) macro wraps DockSpaceOverViewport —
the dock region is the entire OS window. Inside its block, each
dock_window(NAME, ...) is a Begin/End-wrapped panel that participates
in the docking system. Path-prefixing works the same as containers
(window / child / tab_bar): dock_window(EXPLORER) {
button(REFRESH_BTN, ...) } registers the button under
DOCK_ROOT/EXPLORER/REFRESH_BTN.
The closable = true option (on OUTPUT here) wires the X-button in
the tab to state.open — closing the panel without rebuilding the
layout.
Standalone vs live
Same as previous tutorials — main() runs the loop standalone;
daslang-live invokes init / update / shutdown directly.
ImGui’s docking state is preserved across reloads because the ImGui
context survives reload (imgui_live serializes the context pointer
through the reload, and the dock state lives inside that context).
Driving from outside
Four live commands cover the docking surface. Targets are path-qualified
— the dockspace pushes its name onto the path, so panel targets are
DOCK_ROOT/<name>:
# Pop Output out into a floating window
curl -X POST -d '{"name":"imgui_undock","args":{"target":"DOCK_ROOT/OUTPUT"}}' \
localhost:9090/command
# Reposition the floating window (also works on docked windows — ImGui ignores
# the SetNextWindowPos while a window is docked, so this is most useful after
# imgui_undock). w/h are optional.
curl -X POST -d '{"name":"imgui_set_window_pos","args":{"target":"DOCK_ROOT/OUTPUT","value":{"x":580,"y":220,"w":360,"h":220}}}' \
localhost:9090/command
# Reset the dockspace back to the default layout
curl -X POST -d '{"name":"imgui_dock_reset","args":{"target":"DOCK_ROOT"}}' \
localhost:9090/command
# Close the OUTPUT panel (X-button equivalent — closable=true required)
curl -X POST -d '{"name":"imgui_close","args":{"target":"DOCK_ROOT/OUTPUT"}}' \
localhost:9090/command
imgui_dock is the inverse of imgui_undock — it takes a value of
type uint (a dock-node id from a prior DockBuilder* call) and
re-docks the panel into that node. imgui_set_window_pos is the
companion you’ll usually pair with imgui_undock, since a freshly
undocked window picks its position from imgui.ini (or (0,0) if
the window has never floated).
Next steps
Style scopes are next — with_style for pushing colors and metrics
across a sub-tree of widgets, balanced pop, and how nesting stacks.
See also
Full source: examples/tutorial/docking.das
Richer reference: examples/features/dock_basic.das — same boost
surface with a 4-panel initial layout and a wider widget set.
Integration test: tests/integration/test_docking_basic.das —
registration, initial-layout geometry, and live-command round-trips.
Previous tutorial: Layout
Boost macros — the macro layer.