8.18.1.16. Group

group brackets BeginGroup/EndGroup — a pure layout container with no chrome, no flags, and no observable state. What it does is combine its children into a single layout unit so that:

  • same_line after the closing brace continues from the group’s right edge, not the last widget’s. Two stacked column blocks share a row by sandwiching same_line() between two groups.

  • IsItemHovered / IsItemActive / GetItemRect* after the group report against the combined bbox of every widget inside. Lets a tooltip attach to “anywhere in the icon-plus-label cluster” with no manual rectangle math.

GroupState is empty. The path push still happens, so widgets inside group(LEFT_GRP) register under <window>/LEFT_GRP/<leaf>.

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

8.18.1.16.1. Walkthrough

The recording walks the three patterns. First it toggles a checkbox in each of the two side-by-side blocks — both stay live while same_line keeps them on one row. Then it hovers the middle of the [i] Info cluster, not the button, and the tooltip still fires — proof that IsItemHovered after EndGroup reads against the whole group’s bounding box. Finally it drags the row’s slider and the value updates on the line below. Every step is a real synth gesture the recording asserts landed.

  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: group — BeginGroup/EndGroup pure layout grouping.
 21//
 22//   group(IDENT) { body }
 23//
 24// Treats several widgets as a single layout unit. Two effects:
 25//
 26//   1. Same-line stacking — same_line() after the group's closing brace
 27//      continues from the group's right edge, not the last widget's. Useful
 28//      for laying out (label-block | value-block | edit-block) rows.
 29//
 30//   2. Hit-test the whole group — IsItemHovered() / IsItemActive() called
 31//      after EndGroup() report against the group's combined bbox. Lets a
 32//      tooltip attach to "anywhere inside the icon-plus-label cluster".
 33//
 34// GroupState is empty — no flags, no observables. Pure structural marker.
 35// The path push still happens, so a checkbox inside group(LEFT_GRP) is
 36// reachable at GRP_WIN/LEFT_GRP/L_WIRE.
 37//
 38// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/group.das
 39// LIVE:       daslang-live modules/dasImgui/examples/tutorial/group.das
 40// =============================================================================
 41
 42[export]
 43def init() {
 44    live_create_window("dasImgui group tutorial", 720, 480)
 45    live_imgui_init(live_window)
 46    let io & = unsafe(GetIO())
 47    GetStyle().FontScaleMain = 1.4
 48}
 49
 50[export]
 51def update() {
 52    if (!live_begin_frame()) return
 53    begin_frame()
 54
 55    ImGui_ImplGlfw_NewFrame()
 56    apply_synth_io_override()
 57    NewFrame()
 58
 59    SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
 60    SetNextWindowSize(ImVec2(680.0f, 440.0f), ImGuiCond.Always)
 61    window(GRP_WIN, (text = "group tutorial", closable = false,
 62                     flags = ImGuiWindowFlags.None)) {
 63
 64        text("group(IDENT) takes a block and treats its widgets as one layout unit.")
 65        separator()
 66
 67        // ---- A: two side-by-side groups via same_line ----
 68        text("A) Two groups + same_line: each block stacks vertically; same_line")
 69        text("   between them keeps the right block's top aligned with the left.")
 70        group(LEFT_GRP) {
 71            text("Left block")
 72            checkbox(L_WIRE, (text = "Wireframe"))
 73            checkbox(L_GRID, (text = "Show grid"))
 74            // Narrow the slider so LEFT_GRP leaves enough room for RIGHT_GRP
 75            // on the same row at FontGlobalScale 1.4 + 680 px content width.
 76            SetNextItemWidth(160.0f)
 77            slider_int(L_FRAMES, (text = "frame limit"))
 78        }
 79        same_line((spacing = 24.0f))
 80        group(RIGHT_GRP) {
 81            text("Right block")
 82            checkbox(R_VSYNC, (text = "VSync"))
 83            checkbox(R_FULL, (text = "Fullscreen"))
 84            small_button(R_APPLY, (text = "Apply"))
 85        }
 86        separator()
 87
 88        // ---- B: group as a tooltip hit-test surface ----
 89        text("B) Hit-test a whole cluster - hover anywhere in the row below.")
 90        group(HOVER_GRP) {
 91            text("[i] Info")
 92            same_line()
 93            text("hover me anywhere in this row")
 94            same_line()
 95            small_button(HOVER_BTN, (text = "Action"))
 96        }
 97        if (IsItemHovered(ImGuiHoveredFlags.None)) {
 98            tooltip(GRP_TIP) {
 99                text("BeginTooltip while the whole group is hovered.")
100                text("IsItemHovered() after EndGroup() reports against the combined bbox.")
101            }
102        }
103        separator()
104
105        // ---- C: group used as a "row" with right-aligned status ----
106        text("C) The (label | value | button) row pattern.")
107        group(ROW_GRP) {
108            text("Speed:")
109            same_line()
110            slider_float(ROW_SPEED, (text = "##speed"))
111            same_line()
112            small_button(ROW_RESET, (text = "Reset"))
113        }
114        text("ROW_SPEED.value = {ROW_SPEED.value}")
115    }
116
117    end_of_frame()
118    Render()
119    var w, h : int
120    live_get_framebuffer_size(w, h)
121    glViewport(0, 0, w, h)
122    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
123    glClear(GL_COLOR_BUFFER_BIT)
124    live_imgui_render()
125
126    live_end_frame()
127}
128
129[export]
130def shutdown() {
131    live_imgui_shutdown()
132    live_destroy_window()
133}
134
135[export]
136def main() {
137    init()
138    while (!exit_requested()) {
139        update()
140    }
141    shutdown()
142}

