8.18.1.18. Collapsing header
collapsing_header is the section-fold sibling of tree_node: same
expand/collapse chevron, but no TreePop pair — ImGui owns the
close lifecycle. Two distinct gates govern visibility:
Expanded — the chevron click toggles the body.
state.openedis the per-frameCollapsingHeaderreturn value.Closable — with
closable=true, ImGui adds an X-button that setsstate.open=false. Whilestate.openis false, the whole header strip is hidden (not just its body).
The boost wrapper exposes one channel — pending_open /
pending_close — that drives the chevron (expanded gate). The
live commands imgui_open / imgui_close ride that channel.
imgui_open additionally re-sets state.open=true so a previously
X-hidden strip becomes visible again. imgui_close does NOT touch
state.open — to hide the strip, click the X-button or write
state.open=false from app code.
Source: modules/dasImgui/examples/tutorial/collapsing_header.das.
8.18.1.18.1. Walkthrough
1options gen2
2options _comment_hygiene = true
3
4require imgui
5require imgui_app
6require opengl/opengl_boost
7require live/glfw_live
8require live/live_api
9require live/live_commands
10require live/live_vars
11require live_host
12require imgui/imgui_live
13require imgui/imgui_boost_runtime
14require imgui/imgui_boost_v2
15require imgui/imgui_widgets_builtin
16require imgui/imgui_containers_builtin
17require imgui/imgui_visual_aids
18
19// =============================================================================
20// TUTORIAL: collapsing_header — expandable section header with optional X-button.
21//
22// collapsing_header(IDENT, (text = "..", closable = bool,
23// flags = ImGuiTreeNodeFlags....)) { body }
24//
25// Same shape as tree_node BUT no TreePop pair — ImGui owns the close lifecycle.
26// Two distinct gates control visibility, each on its own field:
27//
28// 1. EXPANDED (chevron) — controls whether the BODY runs. state.opened
29// mirrors CollapsingHeader's per-frame return. pending_open /
30// pending_close (driven by imgui_open / imgui_close) override the
31// chevron via SetNextItemOpen on the next frame.
32//
33// 2. CLOSABLE (X-button) — controls whether the entire HEADER STRIP
34// renders. Only active when closable=true. ImGui flips state.open=false
35// on X-click; the wrapper then skips BeginCollapsingHeader entirely so
36// the strip vanishes. imgui_open re-sets state.open=true; imgui_close
37// does NOT — it only collapses the chevron via the expanded channel.
38// To programmatically hide the strip, write state.open=false from app
39// code.
40//
41// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/collapsing_header.das
42// LIVE: daslang-live modules/dasImgui/examples/tutorial/collapsing_header.das
43//
44// DRIVE (when running live):
45// # Collapse the chevron (body stops rendering; header strip stays):
46// curl -X POST -d '{"name":"imgui_close","args":{"target":"CH_WIN/CLOSABLE_CH"}}' \
47// localhost:9090/command
48// # Re-expand the chevron AND re-show the strip if X-button hid it:
49// curl -X POST -d '{"name":"imgui_open","args":{"target":"CH_WIN/CLOSABLE_CH"}}' \
50// localhost:9090/command
51// # To HIDE the strip, click the X-button or write CLOSABLE_CH.open=false
52// # from app code — there's no imgui_close path that does this.
53// =============================================================================
54
55[export]
56def init() {
57 live_create_window("dasImgui collapsing_header tutorial", 760, 520)
58 live_imgui_init(live_window)
59 let io & = unsafe(GetIO())
60 GetStyle().FontScaleMain = 1.4
61}
62
63[export]
64def update() {
65 if (!live_begin_frame()) return
66 begin_frame()
67
68 ImGui_ImplGlfw_NewFrame()
69 apply_synth_io_override()
70 NewFrame()
71
72 SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
73 SetNextWindowSize(ImVec2(720.0f, 480.0f), ImGuiCond.Always)
74 window(CH_WIN, (text = "collapsing_header tutorial", closable = false,
75 flags = ImGuiWindowFlags.None)) {
76
77 text("Three collapsing_header variants - basic, closable, default-open.")
78 separator()
79
80 // ---- A: basic (non-closable) ----
81 collapsing_header(BASIC_CH, (text = "Basic header", closable = false,
82 flags = ImGuiTreeNodeFlags.None)) {
83 text("Click the chevron to fold this section.")
84 checkbox(BASIC_LIVE, (text = "Receive updates"))
85 text("opened = {BASIC_CH.opened}")
86 }
87
88 // ---- B: closable (renders an X-button) ----
89 collapsing_header(CLOSABLE_CH, (text = "Closable header",
90 closable = true,
91 flags = ImGuiTreeNodeFlags.DefaultOpen)) {
92 text("X-button on the right of the header strip closes this section.")
93 text("Once closed, the whole row disappears - flip back via")
94 text("imgui_open against CH_WIN/CLOSABLE_CH.")
95 VOLUME.bounds = (0, 100)
96 slider_int(VOLUME, (text = "volume"))
97 }
98 text("CLOSABLE_CH.open = {CLOSABLE_CH.open}, opened = {CLOSABLE_CH.opened}")
99 separator()
100
101 // ---- C: DefaultOpen flag (chevron already down on first frame) ----
102 collapsing_header(STARTUP_CH, (text = "Starts open (DefaultOpen flag)",
103 closable = false,
104 flags = ImGuiTreeNodeFlags.DefaultOpen)) {
105 text(STARTUP_HINT, (text = "ImGuiTreeNodeFlags.DefaultOpen makes the chevron expanded"))
106 text("on first render; later frames respect user input.")
107 }
108 }
109
110 end_of_frame()
111 Render()
112 var w, h : int
113 live_get_framebuffer_size(w, h)
114 glViewport(0, 0, w, h)
115 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
116 glClear(GL_COLOR_BUFFER_BIT)
117 live_imgui_render()
118
119 live_end_frame()
120}
121
122[export]
123def shutdown() {
124 live_imgui_shutdown()
125 live_destroy_window()
126}
127
128[export]
129def main() {
130 init()
131 while (!exit_requested()) {
132 update()
133 }
134 shutdown()
135}
8.18.1.18.1.1. Requires
Already in the baseline boost layer:
imgui/imgui_containers_builtin—collapsing_header.imgui/imgui_widgets_builtin—text,checkbox,slider_int.
8.18.1.18.1.2. Two visibility dimensions
CollapsingHeaderState carries both gates:
var CLOSABLE_CH : CollapsingHeaderState
// After the frame renders:
// CLOSABLE_CH.open : bool // X-button gate (visible at all?)
// CLOSABLE_CH.opened : bool // chevron gate (body expanded?)
// CLOSABLE_CH.flags : ImGuiTreeNodeFlags // sticky
open is the X-button channel. opened is the chevron channel.
Both surface in the snapshot. Tests that want to verify “the user
expanded the section but didn’t close it” check opened==true &&
open==true.
8.18.1.18.1.3. The closable form
When closable=true, ImGui::CollapsingHeader takes a
bool* p_visible and renders an X-button on the right of the header
strip. The boost wrapper feeds &state.open to that pointer:
collapsing_header(CLOSABLE_CH, (text = "Closable header",
closable = true,
flags = ImGuiTreeNodeFlags.DefaultOpen)) {
text("X-button on the right of the header strip closes this section.")
// body
}
Click the X — ImGui sets state.open=false. Next frame, the
wrapper sees open==false and skips the ImGui call entirely.
The header row is gone until something flips open back to true.
That something is either app code (CLOSABLE_CH.open = true) or a
live driver (imgui_open against the path).
8.18.1.18.1.4. The pending-flag channel
Two channels feed open/close from outside:
state.pending_open = true— set anywhere in app code, then the next frame’s call to the container clears it and appliesSetNextItemOpen(true, Always).imgui_open— the live command. The dispatcher walks theImguiPathRegistryand setspending_openon the matching state.
Same shape for pending_close / imgui_close. The closable form
also clears state.open=false from the X-button — three control
surfaces flow through one struct.
8.18.1.18.1.5. DefaultOpen flag
ImGuiTreeNodeFlags.DefaultOpen makes the chevron expanded on first
render only. Subsequent frames respect whatever the user clicked.
Useful for “show me the important section by default” without
remembering open-state across runs.
collapsing_header(STARTUP_CH, (text = "Starts open",
closable = false,
flags = ImGuiTreeNodeFlags.DefaultOpen)) {
text("Body visible on first render.")
}
Other ImGuiTreeNodeFlags compose: OpenOnArrow, OpenOnDoubleClick,
Bullet, Framed, Leaf — same flag enum as tree_node (the
two rails share semantics intentionally).
8.18.1.18.1.6. Standalone vs live
Same convention as the other tutorials.
8.18.1.18.1.7. Driving from outside
The header strip is a real click target — its bbox is captured into the snapshot, so a human (or a playwright driver) clicks the chevron to fold the body and the X-button to hide the whole strip, exactly as the walkthrough above does (every gesture there is a real click, self-verified by the body’s rendered state). The live commands below drive the same gates without a click, for remote or scripted control.
To collapse the chevron (body stops rendering; the header strip stays visible):
curl -X POST -d '{"name":"imgui_close","args":{"target":"CH_WIN/CLOSABLE_CH"}}' \
localhost:9090/command
To re-expand the chevron — and, for a closable header that had been X-hidden, re-show the whole strip:
curl -X POST -d '{"name":"imgui_open","args":{"target":"CH_WIN/CLOSABLE_CH"}}' \
localhost:9090/command
imgui_open writes both state.pending_open AND state.open=true
so a previously X-hidden header re-appears. imgui_close, by
contrast, only writes state.pending_close — there’s no path that
hides the strip via live commands. To hide the strip programmatically,
write state.open=false from app code (the X-button is the only
user-driven path).
See also
Full source: modules/dasImgui/examples/tutorial/collapsing_header.das
Features-side demo: modules/dasImgui/examples/features/collapsing_header_closable.das —
minimal closable-X-button repro with integration test.
Sibling: Containers — umbrella container tour.
Boost macros — the macro layer.