8.19.17. Tree node
tree_node is the block-arg [container] form of ImGui’s
TreeNodeEx: branch headers that fold-and-expand with an auto-paired
TreePop. The wrapper’s signature:
tree_node(IDENT, (text = "...",
flags = ImGuiTreeNodeFlags....)) {
// body — runs only while the chevron is expanded (TreeNodeEx == true)
}
Body invocation gated on ImGui returning true for TreeNodeEx;
TreePop only fires when the body was invoked. TreeNodeState
mirrors the per-frame open status (opened) and exposes the
pending_open / pending_close flags so imgui_open /
imgui_close can drive the chevron from outside.
Source: modules/dasImgui/examples/tutorial/tree_node.das.
8.19.17.1. Walkthrough
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: tree_node — expandable branch with auto-paired TreePop.
22//
23// tree_node(IDENT, (text = "..", flags = ImGuiTreeNodeFlags....)) { body }
24//
25// The [container] form. Body runs only while ImGui reports the node open
26// (chevron expanded). The wrapper handles TreePop automatically when the
27// body's been invoked. Three distinct knobs:
28//
29// 1. flags — ImGuiTreeNodeFlags bitfield. DefaultOpen, OpenOnArrow,
30// OpenOnDoubleClick, Leaf, Bullet, Framed, NoTreePushOnOpen.
31//
32// 2. state.pending_open / pending_close — flips SetNextItemOpen(true/false,
33// Always) on the NEXT frame's TreeNodeEx call. The live commands
34// imgui_open / imgui_close ride this channel.
35//
36// 3. state.opened — per-frame read-only mirror of TreeNodeEx's bool
37// return. A driver checks "did the user actually expand this?" by
38// reading state.opened on the snapshot.
39//
40// tree_node vs tree_node_ex:
41// - tree_node (this tutorial) — [container] block-arg, auto-pairs TreePop.
42// - tree_node_ex — [widget] leaf form, caller drives TreePop manually
43// (see examples/features/tree_node_ex.das). Use it when the body needs
44// to render OUTSIDE the open conditional, e.g. same_line() with a
45// sibling button on the header row.
46//
47// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/tree_node.das
48// LIVE: daslang-live modules/dasImgui/examples/tutorial/tree_node.das
49//
50// DRIVE (when running live):
51// curl -X POST -d '{"name":"imgui_open","args":{"target":"TN_WIN/PHYSICS_TREE"}}' \
52// localhost:9090/command
53// curl -X POST -d '{"name":"imgui_close","args":{"target":"TN_WIN/PHYSICS_TREE"}}' \
54// localhost:9090/command
55// =============================================================================
56
57[export]
58def init() {
59 live_create_window("dasImgui tree_node tutorial", 720, 540)
60 live_imgui_init(live_window)
61 let io & = unsafe(GetIO())
62 GetStyle().FontScaleMain = 1.4
63}
64
65[export]
66def update() {
67 if (!live_begin_frame()) return
68 begin_frame()
69
70 ImGui_ImplGlfw_NewFrame()
71 apply_synth_io_override()
72 NewFrame()
73
74 PHYSICS_GRAVITY.bounds = (0.0f, 50.0f)
75 PHYSICS_DENSITY.bounds = (0.0f, 10.0f)
76 RENDER_FOV.bounds = (30.0f, 120.0f)
77
78 SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
79 SetNextWindowSize(ImVec2(680.0f, 500.0f), ImGuiCond.Always)
80 window(TN_WIN, (text = "tree_node tutorial", closable = false,
81 flags = ImGuiWindowFlags.None)) {
82
83 text("Three patterns - DefaultOpen, nested tree_nodes, and OpenOnArrow.")
84 separator()
85
86 // ---- A: DefaultOpen (chevron down from frame 1) ----
87 tree_node(PHYSICS_TREE, (text = "Physics",
88 flags = ImGuiTreeNodeFlags.DefaultOpen)) {
89 slider_float(PHYSICS_GRAVITY, (text = "gravity"))
90 slider_float(PHYSICS_DENSITY, (text = "density"))
91 text("PHYSICS_TREE.opened = {PHYSICS_TREE.opened}")
92 }
93
94 // ---- B: nested tree_nodes — Render > [Camera | Mesh] ----
95 tree_node(RENDER_TREE, (text = "Render",
96 flags = ImGuiTreeNodeFlags.None)) {
97 tree_node(CAMERA_SUBTREE, (text = "Camera",
98 flags = ImGuiTreeNodeFlags.DefaultOpen)) {
99 slider_float(RENDER_FOV, (text = "fov"))
100 checkbox(CAMERA_ORTHO, (text = "Orthographic"))
101 }
102 tree_node(MESH_SUBTREE, (text = "Mesh",
103 flags = ImGuiTreeNodeFlags.None)) {
104 checkbox(MESH_WIRE, (text = "Wireframe"))
105 checkbox(MESH_NORMALS, (text = "Show normals"))
106 }
107 }
108
109 // ---- C: OpenOnArrow — clicking the label doesn't expand ----
110
111 // DefaultOpen keeps ASSETS_HINT in the registry from frame 1. OpenOnArrow
112 // is still shown: collapse via chevron, then click the label — it stays
113 // collapsed; only the arrow toggles.
114 tree_node(ASSETS_TREE, (text = "Assets (OpenOnArrow + DefaultOpen)",
115 flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.DefaultOpen)) {
116 text(ASSETS_HINT, (text = "OpenOnArrow flag - clicking the LABEL doesn't toggle."))
117 text("Click the arrow chevron specifically. Useful for")
118 text("selectable-row trees where the label is the selection.")
119 }
120 }
121
122 end_of_frame()
123 Render()
124 var w, h : int
125 live_get_framebuffer_size(w, h)
126 glViewport(0, 0, w, h)
127 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
128 glClear(GL_COLOR_BUFFER_BIT)
129 live_imgui_render()
130
131 live_end_frame()
132}
133
134[export]
135def shutdown() {
136 live_imgui_shutdown()
137 live_destroy_window()
138}
139
140[export]
141def main() {
142 init()
143 while (!exit_requested()) {
144 update()
145 maybe_collect_gc()
146 }
147 shutdown()
148}
8.19.17.1.1. Requires
Already in the baseline boost layer:
imgui/imgui_containers_builtin—tree_nodecontainer.imgui/imgui_widgets_builtin—slider_float,checkbox,text, plus the leaftree_node_ex.
8.19.17.1.2. Nesting and path composition
Tree nodes nest naturally. Each level’s IDENT pushes onto the path
hash, so a slider three levels deep registers at
TN_WIN/RENDER_TREE/CAMERA_SUBTREE/RENDER_FOV:
tree_node(RENDER_TREE, (text = "Render", flags = ImGuiTreeNodeFlags.None)) {
tree_node(CAMERA_SUBTREE, (text = "Camera",
flags = ImGuiTreeNodeFlags.DefaultOpen)) {
slider_float(RENDER_FOV, (text = "fov"))
}
tree_node(MESH_SUBTREE, (text = "Mesh",
flags = ImGuiTreeNodeFlags.None)) {
checkbox(MESH_WIRE, (text = "Wireframe"))
}
}
When RENDER_TREE is collapsed, neither subtree’s body runs — the
sliders/checkboxes aren’t in the snapshot until the user expands
RENDER_TREE. The state structs themselves (CAMERA_SUBTREE,
MESH_SUBTREE) still exist; their opened field is false.
8.19.17.1.3. Open / close from outside
Two channels feed open/close into the wrapper:
state.pending_open = true— app code anywhere. Next frame the wrapper appliesSetNextItemOpen(true, Always)before TreeNodeEx runs. Cleared on consume.imgui_open/imgui_close— live commands routed by path.
curl -X POST -d '{"name":"imgui_open","args":{"target":"TN_WIN/PHYSICS_TREE"}}' \
localhost:9090/command
curl -X POST -d '{"name":"imgui_close","args":{"target":"TN_WIN/PHYSICS_TREE"}}' \
localhost:9090/command
Both flow through state.pending_open / state.pending_close —
the dispatcher just walks the registry and flips the flag. Always
in the SetNextItemOpen call means the override wins over ImGui’s
stored chevron state for that one frame.
8.19.17.1.4. Reading state.opened
state.opened is a per-frame mirror of TreeNodeEx’s bool return.
A snapshot reports it under opened:
var PHYSICS_TREE : TreeNodeState
// After the frame:
// PHYSICS_TREE.opened : bool // chevron expanded this frame?
// PHYSICS_TREE.flags : ImGuiTreeNodeFlags // sticky
Drivers verify “user expanded this tree” by reading opened==true.
Note that opened is set to im_open BEFORE the body runs — the
wrapper writes it as soon as TreeNodeEx returns, so even a body that
panics still leaves a correct opened for the snapshot.
8.19.17.1.5. ImGuiTreeNodeFlags
The third arg’s bitfield. Most useful:
DefaultOpen— first-render expansion; subsequent frames respect user input. The Render-Camera example uses this so the sub-tree is open by default after a clean launch.OpenOnArrow— only the chevron click toggles; clicking the label itself doesn’t expand. Pair with a Selectable-row pattern.OpenOnDoubleClick— single click selects, double click toggles.Leaf— render the row without a chevron; ImGui reports it as always-open, so a container body would run every frame. A leaf has no children, so leave the body empty — or usetree_node_ex(the [widget] leaf form) for nodes that should be selectable rows. Seemodules/dasImgui/examples/features/tree_node_ex.das.Bullet— bullet point instead of chevron. Pair withLeaf.Framed— solid background under the header strip.NoTreePushOnOpen— open the node WITHOUT indenting the body. Used bytree_node_exrails where the body lives outside the conditional.SpanAllColumns/SpanAvailWidth/SpanFullWidth— hit-test region tweaks for trees inside tables.
8.19.17.1.6. tree_node vs tree_node_ex
Same backing struct, different ergonomics:
tree_node(IDENT, (...)) { body }(this tutorial) —[container]block-arg. Body runs gated on the open chevron; wrapper handlesTreePop.tree_node_ex(IDENT, (...))—[widget]leaf. Returns a bool; caller pairstree_pop()(the rail) manually, outside anif (open)if needed. The escape hatch for “header has a siblingsame_line()widget” or “Leaf node with selectable behavior” — seemodules/dasImgui/examples/features/tree_node_ex.dasfor the rail-side demo andmodules/dasImgui/examples/features/tree_node_open_manual.dasfor the sibling same_line pattern.
When in doubt, use tree_node — block-arg auto-pairing is harder to
get wrong.
8.19.17.1.7. Standalone vs live
Same convention as the other tutorials.
See also
Full source: modules/dasImgui/examples/tutorial/tree_node.das
Features-side demos:
modules/dasImgui/examples/features/containers_layout.das — tree_node alongside
tab_bar + collapsing_header;
modules/dasImgui/examples/features/tree_node_open_manual.das — leaf-form manual
tree_pop pairing with a sibling button on the header row;
modules/dasImgui/examples/features/tree_node_ex.das — Leaf / Bullet / OpenOnArrow
variations against the [widget] leaf form.
Sibling: Collapsing header — same flags-and-state shape without the chevron / TreePop dance.
Boost macros — the macro layer.