8.19.46. Buttons

Six shapes of click trigger. All six use ClickState; state.clicked is the per-frame bool, state.click_count accumulates across frames.

button(IDENT, (text = ".."))                       // the workhorse
small_button(IDENT, (text = ".."))                 // no frame padding
arrow_button(IDENT, (text = "..", dir = ...))      // triangle glyph
invisible_button(IDENT, (text = "..", size = ...)) // hit area, no render
image_button(IDENT, (text = "..",                  // textured button
                     user_texture_id = tex_id,
                     size = ...))
tab_item_button(IDENT, (text = "..",               // button styled as tab,
                        flags = ...))              //   inside a tab_bar

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

8.19.46.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_visual_aids
 19
 20// =============================================================================
 21// TUTORIAL: buttons — six shapes of click trigger.
 22//
 23//   button(IDENT, (text = ".."))                       — the workhorse.
 24//   small_button(IDENT, (text = ".."))                 — no frame padding,
 25//                                                        for inline tools.
 26//   arrow_button(IDENT, (text = "..", dir = ...))      — triangular glyph,
 27//                                                        ImGuiDir.{Up,Down,
 28//                                                        Left,Right}.
 29//   invisible_button(IDENT, (text = "..", size = ...)) — hit area without
 30//                                                        rendering — overlay
 31//                                                        a clickable region
 32//                                                        on a drawlist scene.
 33//   image_button(IDENT, (text = "..",                  — textured button. The
 34//                        user_texture_id = tex_id,        font atlas works as
 35//                        size = ...))                     a no-setup texture.
 36//   tab_item_button(IDENT, (text = "..",               — button styled as a
 37//                           flags = ...))                 tab — MUST be called
 38//                                                        INSIDE a tab_bar.
 39//
 40// All six use `ClickState`: `state.clicked` is the per-frame click bool,
 41// `state.click_count` accumulates total clicks.
 42//
 43// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/buttons.das
 44// LIVE:       daslang-live modules/dasImgui/examples/tutorial/buttons.das
 45//
 46// DRIVE (when running live):
 47//   curl -X POST -d '{"name":"imgui_click","args":{"target":"BT_WIN/BT_GO"}}' \
 48//        localhost:9090/command
 49//   curl -X POST -d '{"name":"imgui_click","args":{"target":"BT_WIN/BAR/BT_ADD"}}' \
 50//        localhost:9090/command
 51// =============================================================================
 52
 53[export]
 54def init() {
 55    live_create_window("dasImgui buttons tutorial", 800, 720)
 56    live_imgui_init(live_window)
 57    let io & = unsafe(GetIO())
 58    GetStyle().FontScaleMain = 1.4
 59}
 60
 61[export]
 62def update() { // nolint:STYLE038
 63    if (!live_begin_frame()) return
 64    begin_frame()
 65
 66    ImGui_ImplGlfw_NewFrame()
 67    apply_synth_io_override()
 68    NewFrame()
 69
 70    SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
 71    SetNextWindowSize(ImVec2(760.0f, 680.0f), ImGuiCond.Always)
 72    window(BT_WIN, (text = "buttons tutorial", closable = false,
 73                    flags = ImGuiWindowFlags.None)) {
 74
 75        text("Six shapes - ClickState everywhere; state.click_count accumulates.")
 76        text(BT_HINT, (text = "Plain / small / arrow / invisible / image / tab_item - pick by visual fit."))
 77        separator()
 78
 79        // ---- Stage 1: button — the workhorse ----
 80        if (button(BT_GO, (text = "Go"))) {
 81            // Hook your click handler here.
 82        }
 83        text("BT_GO clicks = {BT_GO.click_count}")
 84        spacing()
 85
 86        // ---- Stage 2: small_button — no padding, inline tool ----
 87        small_button(BT_SMALL, (text = "quit"))
 88        text("BT_SMALL clicks = {BT_SMALL.click_count}  // SmallButton has zero frame padding")
 89        spacing()
 90
 91        // ---- Stage 3: arrow_button — directional pair ----
 92        arrow_button(BT_BACK, (text = "##back", dir = ImGuiDir.Left)); same_line(BT_SL1)
 93        arrow_button(BT_FWD,  (text = "##fwd",  dir = ImGuiDir.Right))
 94        text("nav clicks: back={BT_BACK.click_count}, fwd={BT_FWD.click_count}")
 95        spacing()
 96
 97        // ---- Stage 4: invisible_button — hit area without a glyph ----
 98        text("Invisible hotspot below (120x24, no glyph):")
 99        invisible_button(BT_HOT, (text = "##hotspot", size = float2(120.0f, 24.0f)))
100        text("BT_HOT clicks = {BT_HOT.click_count}  // overlay on a drawlist / image / etc.")
101        spacing()
102
103        // ---- Stage 5: image_button — textured button (font atlas) ----
104        let io & = unsafe(GetIO())
105        if (io.Fonts.TexData != null) {
106            image_button(BT_IMG, (text = "##font",
107                                  user_texture_id = io.Fonts.TexRef,
108                                  size = float2(48.0f, 48.0f)))
109            text("BT_IMG clicks = {BT_IMG.click_count}  // textured 48x48 of the font atlas")
110        }
111        spacing()
112
113        // ---- Stage 6: tab_item_button — must live inside a tab_bar ----
114        text("tab_item_button - leading '?' tallies, trailing '+' grows the bar:")
115        tab_bar(BT_BAR, (text = "MyBar",
116                          flags = ImGuiTabBarFlags.FittingPolicyShrink)) {
117            tab_item_button(BT_HELP, (text = "?",
118                                       flags = ImGuiTabItemFlags.Leading |
119                                               ImGuiTabItemFlags.NoTooltip))
120            tab_item_button(BT_ADD, (text = "+",
121                                      flags = ImGuiTabItemFlags.Trailing |
122                                              ImGuiTabItemFlags.NoTooltip))
123            tab_item(BT_TAB_A, (text = "alpha", closable = false,
124                                 flags = ImGuiTabItemFlags.None)) {
125                text(BT_BODY_A, (text = "tab alpha body"))
126            }
127            tab_item(BT_TAB_B, (text = "beta", closable = false,
128                                 flags = ImGuiTabItemFlags.None)) {
129                text(BT_BODY_B, (text = "tab beta body"))
130            }
131        }
132        text("BT_HELP clicks = {BT_HELP.click_count}, BT_ADD clicks = {BT_ADD.click_count}")
133    }
134
135    end_of_frame()
136    Render()
137    var w, h : int
138    live_get_framebuffer_size(w, h)
139    glViewport(0, 0, w, h)
140    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
141    glClear(GL_COLOR_BUFFER_BIT)
142    live_imgui_render()
143
144    live_end_frame()
145}
146
147[export]
148def shutdown() {
149    live_imgui_shutdown()
150    live_destroy_window()
151}
152
153[export]
154def main() {
155    init()
156    while (!exit_requested()) {
157        update()
158        maybe_collect_gc()
159    }
160    shutdown()
161}

