8.19.14. 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
(modules/dasImgui/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: modules/dasImgui/examples/tutorial/containers.das.
8.19.14.1. Walkthrough
The recording drives all four containers with real synthetic input and
self-verifies each step. It flips the General tab’s WIRE checkbox,
then clicks the Audio tab header and asserts the switch by its effect:
AUDIO_TAB’s MUTE starts rendering while GENERAL_TAB’s WIRE
stops — a direct proof that only the open tab’s body runs. It opens the
popup from the button’s pending_open flag (asserting the popup body
appears), toggles the VSync checkbox inside it, closes it with the
popup’s own Close button (asserting the body stops), and finally hovers
the button to bring up the item_tooltip. Any step that failed to land
aborts the recording.
1options gen2
2options _comment_hygiene = true
3options gc
4
5require imgui
6require imgui_app
7require opengl/opengl_boost
8require live/glfw_live
9require live/live_api
10require live/live_commands
11require live/live_vars
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 let io & = unsafe(GetIO())
50 GetStyle().FontScaleMain = 1.5
51}
52
53[export]
54def update() { // nolint:STYLE038
55 if (!live_begin_frame()) return
56 begin_frame()
57
58 ImGui_ImplGlfw_NewFrame()
59 apply_synth_io_override()
60 NewFrame()
61
62 SetNextWindowPos(ImVec2(30.0f, 30.0f), ImGuiCond.FirstUseEver)
63 SetNextWindowSize(ImVec2(680.0f, 480.0f), ImGuiCond.FirstUseEver)
64 window(CONT_WIN, (text = "containers", closable = false,
65 flags = ImGuiWindowFlags.MenuBar)) {
66
67 // ---- menu_bar (window must have ImGuiWindowFlags.MenuBar) ----
68 // Each menu_item registers under "CONT_WIN/MAIN_BAR/FILE_MENU/NEW_ITEM".
69 menu_bar(MAIN_BAR) {
70 menu(FILE_MENU, (text = "File", enabled = true)) {
71 menu_item(NEW_ITEM, (text = "New", shortcut = "Ctrl+N"))
72 menu_item(OPEN_ITEM, (text = "Open", shortcut = "Ctrl+O"))
73 }
74 }
75
76 // ---- tab_bar — three tabs, each its own block ----
77
78 // Only the active tab's block runs each frame; inactive tabs never enter
79 // their body, so their widgets aren't in the registry that frame.
80 tab_bar(MAIN_TABS, (text = "MainTabs", flags = ImGuiTabBarFlags.None)) {
81 tab_item(GENERAL_TAB, (text = "General", closable = false,
82 flags = ImGuiTabItemFlags.None)) {
83 text("Tabs share a window; only the active tab renders.")
84 checkbox(WIRE, (text = "Wireframe"))
85 }
86 tab_item(AUDIO_TAB, (text = "Audio", closable = false,
87 flags = ImGuiTabItemFlags.None)) {
88 checkbox(MUTE, (text = "Mute"))
89 slider_float(VOL, (text = "Volume"))
90 }
91 tab_item(INFO_TAB, (text = "Info", closable = false,
92 flags = ImGuiTabItemFlags.None)) {
93 text("containers tutorial")
94 text("each tab is a block - exclusive render")
95 }
96 }
97
98 separator(CT_SEP_1)
99
100 // ---- popup — opened by a pending flag ----
101
102 // The button only flips the flag; the popup renders next frame when ImGui
103 // sees `pending_open = true` and runs OpenPopup(). The same channel handles
104 // imgui_open from outside (curl example in the DRIVE block).
105 text("Popup - open via pending_open or imgui_open:")
106 if (button(OPEN_POPUP_BTN, (text = "Open options"))) {
107 OPTIONS_POPUP.pending_open = true
108 }
109 popup(OPTIONS_POPUP, (text = "OptionsPopup",
110 flags = ImGuiWindowFlags.None)) {
111 text("Options")
112 separator(CT_SEP_2)
113 checkbox(OPT_VSYNC, (text = "VSync"))
114 checkbox(OPT_HIDPI, (text = "HiDPI"))
115 if (button(POPUP_CLOSE_BTN, (text = "Close"))) {
116 OPTIONS_POPUP.pending_close = true
117 }
118 }
119
120 separator(CT_SEP_3)
121
122 // ---- item_tooltip — hover-gated overlay ----
123 // BeginItemTooltip auto-checks IsItemHovered() under the hood, so
124 // the block only runs while the previous widget is hovered.
125 text("Tooltip - hover the button below:")
126 button(HOVER_BTN, (text = "Hover me"))
127 item_tooltip(HOVER_TIP) {
128 text("This text appears on hover.")
129 text("Driven by BeginItemTooltip (auto-gated).")
130 }
131 }
132
133 end_of_frame()
134 Render()
135 var w, h : int
136 live_get_framebuffer_size(w, h)
137 glViewport(0, 0, w, h)
138 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
139 glClear(GL_COLOR_BUFFER_BIT)
140 live_imgui_render()
141
142 live_end_frame()
143}
144
145[export]
146def shutdown() {
147 live_imgui_shutdown()
148 live_destroy_window()
149}
150
151[export]
152def main() {
153 init()
154 while (!exit_requested()) {
155 update()
156 maybe_collect_gc()
157 }
158 shutdown()
159}
8.19.14.1.1. 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.
8.19.14.1.3. 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", closable = false,
flags = ImGuiTabItemFlags.None)) {
checkbox(MUTE, (text = "Mute"))
}
}
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); selecting the active
tab is a separate channel, pending_select, driven by the
imgui_select live command (it hands ImGui
ImGuiTabItemFlags.SetSelected on the next frame and records
state.selected). Each tab_item also registers its header’s
bbox, so a driver can instead switch tabs the way a user does: an
imgui_click on the tab_item target (e.g.
CONT_WIN/MAIN_TABS/AUDIO_TAB) lands on the header and selects it.
Prefer imgui_select when the tab may be scrolled out of the bar —
imgui_click needs a visible header to hit.
8.19.14.1.4. 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).
8.19.14.1.5. 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 modules/dasImgui/examples/features/containers_overlay.das).
8.19.14.1.6. Standalone vs live
Same convention as previous tutorials.
8.19.14.1.7. 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.
8.19.14.1.8. 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: modules/dasImgui/examples/features/popup_context_item.das.
8.19.14.1.9. 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: modules/dasImgui/examples/tutorial/containers.das
Richer references:
modules/dasImgui/examples/features/containers_window.das— window / child / group with closable second windowmodules/dasImgui/examples/features/containers_layout.das— tab_bar plus tree_node, collapsing_headermodules/dasImgui/examples/features/containers_overlay.das— popup_modal, tooltip, combo_select, list_box
Previous tutorial: State & telemetry
Boost macros — the macro layer.