8.19.43. Toggles

The toggle widgets are pick-this-not-that input — three shapes, one mental model. Click flips state; imgui_force_set writes it from outside. The three forms differ in glyph and in whether they share state across call sites:

checkbox(IDENT, (text = ".."))                   // single bool, square glyph
radio_button(IDENT, (text = ".."))               // single bool, circle glyph
radio_button_int(IDENT, (text = "..",            // N sites share ONE state;
                         v_button = N))          //   click writes state.value = v_button

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

8.19.43.1. Walkthrough

  1options gen2
  2options _comment_hygiene = true
  3options gc
  4
  5require math
  6require imgui
  7require imgui_app
  8require opengl/opengl_boost
  9require live/glfw_live
 10require live/live_api
 11require live/live_commands
 12require live/live_vars
 13require live_host
 14require imgui/imgui_live
 15require imgui/imgui_boost_runtime
 16require imgui/imgui_boost_v2
 17require imgui/imgui_widgets_builtin
 18require imgui/imgui_containers_builtin
 19require imgui/imgui_visual_aids
 20
 21// =============================================================================
 22// TUTORIAL: toggles — three forms of pick-this-not-that input.
 23//
 24//   checkbox(IDENT, (text = "..."))           — single bool, click flips.
 25//   radio_button(IDENT, (text = "..."))       — single bool, circle-rendered.
 26//                                                Same ToggleState as checkbox;
 27//                                                difference is purely visual.
 28//   radio_button_int(IDENT, (text = "...",    — N call sites SHARE one
 29//                            v_button = N))     RadioIntState. Click writes
 30//                                                state.value = v_button — the
 31//                                                canonical "pick one of N".
 32//
 33// All three accept imgui_force_set: checkbox / radio_button take bool, radio_button_int
 34// takes int (the v_button to select).
 35//
 36// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/toggles.das
 37// LIVE:       daslang-live modules/dasImgui/examples/tutorial/toggles.das
 38//
 39// DRIVE (when running live):
 40//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"TG_WIN/T_CHECK","value":true}}' \
 41//        localhost:9090/command
 42//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"TG_WIN/T_MODE","value":2}}' \
 43//        localhost:9090/command
 44// =============================================================================
 45
 46[export]
 47def init() {
 48    live_create_window("dasImgui toggles tutorial", 760, 560)
 49    live_imgui_init(live_window)
 50    let io & = unsafe(GetIO())
 51    GetStyle().FontScaleMain = 1.4
 52}
 53
 54let MODE_LABELS = fixed_array("Off", "Manual", "Auto")
 55
 56[export]
 57def update() {
 58    if (!live_begin_frame()) return
 59    begin_frame()
 60
 61    ImGui_ImplGlfw_NewFrame()
 62    apply_synth_io_override()
 63    NewFrame()
 64
 65    SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
 66    SetNextWindowSize(ImVec2(720.0f, 520.0f), ImGuiCond.Always)
 67    window(TG_WIN, (text = "toggles tutorial", closable = false,
 68                    flags = ImGuiWindowFlags.None)) {
 69
 70        text("Three shapes of pick-this-not-that.")
 71        text(T_HINT, (text = "checkbox + radio_button share ToggleState. radio_button_int is the grouped form."))
 72        separator()
 73
 74        // ---- Stage 1: checkbox — single bool, square widget ----
 75        checkbox(T_CHECK, (text = "Enabled"))
 76        text("T_CHECK.value = {T_CHECK.value}")
 77        spacing()
 78
 79        // ---- Stage 2: radio_button (bool form) — circle-rendered checkbox ----
 80        radio_button(T_RADIO_BOOL, (text = "Subscribe"))
 81        text("T_RADIO_BOOL.value = {T_RADIO_BOOL.value}  // same ToggleState as checkbox")
 82        spacing()
 83
 84        // ---- Stage 3: radio_button_int — three sites, ONE shared state ----
 85        text("Operation mode:")
 86        radio_button_int(T_MODE, (text = "Off",    v_button = 0)); same_line(T_SL1)
 87        radio_button_int(T_MODE, (text = "Manual", v_button = 1)); same_line(T_SL2)
 88        radio_button_int(T_MODE, (text = "Auto",   v_button = 2))
 89        text("T_MODE.value = {T_MODE.value} ('{MODE_LABELS[clamp(T_MODE.value, 0, 2)]}')")
 90    }
 91
 92    end_of_frame()
 93    Render()
 94    var w, h : int
 95    live_get_framebuffer_size(w, h)
 96    glViewport(0, 0, w, h)
 97    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
 98    glClear(GL_COLOR_BUFFER_BIT)
 99    live_imgui_render()
100
101    live_end_frame()
102}
103
104[export]
105def shutdown() {
106    live_imgui_shutdown()
107    live_destroy_window()
108}
109
110[export]
111def main() {
112    init()
113    while (!exit_requested()) {
114        update()
115        maybe_collect_gc()
116    }
117    shutdown()
118}

