8.19.7. Layout

Three boost helpers — dock_left, split_h, split_v — compose into an IDE-style layout on a single panel. Each helper takes a state struct, an init value, optional bounds, and a block per pane; the resulting splits are draggable at runtime and survive live reload.

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

8.19.7.1. Walkthrough

The recording drives the layout with real synthetic drags on the splitter handles — no imgui_force_set. It grabs the sidebar’s right edge and pulls it wider (asserting SIDEBAR’s pixel width rose), drags the vertical handle between the Files and Editor panes (left-pane fraction rose), then drags the horizontal handle down so the editor grows and the output shrinks (top fraction rose). Each handle surfaces its own bbox as a <container>/HANDLE alias in the snapshot, so the cursor targets the real 8 px InvisibleButton; a drag that moved nothing aborts the recording at teardown.

  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: layout — split_h / split_v / dock_left on a single panel.
 23//
 24// Three boost layout helpers compose into a classic IDE-style layout:
 25//   SIDEBAR     : dock_left   — fixed-width left rail with a draggable edge
 26//   SPLIT_VERT  : split_v     — splits the main area into top + bottom
 27//   SPLIT_MAIN  : split_h     — splits the top area into left + right
 28//
 29// Each helper takes a state struct (registered automatically), an `init`
 30// value, optional `bounds`, and a block (`${ ... }`) per pane. The state's
 31// `value` field holds either the split fraction (split_*) or the pane width
 32// in pixels (dock_*). All three are draggable at runtime.
 33//
 34// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/layout.das
 35// LIVE:       daslang-live modules/dasImgui/examples/tutorial/layout.das
 36//
 37// DRIVE (when running live):
 38//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"LAYOUT_WIN/SPLIT_VERT/SPLIT_MAIN","value":0.7}}' localhost:9090/command
 39//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"LAYOUT_WIN/SPLIT_VERT","value":0.4}}'           localhost:9090/command
 40//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"LAYOUT_WIN/SIDEBAR","value":220.0}}'            localhost:9090/command
 41//
 42// Note: the boost ``window(LAYOUT_WIN, ...)`` wrapper pushes the window's
 43// name as a path prefix on every nested widget, so live targets are
 44// path-qualified (no bare ``SIDEBAR``).
 45// =============================================================================
 46
 47[export]
 48def init() {
 49    live_create_window("dasImgui layout tutorial", 1024, 720)
 50    live_imgui_init(live_window)
 51    let io & = unsafe(GetIO())
 52    GetStyle().FontScaleMain = 1.5
 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    // One window holds the whole layout. dock_left carves out the sidebar;
 65    // split_v then split_h compose the main pane.
 66    SetNextWindowPos(ImVec2(60.0f, 60.0f), ImGuiCond.Always)
 67    SetNextWindowSize(ImVec2(900.0f, 600.0f), ImGuiCond.Always)
 68    window(LAYOUT_WIN, (text = "IDE layout",
 69                        closable = false,
 70                        flags = ImGuiWindowFlags.None)) {
 71
 72        // Left rail: 200 px by default, draggable between 80 and 320 px.
 73        dock_left(SIDEBAR, (init = 200.0f, bounds = (80.0f, 320.0f))) {
 74            text("Sidebar")
 75            spacing(LO_SP_1)
 76            button(EXPLORE_BTN, (text = "Explore"))
 77            button(SEARCH_BTN,  (text = "Search"))
 78            button(SOURCE_BTN,  (text = "Source"))
 79        }
 80
 81        // Main pane: top/bottom (split_v) where top is left/right (split_h).
 82        split_v(SPLIT_VERT, (init = 0.65f, bounds = (0.1f, 0.9f)),
 83                ${
 84                    split_h(SPLIT_MAIN, (init = 0.4f, bounds = (0.1f, 0.9f)),
 85                            ${
 86                                text("Files")
 87                                button(FILE_A_BTN, (text = "main.das"))
 88                                button(FILE_B_BTN, (text = "lib.das"))
 89                            },
 90                            ${
 91                                text("Editor")
 92                                text("// drag the splitters -")
 93                                text("// the pane bounds")
 94                                text("// stay clipped.")
 95                            })
 96                },
 97                ${
 98                    text("Output")
 99                    text("> ready.")
100                    button(CLEAR_BTN, (text = "Clear output"))
101                })
102    }
103
104    end_of_frame()
105    Render()
106    var w, h : int
107    live_get_framebuffer_size(w, h)
108    glViewport(0, 0, w, h)
109    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
110    glClear(GL_COLOR_BUFFER_BIT)
111    live_imgui_render()
112
113    live_end_frame()
114}
115
116[export]
117def shutdown() {
118    live_imgui_shutdown()
119    live_destroy_window()
120}
121
122[export]
123def main() {
124    init()
125    while (!exit_requested()) {
126        update()
127        maybe_collect_gc()
128    }
129    shutdown()
130}

