8.18.34. Window size constraints
ImGui’s SetNextWindowSizeConstraints clamps the next window between a
min and max corner size. The 2-arg form is enough for a static box constraint;
the 3-arg form takes an ImGuiSizeCallback — a C function pointer ImGui
invokes on every resize so the callback can re-shape the requested size
(aspect-ratio lock, fixed-step quantization, “always square”, etc.).
The boost module imgui/imgui_window_constraints_builtin ships a daslang
wrapper around the 3-arg form: an ImGuiSizeConstraints struct that wraps
a daslang lambda + the existing C++ trampoline (das_invoke_lambda<void>
at src/dasIMGUI.main.cpp:361). End-user code passes the wrapper directly:
let aspect = @ capture(= ratio) (var data : ImGuiSizeCallbackData) : void {
data.DesiredSize = float2(data.DesiredSize.x,
data.DesiredSize.x / ratio)
}
SetNextWindowSizeConstraints(float2(0,0), float2(FLT_MAX, FLT_MAX),
ImGuiSizeConstraints(aspect))
The 2-arg SetNextWindowSizeConstraints(min, max) form stays on the
boost-surface allow-list — only require this module when you need the
callback form.
Source: modules/dasImgui/examples/tutorial/window_size_constraints.das.
8.18.34.1. Walkthrough
The recording drags each window by its bottom-right corner - the real “drag border” gesture - to a shape that violates its constraint, and the callback snaps it back: the first window, dragged to a wide-short rectangle, springs to a 400x400 square; the second, dragged tall, collapses to 480x270 (16:9); the third, dragged off-grid, lands on 350x200 (nearest 50). Each snapped size is asserted - a drag that didn’t resize, or a callback that didn’t reshape the request, aborts the recording.
1options gen2
2options _comment_hygiene = true
3options gc
4
5require imgui
6require imgui_app
7require opengl/opengl_boost
8require live/glfw_live
9require live/live_api
10require live/live_commands
11require live/live_vars
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_window_constraints_builtin
19require imgui/imgui_visual_aids
20require math
21
22// =============================================================================
23// TUTORIAL: window_size_constraints — lambda-callback overload for
24// ImGui::SetNextWindowSizeConstraints.
25//
26// The 2-arg form `SetNextWindowSizeConstraints(min, max)` is plain-allowed
27// at user-call sites — clamp the window between two corner sizes. The 3-arg
28// form takes a `ImGuiSizeCallback`, a C function pointer ImGui invokes on
29// every resize so the callback can re-shape the requested size (aspect-ratio
30// lock, fixed step quantization, "always square", ...).
31//
32// The boost module `imgui/imgui_window_constraints_builtin` provides a
33// daslang-side `ImGuiSizeConstraints` struct + ctor that wraps a daslang
34// lambda; the matching 3-arg overload threads the wrapper through the
35// existing C++ trampoline at `src/dasIMGUI.main.cpp:361-384`. End-user code
36// can then write:
37//
38// let aspect = @ capture(= ratio) (var data : ImGuiSizeCallbackData) : void {
39// data.DesiredSize.y = data.DesiredSize.x / ratio
40// }
41// SetNextWindowSizeConstraints(float2(0,0), float2(FLT_MAX, FLT_MAX),
42// ImGuiSizeConstraints(aspect))
43//
44// This tutorial mounts three resizable windows side-by-side — square,
45// aspect-16:9, fixed-step-50 — each with a body line printing its current
46// (width, height) so resizing visibly snaps to the constraint.
47//
48// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/window_size_constraints.das
49// LIVE: daslang-live modules/dasImgui/examples/tutorial/window_size_constraints.das
50// =============================================================================
51
52// Module-scope holders for the three ImGuiSizeConstraints. ImGui stores the
53// callback pointer + user-data for use by the NEXT Begin(); the wrapper
54// struct's address must outlive the SetNextWindowSizeConstraints call.
55// Stack-locals don't survive long enough — auto-fit invokes the callback
56// from inside Begin AFTER the wrapper function has returned. Module
57// scope keeps the struct alive for the lifetime of the program.
58var private SQUARE_CN : ImGuiSizeConstraints
59var private ASPECT_CN : ImGuiSizeConstraints
60var private STEP_CN : ImGuiSizeConstraints
61
62[export]
63def init() {
64 live_create_window("dasImgui window_size_constraints tutorial", 980, 560)
65 live_imgui_init(live_window)
66 // Relies on FirstUseEver window sizes; disable imgui.ini so the documented
67 // layout (and recording) is deterministic every run instead of restoring a
68 // prior session. Tutorial-scoped on purpose — not a live_imgui_init change.
69 DisableIniPersistence()
70 let io & = unsafe(GetIO())
71 GetStyle().FontScaleMain = 1.2
72
73 // Seed the three constraints once at init — daslang lambdas hold their
74 // own capture state, so a single per-shape struct is enough for all
75 // future Begin() invocations of the matching window.
76 SQUARE_CN <- ImGuiSizeConstraints(@(var data : ImGuiSizeCallbackData) : void {
77 let s = max(data.DesiredSize.x, data.DesiredSize.y)
78 data.DesiredSize = float2(s, s)
79 })
80 let ratio = 16.0f / 9.0f
81 ASPECT_CN <- ImGuiSizeConstraints(@ capture(= ratio) (var data : ImGuiSizeCallbackData) : void {
82 data.DesiredSize = float2(data.DesiredSize.x,
83 float(int(data.DesiredSize.x / ratio)))
84 })
85 let step = 50.0f
86 STEP_CN <- ImGuiSizeConstraints(@ capture(= step) (var data : ImGuiSizeCallbackData) : void {
87 let rx = float(int(data.DesiredSize.x / step + 0.5f)) * step
88 let ry = float(int(data.DesiredSize.y / step + 0.5f)) * step
89 data.DesiredSize = float2(rx, ry)
90 })
91}
92
93[export]
94def update() {
95 if (!live_begin_frame()) return
96 begin_frame()
97
98 ImGui_ImplGlfw_NewFrame()
99 apply_synth_io_override()
100 NewFrame()
101
102 // -------- 1. Square — max(w, h) wins both dims. --------
103 SetNextWindowPos(ImVec2(20.0f, 60.0f), ImGuiCond.FirstUseEver)
104 SetNextWindowSize(ImVec2(240.0f, 240.0f), ImGuiCond.FirstUseEver)
105 SetNextWindowSizeConstraints(float2(120.0f, 120.0f), float2(FLT_MAX, FLT_MAX),
106 SQUARE_CN)
107 window(SQUARE_WIN, (text = "Square", closable = false,
108 flags = ImGuiWindowFlags.None)) {
109 let s = SQUARE_WIN.size
110 text(SQUARE_TEXT, (text = "Drag border - always square.\n"
111 + "Size: {int(s.x)} x {int(s.y)}"))
112 }
113
114 // -------- 2. Aspect ratio 16:9 — height = width / aspect. --------
115 SetNextWindowPos(ImVec2(290.0f, 60.0f), ImGuiCond.FirstUseEver)
116 SetNextWindowSize(ImVec2(320.0f, 180.0f), ImGuiCond.FirstUseEver)
117 SetNextWindowSizeConstraints(float2(160.0f, 0.0f), float2(FLT_MAX, FLT_MAX),
118 ASPECT_CN)
119 window(ASPECT_WIN, (text = "Aspect 16:9", closable = false,
120 flags = ImGuiWindowFlags.None)) {
121 let s = ASPECT_WIN.size
122 text(ASPECT_TEXT, (text = "Drag border - height tracks width / 16:9.\n"
123 + "Size: {int(s.x)} x {int(s.y)}"))
124 }
125
126 // -------- 3. Fixed step 50 — both dims snap to nearest 50 px. --------
127 SetNextWindowPos(ImVec2(640.0f, 60.0f), ImGuiCond.FirstUseEver)
128 SetNextWindowSize(ImVec2(300.0f, 250.0f), ImGuiCond.FirstUseEver)
129 SetNextWindowSizeConstraints(float2(100.0f, 100.0f), float2(FLT_MAX, FLT_MAX),
130 STEP_CN)
131 window(STEP_WIN, (text = "Step 50", closable = false,
132 flags = ImGuiWindowFlags.None)) {
133 let s = STEP_WIN.size
134 text(STEP_TEXT, (text = "Drag border - both dims snap to 50 px.\n"
135 + "Size: {int(s.x)} x {int(s.y)}"))
136 }
137
138 end_of_frame()
139 Render()
140 var w, h : int
141 live_get_framebuffer_size(w, h)
142 glViewport(0, 0, w, h)
143 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
144 glClear(GL_COLOR_BUFFER_BIT)
145 live_imgui_render()
146
147 live_end_frame()
148}
149
150[export]
151def shutdown() {
152 live_imgui_shutdown()
153 live_destroy_window()
154}
155
156[export]
157def main() {
158 init()
159 while (!exit_requested()) {
160 update()
161 maybe_collect_gc()
162 }
163 shutdown()
164}
8.18.34.1.1. Requires
One extra module on top of the baseline boost layer:
imgui/imgui_window_constraints_builtin— theImGuiSizeConstraintsstruct + ctor + 3-arg overload ofSetNextWindowSizeConstraints.
8.18.34.1.2. The three callback patterns
The tutorial mounts three resizable windows demonstrating the canonical callback shapes:
Square — max(w, h) wins both dims:
let sq <- @ (var data : ImGuiSizeCallbackData) : void {
let s = max(data.DesiredSize.x, data.DesiredSize.y)
data.DesiredSize = float2(s, s)
}
Aspect 16:9 — height tracks width / aspect_ratio:
let ratio = 16.0f / 9.0f
let aspect <- @ capture(= ratio) (var data : ImGuiSizeCallbackData) : void {
data.DesiredSize = float2(data.DesiredSize.x,
data.DesiredSize.x / ratio)
}
Fixed step — both dims snap to the nearest multiple:
let step = 50.0f
let snap <- @ capture(= step) (var data : ImGuiSizeCallbackData) : void {
let rx = float(int(data.DesiredSize.x / step + 0.5f)) * step
let ry = float(int(data.DesiredSize.y / step + 0.5f)) * step
data.DesiredSize = float2(rx, ry)
}
Capture rules follow the standard daslang lambda surface — capture(= var)
for by-value, capture(& var) for by-reference. The @ (no-capture)
form works for callbacks that read only data.
8.18.34.1.3. Struct layout
The ImGuiSizeConstraints daslang struct mirrors
das::DasImGuiSizeConstraints in src/dasIMGUI.main.cpp field-by-field:
struct ImGuiSizeConstraints {
context : Context?
callback : lambda<(var data : ImGuiSizeCallbackData) : void>
at : LineInfo?
}
The C++ trampoline reads field offsets directly to dispatch the lambda
through das_invoke_lambda<void>::invoke<ImGuiSizeCallbackData*>. Don’t
re-order the fields — the binding is not symbolic.
8.18.34.1.4. Standalone vs live
Same convention as previous tutorials. Resize any of the three windows by dragging a border or corner — the callback fires on every drag delta and quantizes the result.
See also
Full source: modules/dasImgui/examples/tutorial/window_size_constraints.das
app_small ShowExampleAppConstrainedResize (mirrors ImGui’s
imgui_demo.cpp:7885-7978) exercises 9 constraint options across both
overload forms: examples/imgui_demo/app_small.das.
Integration test: modules/dasImgui/tests/test_app_small_constrained_resize.das.
Boost macros — the macro layer.