8.19.43.1.1. Requires

Already in the baseline boost layer:

  • imgui/imgui_widgets_builtincheckbox / radio_button / radio_button_int rails.

  • imgui/imgui_boost_runtimeToggleState (checkbox + bool radio) and RadioIntState (grouped radio).

8.19.43.1.2. checkbox vs radio_button (bool form)

checkbox and radio_button are the same widget under different glyphs. Both take ToggleState; click flips state.value. The only difference is the rendering — square check mark vs filled circle. Pick by visual fit, not by semantics.

checkbox(ENABLED, (text = "Enabled"))      // [x] Enabled
radio_button(SUBSCRIBE, (text = "Sub"))    // (•) Sub

Two checkboxes are mutually independent. Two radio_button calls with different state idents are also mutually independent — they only look like a radio group. If you want one-of-N exclusivity, use radio_button_int.

8.19.43.1.3. radio_button_int — grouped, mutually exclusive

The grouped form is the canonical “pick one of N.” Multiple call sites share one RadioIntState; each call passes its own v_button integer. Clicking a row writes state.value = v_button — the other rows light off automatically because they re-check whether state.value == their_v_button each frame.

radio_button_int(MODE, (text = "Off",    v_button = 0)); same_line(SL_1)
radio_button_int(MODE, (text = "Manual", v_button = 1)); same_line(SL_2)
radio_button_int(MODE, (text = "Auto",   v_button = 2))
// MODE.value is 0 / 1 / 2 — exactly one selected.

same_line between rows lays them horizontally. Stack vertically without it.

Because the rows share one ident (hence one snapshot path), each row also registers a per-site geometry alias <path>#<v_button> — e.g. TG_WIN/T_MODE#0 / #1 / #2. Use these to click or test one specific option (imgui_click resolves an alias to that row’s bbox); the shared value still lives at the bare TG_WIN/T_MODE.

8.19.43.1.4. Driving from outside

Same telemetry channel as every other widget — imgui_force_set writes state.pending_value, consumed next frame:

# checkbox / bool radio — bool value
curl -X POST -d '{"name":"imgui_force_set","args":{"target":"TG_WIN/T_CHECK","value":true}}' \
     localhost:9090/command
# grouped radio — int value (the v_button to select)
curl -X POST -d '{"name":"imgui_force_set","args":{"target":"TG_WIN/T_MODE","value":2}}' \
     localhost:9090/command

The dispatcher ([widget_dispatch] on ToggleState and RadioIntState) parses the right JSON shape per state type.

8.19.43.1.5. Caller-owned variants

For sites where the value already lives on an external bool (not a widget state struct), use the edit_* rails — they take a bool? pointer via safe_addr (require daslib/safe_addr) and skip the state-struct allocation:

var g_enabled : bool = false
edit_checkbox(safe_addr(g_enabled), (id = "EN", text = "Enabled"))

var g_subscribed : bool = false
edit_radio_button(safe_addr(g_subscribed), (id = "SUB", text = "Sub"))

Only the bool forms have caller-owned rails: edit_checkbox and edit_radio_button. There is no edit_radio_button_int — the grouped one-of-N form needs the shared RadioIntState that carries the group’s selected value, so declare a RadioIntState ident and use radio_button_int.

See External-pointer editing rail.

See also

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

Sibling: Dropdown + select — for one-of-N picks too large for a radio strip, combo collapses the choices into a dropdown.

Boost macros — the macro layer.