8.19.46.1.1. Requires

Already in the baseline boost layer:

  • imgui/imgui_widgets_builtin — every *_button rail.

  • imgui/imgui_containers_builtintab_bar / tab_item for the tab_item_button host.

  • imgui/imgui_boost_runtimeClickState (shared across all six widgets).

8.19.46.1.2. button — the workhorse

The default click trigger. Returns true on the frame the user clicked; state.click_count tallies across frames:

if (button(SAVE, (text = "Save"))) {
    // one-shot handler — runs once per click
}
// OR drive on accumulated count:
text("saves: {SAVE.click_count}")

size = (0, 0) lets ImGui auto-size to the label. Pass an explicit size for fixed-width toolbar rows.

8.19.46.1.3. small_button — zero padding

Same widget shape; SmallButton() drops the frame padding. Use for inline tool strips that need to feel like part of the surrounding text:

text("Status: ")
same_line()
small_button(REFRESH, (text = "refresh"))

No size arg — small_button always auto-sizes.

8.19.46.1.4. arrow_button — directional glyph

A triangle pointing in one of four directions. dir : ImGuiDir picks the direction:

arrow_button(BACK, (text = "##back", dir = ImGuiDir.Left)); same_line()
arrow_button(FWD,  (text = "##fwd",  dir = ImGuiDir.Right))

