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

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

Requires

Already in the baseline boost layer:

  • imgui/imgui_containers_builtin — defines child, child_scroll_reenter.

  • imgui/imgui_widgets_builtintext, checkbox, slider_int.

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.

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.

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.

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.

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.

Standalone vs live

Same convention as the other tutorials.

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

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