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*_buttonrail.imgui/imgui_containers_builtin—tab_bar/tab_itemfor thetab_item_buttonhost.imgui/imgui_boost_runtime—ClickState(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.