8.19.35. With tab stop

ImGui’s NoTabStop item flag controls which widgets participate in TAB / Shift+TAB focus cycling. The boost layer wraps the PushItemFlag(ImGuiItemFlags.NoTabStop, !tab_stop) / PopItemFlag pair as a stateless scope wrapper (ImGui obsoleted the PushTabStop / PopTabStop shorthand in 1.91):

with_tab_stop(false) {
    input_text(TS_FIELD_SKIP, (text = "Skipped by TAB", init = "..."))
}

The wrapper itself emits no telemetry — it only pushes the tab-stop flag on entry and pops it on exit, matching the rest of the with_* family (with_indent, with_disabled, with_button_repeat, with_font, with_clip_rect).

Source: modules/dasImgui/examples/tutorial/with_tab_stop.das.

8.19.35.1. Walkthrough

 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_tab_stop — stateless scope wrapper around PushTabStop /
23//           PopTabStop. While the wrapped block is active, every widget
24//           inside is skipped by TAB / Shift+Tab focus cycling. Useful for
25//           help markers or decorative inputs that shouldn't steal keyboard
26//           focus when navigating an editable form.
27//
28//   with_tab_stop(false) { widgets_to_skip... }
29//
30// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/with_tab_stop.das
31// LIVE:       daslang-live modules/dasImgui/examples/tutorial/with_tab_stop.das
32// =============================================================================
33
34[export]
35def init() {
36    live_create_window("dasImgui with_tab_stop tutorial", 760, 460)
37    live_imgui_init(live_window)
38    let io & = unsafe(GetIO())
39    GetStyle().FontScaleMain = 1.4
40}
41
42[export]
43def update() {
44    if (!live_begin_frame()) return
45    begin_frame()
46
47    ImGui_ImplGlfw_NewFrame()
48    apply_synth_io_override()
49    NewFrame()
50
51    SetNextWindowPos(ImVec2(40.0f, 40.0f), ImGuiCond.Always)
52    SetNextWindowSize(ImVec2(720.0f, 400.0f), ImGuiCond.Always)
53    window(TS_WIN, (text = "with_tab_stop", closable = false,
54                    flags = ImGuiWindowFlags.None)) {
55        text("Press TAB / Shift+TAB to cycle focus.")
56        text("The middle field is wrapped in with_tab_stop(false) - TAB skips it.")
57        separator()
58
59        input_text(TS_FIELD_A, (text = "Field A"))
60        input_text(TS_FIELD_B, (text = "Field B"))
61
62        with_tab_stop(false) {
63            input_text(TS_FIELD_SKIP,
64                (text = "Skipped by TAB", init = "(unreachable via keyboard)"))
65        }
66
67        input_text(TS_FIELD_C, (text = "Field C"))
68        input_text(TS_FIELD_D, (text = "Field D"))
69    }
70
71    end_of_frame()
72    Render()
73    var w, h : int
74    live_get_framebuffer_size(w, h)
75    glViewport(0, 0, w, h)
76    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
77    glClear(GL_COLOR_BUFFER_BIT)
78    live_imgui_render()
79
80    live_end_frame()
81}
82
83[export]
84def shutdown() {
85    live_imgui_shutdown()
86    live_destroy_window()
87}
88
89[export]
90def main() {
91    init()
92    while (!exit_requested()) {
93        update()
94        maybe_collect_gc()
95    }
96    shutdown()
97}

8.19.35.1.1. Requires

Same baseline as the other with_* wrappers, plus an explicit require imgui/imgui_scope_builtinimgui/imgui_boost_v2 re-exports only imgui, imgui/imgui_lint and imgui/imgui_boost_runtime, so the scope wrappers need their own require.

8.19.35.1.2. Behaviour

While the wrapped block is active, every focusable widget inside has its ImGui tab-stop flag suppressed:

  • TAB and Shift+TAB cycling skip past those widgets.

  • Mouse click still focuses the widget (tab-stop is keyboard-only).

  • Programmatic focus (SetKeyboardFocusHere) still works.

The flag scope follows the block exactly: as soon as the wrapped block exits, the previous tab-stop policy is restored, so wrapping a help-marker or decorative input inside with_tab_stop(false) doesn’t leak the suppression onto sibling widgets.

8.19.35.1.3. When to reach for it

The two recurring shapes:

  • Help markers / tooltips — a (?) glyph that opens an info popup on hover but shouldn’t intercept TAB navigation through the editable form.

  • Decorative inputs — a read-only field that displays computed state next to editable siblings; keyboard navigation should skip past it entirely.

The same pattern is used in examples/imgui_demo/inputs.das — both the Tabbing and Focus-from-code sub-sections wrap their “(tab skip)” field + help marker in with_tab_stop(false) { ... }.

8.19.35.1.4. Standalone vs live

Same convention as the other scope-wrapper tutorials. daslang.exe runs the demo headlessly to frame N; daslang-live keeps the window open and reloads on source edits.

See also

Full source: modules/dasImgui/examples/tutorial/with_tab_stop.das

Integration test: modules/dasImgui/tests/test_with_tab_stop.das.

Boost macros — the macro layer.