8.19.12. With disabled / font / button_repeat
Four stateless scope-wrappers landed alongside the v2 boost surface. They
share the same shape: a top-level function that takes a single value plus a
trailing block, pushes the corresponding ImGui scope on entry, pops on exit.
None of them emit telemetry — no [widget] / [container] annotation,
no idents — they exist to subsume the raw Push* / Pop* pairs that
v2’s lint flags as invisible state changes.
with_disabled(disabled, blk)—BeginDisabled/EndDisabled.with_font(font, size, blk)—PushFont(font, size)/PopFont;sizedefaults to0.0f(keep the current size), sowith_font(f) { ... }is a pure font swap.with_button_repeat(repeat, blk)—PushItemFlag(ImGuiItemFlags.ButtonRepeat, repeat)/PopItemFlag(ImGui obsoleted thePushButtonRepeatshorthand in 1.91).with_clip_rect(min, max, isect, blk)—PushClipRect/PopClipRect.
checkbox(ENABLED_MASTER, (text = "Enable child group"))
with_disabled(!ENABLED_MASTER.value) {
button(CHILD_SAVE, (text = "Save"))
button(CHILD_LOAD, (text = "Load"))
slider_float(CHILD_VOL, (text = "Volume"))
}
The block’s interior renders normally; the wrapper’s argument drives the
ImGui-stack push that surrounds it. Toggling ENABLED_MASTER flips
with_disabled’s argument and the child widgets re-render greyed (or
active) on the next frame.
Source: modules/dasImgui/examples/tutorial/with_disabled.das.
8.19.12.1. Walkthrough
The recording drives the with_disabled contrast with real synthetic
input and self-verifies each step. While the checkbox is off it clicks the
greyed Save button — nothing happens. It then ticks the checkbox on
(asserting the value flipped), clicks Save again, and asserts
CHILD_SAVE.click_count is now exactly one — proving the earlier
greyed click never registered. Finally it presses and holds the +
button under with_button_repeat and asserts the counter climbs twice
under the single hold — a genuine repeat stream, not one click. Any step
that failed to land would abort 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_scope_builtin
19require imgui/imgui_visual_aids
20
21// =============================================================================
22// TUTORIAL: with_disabled — stateless scope-wrapper family.
23//
24// Four new wrappers landed alongside the v2 boost surface. They share the
25// same shape: a top-level function that takes a single value + trailing
26// block, pushes the corresponding ImGui scope on entry, pops on exit. None
27// emit telemetry (no [widget] / [container] annotation, no idents).
28//
29// with_disabled(disabled, blk) — BeginDisabled / EndDisabled
30// with_font(font, blk) — PushFont / PopFont
31// with_button_repeat(repeat, blk) — PushButtonRepeat / PopButtonRepeat
32// with_clip_rect(min, max, isect, blk) — PushClipRect / PopClipRect
33//
34// They subsume the raw Push/Pop pairs that v2's lint flags as invisible.
35//
36// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/with_disabled.das
37// LIVE: daslang-live modules/dasImgui/examples/tutorial/with_disabled.das
38// =============================================================================
39
40[export]
41def init() {
42 live_create_window("dasImgui with_disabled family", 760, 520)
43 live_imgui_init(live_window)
44 let io & = unsafe(GetIO())
45 GetStyle().FontScaleMain = 1.4
46}
47
48[export]
49def update() {
50 if (!live_begin_frame()) return
51 begin_frame()
52
53 ImGui_ImplGlfw_NewFrame()
54 apply_synth_io_override()
55 NewFrame()
56
57 SetNextWindowPos(ImVec2(60.0, 60.0), ImGuiCond.Always)
58 SetNextWindowSize(ImVec2(640.0, 440.0), ImGuiCond.Always)
59 window(SCOPES_WIN, (text = "Scope wrappers", closable = false,
60 flags = ImGuiWindowFlags.None)) {
61 separator_text("with_disabled")
62
63 text("Toggle ENABLED_MASTER; child widgets follow.")
64 checkbox(ENABLED_MASTER, (text = "Enable child group"))
65
66 // The whole block inside `with_disabled(...)` is rendered greyed-out
67 // when the disabled flag is true; child clicks are inert.
68 with_disabled(!ENABLED_MASTER.value) {
69 if (button(CHILD_SAVE, (text = "Save"))) {
70 print("save\n")
71 }
72 same_line(SL_CHILD)
73 if (button(CHILD_LOAD, (text = "Load"))) {
74 print("load\n")
75 }
76 slider_float(CHILD_VOL, (text = "Volume"))
77 }
78 text("CHILD_SAVE: clicked {CHILD_SAVE.click_count} times")
79
80 separator_text("with_button_repeat")
81
82 // Inside with_button_repeat(true), holding the button fires click
83 // events repeatedly — useful for steppers.
84 text("Hold the button - click_count keeps rising.")
85 with_button_repeat(true) {
86 if (button(STEP_UP, (text = "+"))) {
87 print("step up: {STEP_UP.click_count}\n")
88 }
89 }
90
91 text("STEP_UP.click_count = {STEP_UP.click_count}")
92
93 separator_text("with_font")
94
95 // The default font is the same as GetFont() — change to any loaded
96 // font handle to switch scope-local typography.
97 text("with_font(...) swaps the active font for one scope.")
98 with_font(GetFont()) {
99 text(FONT_DEMO, (text = "Inside the with_font scope."))
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 maybe_collect_gc()
127 }
128 shutdown()
129}
8.19.12.1.1. Requires
One extra module beyond the baseline boost layer:
imgui/imgui_scope_builtin— defines the fourwith_*scope wrappers.
8.19.12.1.2. Why scope wrappers
Raw BeginDisabled / EndDisabled pairs work, but the v2 boost lint
flags them as “invisible state changes” — if the source has
BeginDisabled(...) somewhere and the matching EndDisabled() six
branches deep, a reader has to trace control flow to see whether the
disable will pop correctly on early-return paths. The scope wrapper:
Couples the push/pop into a block — exit through any path pops.
Reads as a single statement — the disabled argument is right at the top.
Survives
return,break,continuein the body — block exit always pops the scope.
8.19.12.1.3. with_disabled
The argument is the disabled bool — true greys out the block. The
tutorial drives this with an ENABLED_MASTER checkbox and passes
!ENABLED_MASTER.value so the checkbox’s natural “enable child group”
reading produces the right semantics.
8.19.12.1.4. with_button_repeat
When true, holding a button inside the block fires click events at
ImGui’s repeat rate (configured by KeyRepeatDelay and
KeyRepeatRate in ImGuiIO). Use it for stepper buttons (+,
-) where hold-to-increment is the natural interaction. Outside this
scope, buttons fire one click per press regardless of hold duration.
The tutorial’s STEP_UP.click_count climbs visibly while the cursor
holds down on the + button in the recording — each repeat fires the
click handler.
8.19.12.1.5. with_font
Push a different ImFont? for the block’s render — the default font
is whatever GetFont() returns. Combine with load_daslang_font (see
imgui/imgui_theme_daslang) to load a custom TTF and scope-swap it for
specific UI sections (code editors, narration boxes, etc.).
The tutorial’s with_font(GetFont()) is a no-op for demonstration —
the rendered text is the active font. Production code would pass a
specific ImFont? returned from a prior AddFontFromFileTTF call.
8.19.12.1.6. with_clip_rect
The fourth scope wrapper isn’t exercised in this tutorial — see
modules/dasImgui/examples/features/clip_rect.das for a per-frame clipping demo.
with_clip_rect(min, max, isect, blk) is the safest way to install a
custom clip rectangle around custom-rendered content (drawlist primitives,
images, manual layout) since the scope guarantees the prior clip rect is
restored on exit.
8.19.12.1.7. Combining wrappers
The wrappers compose cleanly — nest them to layer scopes:
checkbox(FEATURE_ENABLED, (text = "Feature on"))
with_disabled(!FEATURE_ENABLED.value) {
with_button_repeat(true) {
button(STEP_UP, (text = "+"))
button(STEP_DN, (text = "-"))
}
}
Steppers fire repeat clicks when held, but the whole pair greys out when the feature flag is off. Each wrapper’s exit pops its own scope; ImGui’s internal stacks handle the LIFO ordering.
See also
Full source: modules/dasImgui/examples/tutorial/with_disabled.das
Integration tests: modules/dasImgui/tests/test_disabled_block.das,
modules/dasImgui/tests/test_button_repeat.das,
modules/dasImgui/tests/test_font_stack.das.
Companion tutorials: With id, With style,
With tab stop — other with_* scope helpers.
Boost macros — the macro layer.