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: examples/tutorial/toggles.das.

Walkthrough

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

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).

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.

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.

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.

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: 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.