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: examples/tutorial/group.das.

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

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.

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.

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.

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.

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 {}.

Standalone vs live

Same convention as the other tutorials.

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: examples/tutorial/group.das

Features-side demo: 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.