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