8.18.1.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.18.1.17.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: tree_node — expandable branch with auto-paired TreePop.
21//
22// tree_node(IDENT, (text = "..", flags = ImGuiTreeNodeFlags....)) { body }
23//
24// The [container] form. Body runs only while ImGui reports the node open
25// (chevron expanded). The wrapper handles TreePop automatically when the
26// body's been invoked. Three distinct knobs:
27//
28// 1. flags — ImGuiTreeNodeFlags bitfield. DefaultOpen, OpenOnArrow,
29// OpenOnDoubleClick, Leaf, Bullet, Framed, NoTreePushOnOpen.
30//
31// 2. state.pending_open / pending_close — flips SetNextItemOpen(true/false,
32// Always) on the NEXT frame's TreeNodeEx call. The live commands
33// imgui_open / imgui_close ride this channel.
34//
35// 3. state.opened — per-frame read-only mirror of TreeNodeEx's bool
36// return. A driver checks "did the user actually expand this?" by
37// reading state.opened on the snapshot.
38//
39// tree_node vs tree_node_ex:
40// - tree_node (this tutorial) — [container] block-arg, auto-pairs TreePop.
41// - tree_node_ex — [widget] leaf form, caller drives TreePop manually
42// (see examples/features/tree_node_ex.das). Use it when the body needs
43// to render OUTSIDE the open conditional, e.g. same_line() with a
44// sibling button on the header row.
45//
46// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/tree_node.das
47// LIVE: daslang-live modules/dasImgui/examples/tutorial/tree_node.das
48//
49// DRIVE (when running live):
50// curl -X POST -d '{"name":"imgui_open","args":{"target":"TN_WIN/PHYSICS_TREE"}}' \
51// localhost:9090/command
52// curl -X POST -d '{"name":"imgui_close","args":{"target":"TN_WIN/PHYSICS_TREE"}}' \
53// localhost:9090/command
54// =============================================================================
55
56[export]
57def init() {
58 live_create_window("dasImgui tree_node tutorial", 720, 540)
59 live_imgui_init(live_window)
60 let io & = unsafe(GetIO())
61 GetStyle().FontScaleMain = 1.4
62}
63
64[export]
65def update() {
66 if (!live_begin_frame()) return
67 begin_frame()
68
69 ImGui_ImplGlfw_NewFrame()
70 apply_synth_io_override()
71 NewFrame()
72
73 PHYSICS_GRAVITY.bounds = (0.0f, 50.0f)
74 PHYSICS_DENSITY.bounds = (0.0f, 10.0f)
75 RENDER_FOV.bounds = (30.0f, 120.0f)
76
77 SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
78 SetNextWindowSize(ImVec2(680.0f, 500.0f), ImGuiCond.Always)
79 window(TN_WIN, (text = "tree_node tutorial", closable = false,
80 flags = ImGuiWindowFlags.None)) {
81
82 text("Three patterns - DefaultOpen, nested tree_nodes, and OpenOnArrow.")
83 separator()
84
85 // ---- A: DefaultOpen (chevron down from frame 1) ----
86 tree_node(PHYSICS_TREE, (text = "Physics",
87 flags = ImGuiTreeNodeFlags.DefaultOpen)) {
88 slider_float(PHYSICS_GRAVITY, (text = "gravity"))
89 slider_float(PHYSICS_DENSITY, (text = "density"))
90 text("PHYSICS_TREE.opened = {PHYSICS_TREE.opened}")
91 }
92
93 // ---- B: nested tree_nodes — Render > [Camera | Mesh] ----
94 tree_node(RENDER_TREE, (text = "Render",
95 flags = ImGuiTreeNodeFlags.None)) {
96 tree_node(CAMERA_SUBTREE, (text = "Camera",
97 flags = ImGuiTreeNodeFlags.DefaultOpen)) {
98 slider_float(RENDER_FOV, (text = "fov"))
99 checkbox(CAMERA_ORTHO, (text = "Orthographic"))
100 }
101 tree_node(MESH_SUBTREE, (text = "Mesh",
102 flags = ImGuiTreeNodeFlags.None)) {
103 checkbox(MESH_WIRE, (text = "Wireframe"))
104 checkbox(MESH_NORMALS, (text = "Show normals"))
105 }
106 }
107
108 // ---- C: OpenOnArrow — clicking the label doesn't expand ----
109
110 // DefaultOpen keeps ASSETS_HINT in the registry from frame 1. OpenOnArrow
111 // is still shown: collapse via chevron, then click the label — it stays
112 // collapsed; only the arrow toggles.
113 tree_node(ASSETS_TREE, (text = "Assets (OpenOnArrow + DefaultOpen)",
114 flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.DefaultOpen)) {
115 text(ASSETS_HINT, (text = "OpenOnArrow flag - clicking the LABEL doesn't toggle."))
116 text("Click the arrow chevron specifically. Useful for")
117 text("selectable-row trees where the label is the selection.")
118 }
119 }
120
121 end_of_frame()
122 Render()
123 var w, h : int
124 live_get_framebuffer_size(w, h)
125 glViewport(0, 0, w, h)
126 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
127 glClear(GL_COLOR_BUFFER_BIT)
128 live_imgui_render()
129
130 live_end_frame()
131}
132
133[export]
134def shutdown() {
135 live_imgui_shutdown()
136 live_destroy_window()
137}
138
139[export]
140def main() {
141 init()
142 while (!exit_requested()) {
143 update()
144 }
145 shutdown()
146}
8.18.1.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.18.1.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.18.1.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.18.1.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.18.1.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. The body is required to not exist (or be empty); usetree_node_ex(the [widget] leaf form) for nodes that should be selectable rows without children. 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.18.1.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.18.1.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.