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

Walkthrough

layout recording
  1options gen2
  2
  3require imgui
  4require imgui_app
  5require glfw/glfw_boost
  6require opengl/opengl_boost
  7require live/glfw_live
  8require live/live_api
  9require live/live_commands
 10require live/live_vars
 11require live/opengl_live
 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_set","args":{"target":"LAYOUT_WIN/SPLIT_VERT/SPLIT_MAIN","value":0.7}}' localhost:9090/command
 39//   curl -X POST -d '{"name":"imgui_set","args":{"target":"LAYOUT_WIN/SPLIT_VERT","value":0.4}}'           localhost:9090/command
 40//   curl -X POST -d '{"name":"imgui_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    var io & = unsafe(GetIO())
 52    io.FontGlobalScale = 1.5
 53}
 54
 55[export]
 56def update() {
 57    if (!live_begin_frame()) return
 58    begin_frame()
 59
 60    ImGui_ImplOpenGL3_NewFrame()
 61    ImGui_ImplGlfw_NewFrame()
 62    apply_synth_io_override()
 63    NewFrame()
 64
 65    // One window holds the whole layout. dock_left carves out the sidebar;
 66    // split_v then split_h compose the main pane.
 67    SetNextWindowPos(ImVec2(60.0f, 60.0f), ImGuiCond.Always)
 68    SetNextWindowSize(ImVec2(900.0f, 600.0f), ImGuiCond.Always)
 69    window(LAYOUT_WIN, (text = "IDE layout",
 70                        closable = false,
 71                        flags = ImGuiWindowFlags.None)) {
 72
 73        // Left rail: 200 px by default, draggable between 80 and 320 px.
 74        dock_left(SIDEBAR, (init = 200.0f, bounds = (80.0f, 320.0f))) {
 75            text("Sidebar")
 76            spacing(LO_SP_1)
 77            button(EXPLORE_BTN, (text = "Explore"))
 78            button(SEARCH_BTN,  (text = "Search"))
 79            button(SOURCE_BTN,  (text = "Source"))
 80        }
 81
 82        // Main pane: top/bottom (split_v) where top is left/right (split_h).
 83        split_v(SPLIT_VERT, (init = 0.65f, bounds = (0.1f, 0.9f)),
 84                ${
 85                    split_h(SPLIT_MAIN, (init = 0.4f, bounds = (0.1f, 0.9f)),
 86                            ${
 87                                text("Files")
 88                                button(FILE_A_BTN, (text = "main.das"))
 89                                button(FILE_B_BTN, (text = "lib.das"))
 90                            },
 91                            ${
 92                                text("Editor")
 93                                text("// drag the splitters")
 94                                text("// — the pane bounds")
 95                                text("//   stay clipped.")
 96                            })
 97                },
 98                ${
 99                    text("Output")
100                    text("> ready.")
101                    button(CLEAR_BTN, (text = "Clear output"))
102                })
103    }
104
105    end_of_frame()
106    Render()
107    var w, h : int
108    live_get_framebuffer_size(w, h)
109    glViewport(0, 0, w, h)
110    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
111    glClear(GL_COLOR_BUFFER_BIT)
112    ImGui_ImplOpenGL3_RenderDrawData(GetDrawData())
113
114    live_end_frame()
115}
116
117[export]
118def shutdown() {
119    live_imgui_shutdown()
120    live_destroy_window()
121}
122
123[export]
124def main() {
125    init()
126    while (!exit_requested()) {
127        update()
128    }
129    shutdown()
130}

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).

Init and shutdown

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

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.

Layout helpers

The panel composes three helpers, nested:

window(LAYOUT_WIN, (text = "IDE layout", ...)) {
    dock_left(SIDEBAR, (init = 200.0f, bounds = (80.0f, 320.0f))) {
        // sidebar contents
    }
    split_v(SPLIT_VERT, (init = 0.65f, bounds = (0.1f, 0.9f)),
        ${ split_h(SPLIT_MAIN, ...) { ... } },
        ${ /* bottom pane */ })
}

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).

Standalone vs live

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

Driving from outside

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

curl -X POST -d '{"name":"imgui_set","args":{"target":"SIDEBAR","value":260.0}}' \
     localhost:9090/command
curl -X POST -d '{"name":"imgui_set","args":{"target":"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_*.

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) { TextUnformatted(LIPSUM) }   // window right edge
with_text_wrap_pos(200.0f) { TextUnformatted(LIPSUM) } // wrap at 200 px

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

Standalone vs live

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

See also

Full source: examples/tutorial/layout.das

Richer reference: 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.