Containers
The boost layer ships a family of containers — block-arg wrappers
around ImGui’s Begin*/End* pairs that share three properties:
Block-arg shape — the body runs once per frame inside the begin/end pair. No manual
End()to forget.Path push — leaf widgets inside the block register under
<container>/<leaf>. Every container contributes a path segment, just likewindowandwith_iddid in earlier tutorials.Open-state via pending flags —
state.pending_open = truequeues the container to open next frame;pending_close = truecloses it. Live commandsimgui_open/imgui_closemutate the same flag, so app code, external drivers, and the chrome’s close-button share one channel.
This tutorial covers four representative containers: menu_bar +
menu + menu_item, tab_bar + tab_item, popup, and
item_tooltip. The features-side demos
(examples/features/containers_*.das) cover the rest:
child / group (window family), tree_node /
collapsing_header (layout family), popup_modal /
tooltip / combo_select / list_box (overlay family).
Source: examples/tutorial/containers.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_containers_builtin
18require imgui/imgui_visual_aids
19
20// =============================================================================
21// TUTORIAL: containers — tab_bar, menu_bar, popup, item_tooltip.
22//
23// Containers wrap an ImGui begin/end pair around a daslang block. They
24// share three properties:
25//
26// 1. Block-arg shape: the body runs once per frame inside the begin/end.
27// 2. Path push: leaf widgets register under "<container>/<leaf>" — every
28// container contributes a segment to the registry path, just like
29// `window` and `with_id` did in earlier tutorials.
30// 3. Open-state via pending flags: `(state).pending_open = true` queues
31// the container to open next frame; `pending_close = true` reverses
32// it. The same flag is what `imgui_open` / `imgui_close` mutate from
33// outside, so all three control surfaces (app code, live commands,
34// and the close-button on the chrome) share one channel.
35//
36// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/containers.das
37// LIVE: daslang-live modules/dasImgui/examples/tutorial/containers.das
38//
39// DRIVE (when running live):
40// curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command
41// curl -X POST -d '{"name":"imgui_open","args":{"target":"CONT_WIN/OPTIONS_POPUP"}}' localhost:9090/command
42// curl -X POST -d '{"name":"imgui_close","args":{"target":"CONT_WIN/OPTIONS_POPUP"}}' localhost:9090/command
43// =============================================================================
44
45[export]
46def init() {
47 live_create_window("dasImgui containers tutorial", 760, 560)
48 live_imgui_init(live_window)
49 var io & = unsafe(GetIO())
50 io.FontGlobalScale = 1.5
51}
52
53[export]
54def update() {
55 if (!live_begin_frame()) return
56 begin_frame()
57
58 ImGui_ImplOpenGL3_NewFrame()
59 ImGui_ImplGlfw_NewFrame()
60 apply_synth_io_override()
61 NewFrame()
62
63 SetNextWindowPos(ImVec2(30.0f, 30.0f), ImGuiCond.FirstUseEver)
64 SetNextWindowSize(ImVec2(680.0f, 480.0f), ImGuiCond.FirstUseEver)
65 window(CONT_WIN, (text = "containers", closable = false,
66 flags = ImGuiWindowFlags.MenuBar)) {
67
68 // ---- menu_bar (window must have ImGuiWindowFlags.MenuBar) ----
69 // Each menu_item registers under "CONT_WIN/MAIN_BAR/FILE_MENU/NEW_ITEM".
70 menu_bar(MAIN_BAR) {
71 menu(FILE_MENU, (text = "File", enabled = true)) {
72 menu_item(NEW_ITEM, (text = "New", shortcut = "Ctrl+N"))
73 menu_item(OPEN_ITEM, (text = "Open", shortcut = "Ctrl+O"))
74 }
75 }
76
77 // ---- tab_bar — three tabs, each its own block ----
78 // Only the active tab's block runs each frame; inactive tabs don't
79 // even enter their body, so widgets in inactive tabs aren't in
80 // the registry that frame.
81 tab_bar(MAIN_TABS, (text = "MainTabs", flags = ImGuiTabBarFlags.None)) {
82 tab_item(GENERAL_TAB, (text = "General", closable = false,
83 flags = ImGuiTabItemFlags.None)) {
84 text("Tabs share a window; only the active tab renders.")
85 checkbox(WIRE, (text = "Wireframe"))
86 }
87 tab_item(AUDIO_TAB, (text = "Audio", closable = false,
88 flags = ImGuiTabItemFlags.None)) {
89 checkbox(MUTE, (text = "Mute"))
90 slider_float(VOL, (text = "Volume"))
91 }
92 tab_item(INFO_TAB, (text = "Info", closable = false,
93 flags = ImGuiTabItemFlags.None)) {
94 text("containers tutorial")
95 text("each tab is a block — exclusive render")
96 }
97 }
98
99 separator(CT_SEP_1)
100
101 // ---- popup — opened by a pending flag ----
102 // The button only flips the flag; the popup itself renders on the
103 // next frame when ImGui sees `pending_open = true` and runs its
104 // OpenPopup() call. The same channel handles imgui_open from
105 // outside (curl example in the DRIVE block).
106 text("Popup — open via pending_open or imgui_open:")
107 if (button(OPEN_POPUP_BTN, (text = "Open options"))) {
108 OPTIONS_POPUP.pending_open = true
109 }
110 popup(OPTIONS_POPUP, (text = "OptionsPopup",
111 flags = ImGuiWindowFlags.None)) {
112 text("Options")
113 separator(CT_SEP_2)
114 checkbox(OPT_VSYNC, (text = "VSync"))
115 checkbox(OPT_HIDPI, (text = "HiDPI"))
116 if (button(POPUP_CLOSE_BTN, (text = "Close"))) {
117 OPTIONS_POPUP.pending_close = true
118 }
119 }
120
121 separator(CT_SEP_3)
122
123 // ---- item_tooltip — hover-gated overlay ----
124 // BeginItemTooltip auto-checks IsItemHovered() under the hood, so
125 // the block only runs while the previous widget is hovered.
126 text("Tooltip — hover the button below:")
127 button(HOVER_BTN, (text = "Hover me"))
128 item_tooltip(HOVER_TIP) {
129 text("This text appears on hover.")
130 text("Driven by BeginItemTooltip (auto-gated).")
131 }
132 }
133
134 end_of_frame()
135 Render()
136 var w, h : int
137 live_get_framebuffer_size(w, h)
138 glViewport(0, 0, w, h)
139 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
140 glClear(GL_COLOR_BUFFER_BIT)
141 ImGui_ImplOpenGL3_RenderDrawData(GetDrawData())
142
143 live_end_frame()
144}
145
146[export]
147def shutdown() {
148 live_imgui_shutdown()
149 live_destroy_window()
150}
151
152[export]
153def main() {
154 init()
155 while (!exit_requested()) {
156 update()
157 }
158 shutdown()
159}
Requires
One extra module on top of the baseline boost layer:
imgui/imgui_containers_builtin— defines every container macro used here. The window/child/group/menu/tab/popup/tooltip surface all lives in this one module.
tab_bar / tab_item
tab_bar holds one or more tab_item blocks; ImGui owns the
active-tab selection:
tab_bar(MAIN_TABS, (text = "MainTabs", flags = ImGuiTabBarFlags.None)) {
tab_item(GENERAL_TAB, (text = "General", closable = false,
flags = ImGuiTabItemFlags.None)) {
Text("Tabs share a window; only the active tab renders.")
checkbox(WIRE, (text = "Wireframe"))
}
tab_item(AUDIO_TAB, (text = "Audio", ...)) { ... }
tab_item(INFO_TAB, (text = "Info", ...)) { ... }
}
Only the active tab’s block runs each frame — widgets inside
inactive tabs aren’t in the registry that frame, so a snapshot taken
while GENERAL_TAB is active won’t list any AUDIO_TAB children.
TabItemState.pending_open controls the closable-tab visibility
(skip BeginTabItem entirely when open=false), but it does NOT
programmatically select the active tab — that’s an ImGui internal
state, set by clicking the tab header.
popup
A popup renders only when explicitly opened. The state struct’s
pending_open flag is the open-channel; the renderer calls
OpenPopup next frame:
if (button(OPEN_POPUP_BTN, (text = "Open options"))) {
OPTIONS_POPUP.pending_open = true
}
popup(OPTIONS_POPUP, (text = "OptionsPopup",
flags = ImGuiWindowFlags.None)) {
Text("Options")
checkbox(OPT_VSYNC, (text = "VSync"))
if (button(POPUP_CLOSE_BTN, (text = "Close"))) {
OPTIONS_POPUP.pending_close = true
}
}
External drivers reach the same flag via imgui_open /
imgui_close — three control surfaces (app code, live commands, the
close-button chrome) all funnel through the popup state’s pending
flags. The popup also auto-closes when the user clicks outside it
(ImGui’s normal popup behavior).
item_tooltip — hover-gated overlay
ImGui’s BeginItemTooltip checks IsItemHovered() internally, so
the block only runs while the immediately-preceding widget is hovered.
No manual gate:
button(HOVER_BTN, (text = "Hover me"))
item_tooltip(HOVER_TIP) {
Text("This text appears on hover.")
Text("Driven by BeginItemTooltip (auto-gated).")
}
For tooltips whose own gating logic differs from “previous item
hovered” — say, tooltips on an entire window or a custom hover state —
use the lower-level tooltip(...) container and gate it manually
(see examples/features/containers_overlay.das).
Standalone vs live
Same convention as previous tutorials.
Driving from outside
Path-qualified targets for every container leaf:
curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command
# Registers: CONT_WIN/MAIN_BAR/FILE_MENU/NEW_ITEM, CONT_WIN/MAIN_TABS/GENERAL_TAB/WIRE, ...
curl -X POST -d '{"name":"imgui_open","args":{"target":"CONT_WIN/OPTIONS_POPUP"}}' \
localhost:9090/command
curl -X POST -d '{"name":"imgui_close","args":{"target":"CONT_WIN/OPTIONS_POPUP"}}' \
localhost:9090/command
curl -X POST -d '{"name":"imgui_click","args":{"target":"CONT_WIN/MAIN_BAR/FILE_MENU/NEW_ITEM"}}' \
localhost:9090/command
Note that menu items receive imgui_click directly — they’re click
targets, not open/close targets.
Context popups
popup_context_item is the right-click-context sibling of popup —
ImGui drives open/close internally based on the previous item receiving
a right-click; the wrapper just gates the body on Begin returning true:
require imgui/imgui_containers_builtin
button(TARGET_BTN, (text = "Right-click me"))
popup_context_item(TARGET_CTX, (str_id = "target_ctx",
flags = ImGuiPopupFlags.MouseButtonRight)) {
if (menu_item(ACTION_RENAME, (text = "Rename", shortcut = "F2"))) {
// ...
}
if (menu_item(ACTION_DELETE, (text = "Delete", shortcut = "Del"))) {
// ...
}
}
The popup is keyed off the previously submitted item — submission
order matters, and popup_context_item registers under its own path
in the snapshot. Use imgui_click to drive menu items from outside.
Feature demo: examples/features/popup_context_item.das.
Next steps
So far every tutorial has assumed the standard standalone/live run.
Next up is live-reload itself — the daslang-live workflow, how
[live_command] / [before_reload] / [after_reload] plumb
in, and what survives a reload (state structs, ImGui context, the
HTTP server) versus what gets rebuilt.
See also
Full source: examples/tutorial/containers.das
Richer references:
examples/features/containers_window.das— window / child / group with closable second windowexamples/features/containers_layout.das— tab_bar plus tree_node, collapsing_headerexamples/features/containers_overlay.das— popup_modal, tooltip, combo_select, list_box
Previous tutorial: State & telemetry
Boost macros — the macro layer.