8.18.1.16.1.1. Requires

Already in the baseline boost layer:

  • imgui/imgui_containers_builtingroup plus tooltip, same_line, separator.

  • imgui/imgui_widgets_builtin — leaves used inside the group.

8.18.1.16.1.2. Two columns via same_line

The classic use: stack two column blocks side by side without manually computing column widths.

group(LEFT_GRP) {
    text("Left block")
    checkbox(L_WIRE, (text = "Wireframe"))
    // Narrow the slider so the left group leaves room for the
    // right group on the same row.
    SetNextItemWidth(160.0f)
    slider_int(L_FRAMES, (text = "frame limit"))
}
same_line((spacing = 24.0f))
group(RIGHT_GRP) {
    text("Right block")
    checkbox(R_VSYNC, (text = "VSync"))
    small_button(R_APPLY, (text = "Apply"))
}

Without the groups, same_line would only join the previous widget (the slider) to the next, leaving the rest of the right block to overlap. The group treats four widgets as one item for same_line’s “continue from the right edge” semantics.

8.18.1.16.1.3. Hit-testing a cluster

IsItemHovered after EndGroup applies to the whole group’s bbox. The tutorial uses it to gate a manual tooltip:

group(HOVER_GRP) {
    text("[i] Info")
    same_line()
    text("hover me anywhere in this row")
    same_line()
    small_button(HOVER_BTN, (text = "Action"))
}
if (IsItemHovered(ImGuiHoveredFlags.None)) {
    tooltip(GRP_TIP) {
        text("BeginTooltip while the whole group is hovered.")
    }
}

Hovering the icon, the label, the button — any of them — fires the tooltip. No need for IsItemHovered on each child or for Set... rectangle hacks. The group’s bbox subsumes everything.

Note that HOVER_BTN is still independently clickable. Group hit-test reads over the cluster but does NOT block child events.

8.18.1.16.1.4. The (label | value | button) row

Compose three primitives into a logical row:

group(ROW_GRP) {
    text("Speed:")
    same_line()
    slider_float(ROW_SPEED, (text = "##speed"))
    same_line()
    small_button(ROW_RESET, (text = "Reset"))
}

The group keeps the three on one line if it fits; even if a wider label later forces a wrap, the group stays cohesive — subsequent same_line calls outside the group treat it as one unit. ROW_SPEED.value and ROW_RESET.click_count register under their respective paths (ROW_GRP/ROW_SPEED, ROW_GRP/ROW_RESET) for driver access.

8.18.1.16.1.5. Why no flags?

ImGui’s BeginGroup/EndGroup take no parameters. The wrapper’s only argument is the IDENT. GroupState carries an @optional _placeholder field so the [container] auto-emitter has something to serialize — the field is unused and the JV payload is {}.

8.18.1.16.1.6. Standalone vs live

Same convention as the other tutorials.

8.18.1.16.1.7. Driving from outside

The group itself isn’t open/close-driveable (nothing to open). Its children are reachable at the composed path:

curl -X POST -d '{"name":"imgui_force_set","args":{"target":"GRP_WIN/ROW_GRP/ROW_SPEED","value":0.75}}' \
     localhost:9090/command
curl -X POST -d '{"name":"imgui_click","args":{"target":"GRP_WIN/ROW_GRP/ROW_RESET"}}' \
     localhost:9090/command

See also

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

Features-side demo: modules/dasImgui/examples/features/containers_window.das — uses group alongside child to lay out a window’s right pane.

Sibling: Containers — umbrella container tour.

Boost macros — the macro layer.