8.19.48. tree_node_ex + image

Two leaf widgets the container-shaped variants don’t cover. tree_node_ex is the explicit-control sibling of the tree_node container; image is the display-only sibling of image_button.

tree_node_ex(IDENT, (text = "..", flags = ...))    // returns open bool
                                                    // caller pairs tree_pop()
image(IDENT, (user_texture_id = tex_id, size = .., // display only
              uv0 = .., uv1 = .., tint_col = ..,    // no ClickState
              border_col = ..))

Source: modules/dasImgui/examples/tutorial/tree_image_misc.das.

8.19.48.1. Walkthrough

The recording clicks the tree_node_ex header to expand it - the three children appear and the node’s returned open bool flips to true - then moves to the display-only image. The expand is verified against that open flag and the image against its on-screen presence, so a click that failed to open the node would abort 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_layout_builtin
 19require imgui/imgui_visual_aids
 20
 21// =============================================================================
 22// TUTORIAL: tree_node_ex + image — two widgets the container-shaped variants
 23// don't cover.
 24//
 25//   tree_node_ex(IDENT, (text = "..", flags = ...))     — leaf form. Returns
 26//                                                          the open bool;
 27//                                                          caller pairs
 28//                                                          TreePop() when
 29//                                                          needed (when no
 30//                                                          NoTreePushOnOpen
 31//                                                          flag).
 32//
 33//   image(IDENT, (user_texture_id = tex_id, size = ..,  — display widget.
 34//                 uv0 = .., uv1 = .., tint_col = ..,      No state-tracked
 35//                 border_col = ..))                       click / hover; cf.
 36//                                                          image_button for
 37//                                                          the click trigger.
 38//
 39// tree_node_ex is the explicit-control sibling of `tree_node` (the
 40// container). Use it when you need the open/closed bool to gate sibling
 41// work, OR when you want NoTreePushOnOpen / Bullet / Leaf flags that
 42// the container form doesn't surface cleanly.
 43//
 44// image is the display-only sibling of image_button — no ClickState, no
 45// frame padding, no click semantics.
 46//
 47// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/tree_image_misc.das
 48// LIVE:       daslang-live modules/dasImgui/examples/tutorial/tree_image_misc.das
 49// =============================================================================
 50
 51[export]
 52def init() {
 53    live_create_window("dasImgui tree_node_ex + image tutorial", 980, 720)
 54    live_imgui_init(live_window)
 55    let io & = unsafe(GetIO())
 56    GetStyle().FontScaleMain = 1.4
 57}
 58
 59[export]
 60def update() {
 61    if (!live_begin_frame()) return
 62    begin_frame()
 63
 64    ImGui_ImplGlfw_NewFrame()
 65    apply_synth_io_override()
 66    NewFrame()
 67
 68    SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
 69    SetNextWindowSize(ImVec2(760.0f, 620.0f), ImGuiCond.Always)
 70    window(TI_WIN, (text = "tree_node_ex + image tutorial", closable = false,
 71                    flags = ImGuiWindowFlags.None)) {
 72
 73        text("Two leaf widgets the container-shaped variants don't cover.")
 74        text(TI_HINT, (text = "tree_node_ex returns the bool; image is display-only (no click)."))
 75        separator()
 76
 77        // ---- Stage 1: tree_node_ex — leaf form, caller controls TreePop ----
 78        text("tree_node_ex (leaf form - caller pairs TreePop):")
 79        if (tree_node_ex(TI_TREE, (text = "Group", flags = ImGuiTreeNodeFlags.None))) {
 80            text("  child A")
 81            text("  child B")
 82            text("  child C")
 83            tree_pop()
 84        }
 85        spacing()
 86
 87        // ---- Stage 2: image — display, no click ----
 88        text("image (display widget - paints a texture; no ClickState):")
 89        let io & = unsafe(GetIO())
 90        if (io.Fonts.TexData != null) {
 91            image(TI_IMG, (user_texture_id = io.Fonts.TexRef,
 92                           size = float2(128.0f, 128.0f),
 93                           uv0 = float2(0.0f, 0.0f),
 94                           uv1 = float2(1.0f, 1.0f),
 95                           tint_col = float4(1.0f, 1.0f, 1.0f, 1.0f),
 96                           border_col = float4(0.6f, 0.6f, 0.6f, 1.0f)))
 97            text("font atlas at 128x128 - no click, no padding; cf image_button.")
 98        }
 99    }
100
101    end_of_frame()
102    Render()
103    var w, h : int
104    live_get_framebuffer_size(w, h)
105    glViewport(0, 0, w, h)
106    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
107    glClear(GL_COLOR_BUFFER_BIT)
108    live_imgui_render()
109
110    live_end_frame()
111}
112
113[export]
114def shutdown() {
115    live_imgui_shutdown()
116    live_destroy_window()
117}
118
119[export]
120def main() {
121    init()
122    while (!exit_requested()) {
123        update()
124        maybe_collect_gc()
125    }
126    shutdown()
127}

