8.18.1.15. Child windows

child brackets BeginChild/EndChild into a block-arg container — a sub-window that can be sized, bordered, scrolled, and addressed in the registry hierarchy. The wrapper’s signature:

child(IDENT, (text = "...",
              size = float2(w, h),
              child_flags = ImGuiChildFlags....,
              window_flags = ImGuiWindowFlags....)) {
    // body — leaves register under <window>/IDENT
}

Three knobs do most of the work — size, child_flags, window_flags — and the boost layer captures scroll / scroll_max each frame so a snapshot reports the current scroll position without polling.

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

8.18.1.15.1. Walkthrough

The recording drives each of the three children with real synthetic input and self-verifies. It parks the cursor inside the bordered child A and wheel-scrolls it, asserting SCROLL_A’s scroll payload actually moved; toggles the VSync checkbox inside the AutoResizeY child B; then wheel-scrolls the horizontal child C sideways, asserting SCROLL_C’s scroll moved. The scroll assertions read the boost layer’s per-frame scroll telemetry directly — a no-op scroll (cursor not over the child, wheel not attributed) would abort the recording.

  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
 19var private SCROLL_ROW : table<int; NarrativeState>
 20
 21// nolint:STYLE014 — file-header tutorial banner displaced below a var decl
 22// =============================================================================
 23// TUTORIAL: child — BeginChild/EndChild sub-windows.
 24//
 25//   child(IDENT, (text = "..", size = float2(w, h),
 26//                 child_flags = ..., window_flags = ...)) { body }
 27//
 28// child is the workhorse for nesting a scrollable, optionally-bordered region
 29// inside another window. Three knobs:
 30//
 31//   1. size — float2(w, h). (0,0) auto-fits to the available row/column
 32//      depending on flags. Positive = explicit pixels. Negative = "fill the
 33//      remainder, minus N pixels".
 34//
 35//   2. child_flags — ImGui's child-specific bitfield: Border, AlwaysAutoResize,
 36//      AutoResizeX/Y, FrameStyle, NavFlattened, ResizeX/Y.
 37//
 38//   3. window_flags — same flags every other window takes. Scrollbar control
 39//      lives here: HorizontalScrollbar, AlwaysVerticalScrollbar, etc.
 40//
 41// ChildState observed each frame: scroll (float2 current), scroll_max (float2
 42// max). The body's render runs only while BeginChild returned true; scroll is
 43// captured INSIDE that gate so a hidden child reports the last-known values.
 44//
 45// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/child.das
 46// LIVE:       daslang-live modules/dasImgui/examples/tutorial/child.das
 47// =============================================================================
 48
 49[export]
 50def init() {
 51    live_create_window("dasImgui child tutorial", 800, 540)
 52    live_imgui_init(live_window)
 53    let io & = unsafe(GetIO())
 54    GetStyle().FontScaleMain = 1.4
 55}
 56
 57[export]
 58def update() {
 59    if (!live_begin_frame()) return
 60    begin_frame()
 61
 62    ImGui_ImplGlfw_NewFrame()
 63    apply_synth_io_override()
 64    NewFrame()
 65
 66    SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
 67    SetNextWindowSize(ImVec2(760.0f, 500.0f), ImGuiCond.Always)
 68    window(CHILD_WIN, (text = "child tutorial", closable = false,
 69                       flags = ImGuiWindowFlags.None)) {
 70
 71        text("Three child variants - one each for borders, autosize, and h-scroll.")
 72        separator()
 73
 74        // ---- A: bordered, fixed-size, vertical scroll ----
 75        text("A) Bordered fixed-size with vertical scroll")
 76        child(SCROLL_A, (text = "scroll_a",
 77                         size = float2(360.0f, 140.0f),
 78                         child_flags = ImGuiChildFlags.Borders,
 79                         window_flags = ImGuiWindowFlags.None)) {
 80            for (i in range(20)) {
 81                text(SCROLL_ROW[i], (text = "row {i} - content overflows the visible region"))
 82            }
 83        }
 84        text("scroll.y = {SCROLL_A.scroll.y} / {SCROLL_A.scroll_max.y}")
 85        spacing()
 86
 87        // ---- B: auto-resize Y, FrameStyle (input-group look) ----
 88        text("B) AutoResizeY + FrameStyle - height tracks content")
 89        child(AUTO_B, (text = "auto_b",
 90                       size = float2(360.0f, 0.0f),
 91                       child_flags = ImGuiChildFlags.AutoResizeY | ImGuiChildFlags.FrameStyle,
 92                       window_flags = ImGuiWindowFlags.None)) {
 93            text("Three rows of input chrome")
 94            checkbox(B_VSYNC, (text = "VSync"))
 95            slider_int(B_LIMIT, (text = "frame limit"))
 96        }
 97        // AutoResizeY trims overflow to zero, so scroll = (0,0) and
 98        // scroll_max = (0,0). The child's actual size lives in size.y;
 99        // print scroll fields directly to show "no overflow".
100        text("AUTO_B.scroll = ({AUTO_B.scroll.x}, {AUTO_B.scroll.y}); scroll_max = ({AUTO_B.scroll_max.x}, {AUTO_B.scroll_max.y})")
101        spacing()
102
103        // ---- C: bordered, horizontal scroll ----
104        text("C) Border + horizontal scroll - wide content")
105        child(SCROLL_C, (text = "scroll_c",
106                        size = float2(720.0f, 90.0f),
107                        child_flags = ImGuiChildFlags.Borders,
108                        window_flags = ImGuiWindowFlags.HorizontalScrollbar)) {
109            // One long line — forces horizontal scroll because no wrap.
110            text(LONG_LINE, (text = "very wide content: lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo"))
111            text("(scroll horizontally to read; scroll.x mirrors below)")
112        }
113        text("scroll.x = {SCROLL_C.scroll.x} / {SCROLL_C.scroll_max.x}")
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}

8.18.1.15.1.1. Requires

Already in the baseline boost layer:

  • imgui/imgui_containers_builtin — defines child, child_scroll_reenter.

  • imgui/imgui_widgets_builtintext, checkbox, slider_int.

8.18.1.15.1.2. size — three sizing modes

The size : float2(w, h) argument has three flavors:

  • Explicit positivesize = float2(360.0f, 140.0f) — fixed pixel size. The body’s content is clipped to that region and scrollbars appear if it overflows.

  • Zerosize = float2(0.0f, 0.0f) — auto-fit to the available column/row. Pair with ImGuiChildFlags.AutoResizeX/Y to let the body grow to the content extent instead.

  • Negativesize = float2(-FLT_MIN, 0.0f) — “fill the remaining space minus that many pixels”. Useful for stretchy panes that should fit the parent.

8.18.1.15.1.3. child_flags vs window_flags

ImGuiChildFlags (the third arg) is child-specific:

  • Border — draw a one-pixel border around the region.

  • AutoResizeX / AutoResizeY — height/width tracks content extent.

  • AlwaysAutoResize — combine both axes and re-measure every frame.

  • FrameStyle — frame-style chrome (like input groups). Implies a background and rounded corners drawn from the active style.

  • NavFlattened — keyboard nav treats the child as part of the parent.

ImGuiWindowFlags (the fourth arg) governs the child’s scrollbar policy and window-level behavior:

  • HorizontalScrollbar — enable the bottom scroll track when content overflows horizontally (default = none).

  • AlwaysVerticalScrollbar / AlwaysHorizontalScrollbar — keep the bar visible even when the content fits.

  • NoMove / NoResize / NoBackground — standard window knobs.

The two flag sets compose freely; pick from each based on the child’s chrome and scroll needs.

8.18.1.15.1.4. ChildState — what the snapshot reports

ChildState captures four floats:

var SCROLL_A : ChildState
// After the frame renders:
//   SCROLL_A.size       : float2  // sticky — what was passed in
//   SCROLL_A.scroll     : float2  // current scroll (GetScrollX/Y)
//   SCROLL_A.scroll_max : float2  // GetScrollMaxX/Y for this content
//   SCROLL_A.child_flags / SCROLL_A.window_flags  // sticky

Snapshot reports them under <window>/SCROLL_A. Drivers that want to verify “the user scrolled to row 12” compare scroll.y against a known threshold. scroll_max is read AFTER the body invoke — it depends on the content extent, which the body just emitted.

8.18.1.15.1.5. Auto-resize variants

The B child uses AutoResizeY | FrameStyle:

child(AUTO_B, (text = "auto_b",
               size = float2(360.0f, 0.0f),
               child_flags = ImGuiChildFlags.AutoResizeY | ImGuiChildFlags.FrameStyle,
               window_flags = ImGuiWindowFlags.None)) {
    text("Three rows of input chrome")
    checkbox(B_VSYNC, (text = "VSync"))
    slider_int(B_LIMIT, (text = "frame limit"))
}

Size’s Y is 0.0f — required when AutoResizeY is active so ImGui treats the slot as auto. The height grows to fit the body each frame.

scroll and scroll_max both report (0, 0) for an AutoResize child: no overflow means no scroll range. ChildState.size is the caller-provided input — for AutoResizeY it stays at (360.0f, 0.0f), so it does NOT tell you the rendered height. The wrapper does not currently expose the auto-computed extent; if you need it, call GetWindowSize() inside the child body, or place a sentinel widget at the end and read its bbox bottom from the snapshot.

8.18.1.15.1.6. Horizontal scroll

window_flags.HorizontalScrollbar enables the bottom track:

child(SCROLL_C, (text = "scroll_c",
                size = float2(720.0f, 90.0f),
                child_flags = ImGuiChildFlags.Border,
                window_flags = ImGuiWindowFlags.HorizontalScrollbar)) {
    text(LONG_LINE, (text = "very wide content: lorem ipsum..."))
}

Without the flag, overlong single-line text would be clipped. With it, the user drags the bottom bar and SCROLL_C.scroll.x mirrors the position.

8.18.1.15.1.7. Standalone vs live

Same convention as the other tutorials.

8.18.1.15.1.8. Driving from outside

External drivers can wheel-scroll the active child by posting an imgui_mouse_scroll command (the live counterpart to ImGui_ImplGlfw’s wheel events):

curl -X POST -d '{"name":"imgui_mouse_scroll","args":{"x":0.0,"y":-3.0}}' \
     localhost:9090/command

The cursor must be over the child window for ImGui to attribute the scroll. The recording driver uses this exact channel to nudge SCROLL_A between narration stages.

See also

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

Features-side demo: modules/dasImgui/examples/features/child_scroll_reenter.das — the child_scroll_reenter helper for nudging an existing child’s scroll from outside its block.

Sibling: Containers — the umbrella tour of container rails.

Boost macros — the macro layer.