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
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— thewindowmacro that wraps the layout (boostwindowis more featureful than rawBegin/End; it providesclosable,flags, registered state, and an implicitEndat 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 tour — main() 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.