8.19.48.1.1. Requires

  • imgui/imgui_widgets_builtintree_node_ex and image.

  • imgui/imgui_layout_builtintree_pop().

  • imgui/imgui_boost_runtimeTreeNodeState / ImageState.

8.19.48.1.2. tree_node_ex vs tree_node (container)

The container form (tree_node(IDENT, (text, flags)) { ... }) is what you reach for 90% of the time: it auto-pairs TreePush / TreePop, the body block carries the children:

// Container form — body is the open-state branch:
tree_node(GROUP, (text = "Group", flags = ImGuiTreeNodeFlags.None)) {
    text("  child A")
    text("  child B")
}

tree_node_ex is the leaf form: it returns the open bool, and the caller decides what to do with it. Use it when:

  • You want to render sibling content outside the open-state branch (a row of inline controls that should appear next to the header regardless of open state).

  • You’re passing NoTreePushOnOpen — that skips the TreePush, so a matching tree_pop() would underflow the stack. The leaf form makes the asymmetry explicit.

  • The flag set you want (Leaf, Bullet, SpanAllColumns, etc.) doesn’t compose well with the container body shape.

if (tree_node_ex(GROUP, (text = "Group",
                         flags = ImGuiTreeNodeFlags.None))) {
    text("  child A")
    text("  child B")
    tree_pop()
}

The matching tree_pop() is YOUR responsibility — easy to forget when refactoring. Prefer tree_node unless one of the cases above applies.

8.19.48.1.3. image vs image_button

  • image — display widget. Paints a texture; no ClickState, no frame padding, no click semantics.

  • image_button — click trigger. Texture-faced button with ClickState for accumulating clicks. See Buttons.

Pick by intent: if the texture is content (an avatar, a preview, an icon strip), use image. If it’s an interactive surface, use image_button.

let io & = unsafe(GetIO())
if (io.Fonts.TexData != null) {
    image(AVATAR, (user_texture_id = io.Fonts.TexRef,
                   size = float2(128.0f, 128.0f),
                   uv0 = float2(0.0f, 0.0f),
                   uv1 = float2(1.0f, 1.0f),
                   tint_col = float4(1.0f, 1.0f, 1.0f, 1.0f),
                   border_col = float4(0.6f, 0.6f, 0.6f, 1.0f)))
}

uv0 / uv1 slice into the texture (use for sprite sheets); tint_col modulates the rendered face. border_col is echoed into ImageState for snapshot assertions only — ImGui’s Image() takes no per-call border, it comes from the ImGuiCol_Border style.

The font atlas is always available and works as a no-setup demo texture: io.Fonts.TexRef is its ImGui 1.92 ImTextureRef, and the io.Fonts.TexData != null guard skips the frames before the backend has built the atlas. Real apps decode + upload their own texture and put the handle in ref._TexID — see Texture references for the full path.

8.19.48.1.4. Driving from outside

Both widgets surface their payload via imgui_snapshot — no imgui_force_set channels (the texture is caller-owned, the open state is local to tree_node_ex’s call site):

curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command \
    | jq '.globals."TI_WIN/TI_TREE".payload, .globals."TI_WIN/TI_IMG".payload'

See also

Full source: modules/dasImgui/examples/tutorial/tree_image_misc.das

Container sibling: Tree node (and Collapsing header for the unbordered-open variant).

Click sibling: Buttons (image_button section).

Features-side demo: modules/dasImgui/examples/features/display_image.das.

Boost macros — the macro layer.