Layout primitives
ImGui’s layout cursor advances after every widget — each button,
text, or other rail bumps the cursor to the next line by default.
Four boost rails move the cursor without rendering anything visible:
same_line(IDENT)— keep the next widget on the current row (wrapsImGui::SameLine).spacing(IDENT)— insert one line of vertical gap.new_line(IDENT)— advance one full text-line of vertical space.dummy((size = float2(x, y)))— reserve arbitrary cursor space without rendering.
All four accept the [widget]-style optional IDENT; the tutorial
shows IDENTs on same_line / spacing / new_line (useful when
playwright tests assert their snapshot entries) and the anonymous
dummy form (cursor reservation that no test targets).
All four share EmptyMarkerState — the payload is {}, but the
snapshot still records that the marker fired, so playwright tests can
assert "spacing#3 was rendered between button A and button B".
if (button(BTN_A, (text = "A"))) { /* ... */ }
same_line(SL_AB)
if (button(BTN_B, (text = "B"))) { /* ... */ }
same_line(SL_BC)
if (button(BTN_C, (text = "C"))) { /* ... */ }
spacing(SP_TOP)
new_line(NL_INSERT)
dummy((size = float2(0.0f, 40.0f)))
Source: examples/tutorial/layout_primitives.das.
Walkthrough
The markers render nothing, so the recording’s self-check is that each named
marker fired: it narrates the same_line row while asserting the three
buttons and both same_line markers are on screen, then the spacing and
new_line markers in turn (record_check_rendered on each). A marker that
silently dropped out of the layout would abort the recording.
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_visual_aids
17
18// =============================================================================
19// TUTORIAL: layout_primitives — boost v2 cursor-positioning widgets.
20//
21// Layout markers move the imgui cursor without rendering anything. They share
22// EmptyMarkerState — payload is `{}`, but the snapshot still records the
23// widget existed, so playwright can assert "spacing#3 was rendered".
24//
25// Covers: same_line / new_line / spacing / dummy
26//
27// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/layout_primitives.das
28// LIVE: daslang-live modules/dasImgui/examples/tutorial/layout_primitives.das
29// =============================================================================
30
31[export]
32def init() {
33 live_create_window("dasImgui layout primitives", 1000, 640)
34 live_imgui_init(live_window)
35 let io & = unsafe(GetIO())
36 GetStyle().FontScaleMain = 1.4
37}
38
39[export]
40def update() {
41 if (!live_begin_frame()) return
42 begin_frame()
43
44 ImGui_ImplGlfw_NewFrame()
45 apply_synth_io_override()
46 NewFrame()
47
48 SetNextWindowPos(ImVec2(60.0, 60.0), ImGuiCond.Always)
49 SetNextWindowSize(ImVec2(600.0, 360.0), ImGuiCond.Always)
50 window(LAYOUT_WIN, (text = "Layout primitives", closable = false,
51 flags = ImGuiWindowFlags.None)) {
52 text("Three buttons on one row - same_line() keeps them inline.")
53 if (button(BTN_A, (text = "A"))) {
54 print("A\n")
55 }
56 same_line(SL_AB)
57 if (button(BTN_B, (text = "B"))) {
58 print("B\n")
59 }
60 same_line(SL_BC)
61 if (button(BTN_C, (text = "C"))) {
62 print("C\n")
63 }
64
65 spacing(SP_TOP)
66 text("spacing() inserts a small vertical gap (1 line).")
67
68 spacing(SP_MID)
69 spacing(SP_MID2)
70 spacing(SP_MID3)
71 text("Three spacing() calls stack; cheap if you don't want a Dummy.")
72
73 new_line(NL_INSERT)
74 text("new_line() == one full text-line of vertical space.")
75
76 dummy((size = float2(0.0f, 40.0f)))
77 text("dummy(size) reserves arbitrary cursor space.")
78 }
79
80 end_of_frame()
81 Render()
82 var w, h : int
83 live_get_framebuffer_size(w, h)
84 glViewport(0, 0, w, h)
85 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
86 glClear(GL_COLOR_BUFFER_BIT)
87 live_imgui_render()
88
89 live_end_frame()
90}
91
92[export]
93def shutdown() {
94 live_imgui_shutdown()
95 live_destroy_window()
96}
97
98[export]
99def main() {
100 init()
101 while (!exit_requested()) {
102 update()
103 }
104 shutdown()
105}
Requires
Baseline boost layer (imgui/imgui_boost_v2 re-exports
imgui/imgui_layout_builtin). No extra modules.
When to reach for each
same_line is the workhorse — every multi-column row, every label-then-input
pattern uses it. Pass an explicit offset if the next widget needs a
column-aligned position; default packs against the previous item.
spacing is a minimal 1-line gap — cheaper to read than dummy when
you just want breathing room between sections. Stack three of them if you
want a slightly larger gap without committing to a fixed pixel height.
new_line is conceptually \n at the layout level — one full text
line. Useful when the current row had a tall widget and you want the next
row to start fresh from the left margin without accumulating Y from the
tall content.
dummy(size) reserves an arbitrary rectangle. Pass size = float2(0, 40)
for a 40-pixel-tall invisible spacer. The X component can pre-allocate a
horizontal slot too — useful for grid-like alignment when widgets vary in
width.
Snapshot shape
Each layout marker registers an entry under its ident with kind
"empty_marker":
curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command \
| jq '.globals."LAYOUT_WIN/SL_AB"'
Tests that want to verify “this layout was produced” can walk the
snapshot and confirm the markers fired in the expected order — see
tests/integration/test_layout_primitives.das for the assertion shape.
See also
Full source: examples/tutorial/layout_primitives.das
Companion tutorial: Layout — the higher-level layout
helpers (with_indent, with_item_width, with_text_wrap_pos).
Integration test: tests/integration/test_layout_primitives.das.
Boost macros — the macro layer.