8.18.1.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.18.1.43.1. Walkthrough

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

8.18.1.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.18.1.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.18.1.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.18.1.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.18.1.43.1.5. Caller-owned variants

For sites where the value already lives on an external bool / int (not a widget state struct), use the edit_* rails — they take a T? pointer via 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_mode : int = 0
edit_radio_button_int(safe_addr(g_mode), (id = "MODE",
                                           text = "Off", v_button = 0))

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.