The text is the ImGui label (mostly "##name" to suppress visible text — the glyph carries the meaning). Each call needs its own IDENT (separate ClickState).

8.19.46.1.5. invisible_button — hit area only

Renders nothing, but captures clicks inside its bbox. The canonical use: overlay a hit area on top of a drawlist scene, image, or custom glyph:

invisible_button(HOTSPOT, (text = "##hotspot",
                           size = float2(120.0f, 24.0f)))
if (HOTSPOT.clicked) {
    // capture click on the scene below
}

Requires an explicit size — there’s no glyph to derive bounds from. ImGui still tracks hover and click via IsItemHovered() / IsItemClicked(), so it composes with the rest of the imgui interaction stack.

8.19.46.1.6. image_button — textured trigger

Renders a texture as the button face. user_texture_id : ImTextureRef is ImGui 1.92’s texture handle — either a backend-owned texture (_TexData) or your own GL texture name in the _TexID slot. The font atlas exposes itself as io.Fonts.TexRef, which is always available and makes for a no-setup demo target:

let io & = unsafe(GetIO())
if (io.Fonts.TexData != null) {
    image_button(BTN, (text = "##font",
                       user_texture_id = io.Fonts.TexRef,
                       size = float2(48.0f, 48.0f)))
}

The TexData != null guard skips the frames before the backend has built the atlas. Real apps decode and upload their own texture and wrap the GL name in an ImTextureRef — see Texture references for the full path, and examples/imgui_demo/widgets.das for the 8-button image-grid pattern. uv0 / uv1 slice into the texture; bg_col / tint_col modulate the rendered face.

8.19.46.1.7. tab_item_button — button styled as a tab

A click trigger that visually matches sibling tab_item headers. Must live inside a tab_bar body. Flag with Leading to anchor to the start of the bar, Trailing to the end — the canonical ? / + row:

tab_bar(BAR, (text = "MyTabBar",
               flags = ImGuiTabBarFlags.FittingPolicyShrink)) {
    if (tab_item_button(HELP, (text = "?",
                                flags = ImGuiTabItemFlags.Leading |
                                        ImGuiTabItemFlags.NoTooltip))) {
        // open help popup
    }
    if (tab_item_button(ADD, (text = "+",
                               flags = ImGuiTabItemFlags.Trailing |
                                       ImGuiTabItemFlags.NoTooltip))) {
        // push a new entry
    }
    tab_item(TAB_A, (text = "alpha", closable = false,
                     flags = ImGuiTabItemFlags.None)) {
        text(BODY_A, (text = "tab body"))
    }
}

Unlike tab_item it has no content pane (no block body) — it’s a trigger only. See Tab bar for the regular tab pattern.

8.19.46.1.8. Driving from outside

Every *_button accepts imgui_click and snapshot probes:

# Plain button
curl -X POST -d '{"name":"imgui_click","args":{"target":"BT_WIN/BT_GO"}}' \
     localhost:9090/command
# Tab-bar-nested button — path includes the bar segment
curl -X POST -d '{"name":"imgui_click","args":{"target":"BT_WIN/BT_BAR/BT_HELP"}}' \
     localhost:9090/command

8.19.46.1.9. Caller-owned variant

There is no edit_button: a button owns no value, only click bookkeeping, so there is nothing for the caller to hold. The caller-owned rail covers the toggle end of the click family instead — edit_checkbox / edit_radio_button / edit_menu_item take a bool? pointer via safe_addr and write the caller’s own flag (see External-pointer editing rail). For a plain trigger, read button(...)’s return value directly.

See also

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

Features-side demo: modules/dasImgui/examples/features/triggers.das — every ClickState widget in one window, useful for imgui_click smoke testing.

Sibling tutorials: Color (color_button is a special button variant), Tab bar (where tab_item_button lives).

Boost macros — the macro layer.