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