8.19.7.1.1. Requires

Same backend + boost layer as Widgets tour, with two extra modules pulled in:

  • imgui/imgui_containers_builtin — the window macro that wraps the layout (boost window is more featureful than raw Begin/End; it provides closable, flags, registered state, and an implicit End at block exit).

  • imgui/imgui_layout_builtin — the layout helpers themselves (split_h, split_v, dock_left).

8.19.7.1.2. Init and shutdown

Identical to Widgets tour. 1024x720 window, font scale 1.5, standard live-reload pair.

8.19.7.1.3. The frame loop

Standard dasImgui v2 shape. apply_synth_io_override() between ImGui_ImplGlfw_NewFrame and NewFrame is required again so the driver script can synth drags against the splitters without the real GLFW mouse winning the IO race.

8.19.7.1.4. Layout helpers

The panel composes three helpers, nested:

window(LAYOUT_WIN, (text = "IDE layout", closable = false,
                    flags = ImGuiWindowFlags.None)) {
    dock_left(SIDEBAR, (init = 200.0f, bounds = (80.0f, 320.0f))) {
        text("Sidebar")
    }
    split_v(SPLIT_VERT, (init = 0.65f, bounds = (0.1f, 0.9f)),
            ${
                split_h(SPLIT_MAIN, (init = 0.4f, bounds = (0.1f, 0.9f)),
                        ${ text("Files") },
                        ${ text("Editor") })
            },
            ${ text("Output") })
}

dock_left carves a fixed-width column off the left edge. Its state.value is the pane width in pixels — clamped to the bounds tuple at drag time. After dock_left’s block, ImGui’s cursor is on the SameLine to the right of the rail, so subsequent content flows into the remaining area without explicit positioning.

split_v and split_h each take two block arguments rather than one. The ${ ... } literal is a block-with-no-args, and the boost macro takes the helpers’ panes by position. Their state.value is the first pane’s fraction of the available space, clamped to bounds (so init = 0.65f means the top half occupies 65% of the height).

8.19.7.1.5. Standalone vs live

Same as Widgets tourmain() runs the loop standalone; daslang-live invokes init / update / shutdown directly.

8.19.7.1.6. Driving from outside

Every helper’s state struct is targetable by name. Drag without a mouse:

curl -X POST -d '{"name":"imgui_force_set","args":{"target":"LAYOUT_WIN/SIDEBAR","value":260.0}}' \
     localhost:9090/command
curl -X POST -d '{"name":"imgui_force_set","args":{"target":"LAYOUT_WIN/SPLIT_VERT/SPLIT_MAIN","value":0.6}}' \
     localhost:9090/command

value is the same type the user would set by dragging — pixels for dock_*, fraction for split_*. Targets are path-qualified by the enclosing window/split chain (no bare SIDEBAR).

Each helper also exposes its drag handle’s geometry as a <container>/HANDLE alias in the snapshot, so a driver can perform a real drag instead of setting the fraction directly — LAYOUT_WIN/SIDEBAR/HANDLE and likewise for each split. The drag playwright helper targets it by bbox centre; this is how the recording above drives every split.

8.19.7.1.7. Scope wrappers

Three stateless block-arg wrappers in imgui/imgui_scope_builtin cover the leftover Push/Pop idioms ImGui uses for ad-hoc layout overrides. Each brackets the block with the corresponding ImGui Push/Pop pair and takes no state — they read like inline scopes:

require imgui/imgui_scope_builtin

// Indent / Unindent — nest content under a heading.
with_indent(0.0f) {       // 0.0f defers to style IndentSpacing
    text("Bullet child")
}
with_indent(40.0f) {      // explicit pixel offset
    text("Hard-indented")
}

// PushItemWidth / PopItemWidth — scope a widget-width override.
with_item_width(120.0f) {
    slider_float(NARROW, (text = "narrow", bounds = (0.0f, 1.0f)))
}
with_item_width(-60.0f) { // negative = right-edge minus N
    slider_float(STRETCH, (text = "stretch", bounds = (0.0f, 1.0f)))
}

// PushTextWrapPos / PopTextWrapPos — scope where long text wraps.
with_text_wrap_pos(0.0f) {          // window right edge
    text_unformatted(WRAP_EDGE, (text = LIPSUM))
}
with_text_wrap_pos(200.0f) {        // wrap at 200 px
    text_unformatted(WRAP_200, (text = LIPSUM))
}

Feature demos: modules/dasImgui/examples/features/with_indent.das, modules/dasImgui/examples/features/with_item_width.das, modules/dasImgui/examples/features/with_text_wrap_pos.das.

8.19.7.1.8. Next steps

Docking is next — full ImGui dockspaces and the dock helpers that ride on top of them.

See also

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

Richer reference: modules/dasImgui/examples/features/layout_helpers.das — same three helpers exercised with every option.

Previous tutorial: Widgets tour

Boost macros — the macro layer.

Builtin widgets — widget reference.