8.19.21. Main menu bar
Two related v2 additions live together in this tutorial. The first is
main_menu_bar(IDENT) { ... } — the screen-top bar (BeginMainMenuBar
/ EndMainMenuBar), distinct from menu_bar(IDENT) { ... } which
attaches to the current window (BeginMenuBar, requires
ImGuiWindowFlags.MenuBar). The second is the
menu_item / menu_label split for the leaves under a menu:
menu_item(IDENT, (text=, shortcut=, enabled=))— toggleable.ToggleState.valueflips on click.menu_label(IDENT, (text=, shortcut=, selected=, enabled=))— static.ClickState.click_countaccumulates;selectedis a fixed check-mark (not driven by the click).
menu_label is the right rail for section headers
(selected=false, enabled=false), permanent-check labels
(selected=true), and any “do something, don’t toggle” menu entry.
main_menu_bar(MAIN_BAR) {
menu(FILE_MENU, (text = "File", enabled = true)) {
menu_item(MI_DARKMODE, (text = "Dark mode", shortcut = "Ctrl+D"))
menu_item(MI_AUTOSAVE, (text = "Auto-save", shortcut = ""))
separator(FILE_SEP)
menu_label(FILE_HEADER, (text = "(recent files)",
shortcut = "", selected = false,
enabled = false))
menu_label(FILE_RECENT1, (text = "demo.das"))
menu_label(FILE_STAR, (text = "Star this project",
shortcut = "", selected = true))
}
}
Source: modules/dasImgui/examples/tutorial/main_menu_bar.das.
8.19.21.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: main_menu_bar — top-level menu bar + menu_item vs menu_label split.
22//
23// Two related v2 additions live in this tutorial:
24//
25// 1. `main_menu_bar(IDENT) { ... }` is the screen-top bar (`BeginMainMenuBar`)
26// — distinct from `menu_bar(IDENT) { ... }` which attaches to the current
27// window (`BeginMenuBar`, requires `ImGuiWindowFlags.MenuBar`).
28//
29// 2. `menu_item` vs `menu_label`:
30// - menu_item(IDENT, (text=, shortcut=, enabled=)) → toggleable.
31// ToggleState.value mutates on click.
32// - menu_label(IDENT, (text=, shortcut=, selected=, enabled=)) → static.
33// ClickState.click_count accumulates; `selected` is a fixed check-mark.
34//
35// Use menu_label for section headers (`selected=false, enabled=false`),
36// permanent-check labels (`selected=true`), and any "do something, don't
37// toggle" menu entry.
38//
39// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/main_menu_bar.das
40// LIVE: daslang-live modules/dasImgui/examples/tutorial/main_menu_bar.das
41//
42// DRIVE (when running live):
43// curl -X POST -d '{"name":"imgui_click","args":{"target":"MAIN_BAR/FILE_MENU/MI_DARKMODE"}}' \
44// localhost:9090/command
45// =============================================================================
46
47[export]
48def init() {
49 live_create_window("dasImgui main_menu_bar tutorial", 800, 480)
50 live_imgui_init(live_window)
51 let io & = unsafe(GetIO())
52 GetStyle().FontScaleMain = 1.4
53}
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 // The bar sits at screen top, NOT inside any window().
65 main_menu_bar(MAIN_BAR) {
66 menu(FILE_MENU, (text = "File", enabled = true)) {
67 // Toggleable — clicking flips MI_DARKMODE.value.
68 menu_item(MI_DARKMODE, (text = "Dark mode", shortcut = "Ctrl+D"))
69 menu_item(MI_AUTOSAVE, (text = "Auto-save", shortcut = ""))
70 separator(FILE_SEP)
71 // Section header — static, disabled, no check.
72 menu_label(FILE_HEADER, (text = "(recent files)",
73 shortcut = "", selected = false,
74 enabled = false))
75 menu_label(FILE_RECENT1, (text = "demo.das"))
76 menu_label(FILE_RECENT2, (text = "tour.das"))
77 separator(FILE_SEP2)
78 // Permanent check — selected=true makes it always-checked.
79 menu_label(FILE_STAR, (text = "Star this project",
80 shortcut = "", selected = true))
81 menu_item(MI_QUIT, (text = "Quit", shortcut = "Ctrl+Q"))
82 }
83 menu(EDIT_MENU, (text = "Edit", enabled = true)) {
84 // menu_label for an action — emits click_count, no toggle.
85 menu_label(EDIT_UNDO, (text = "Undo", shortcut = "Ctrl+Z"))
86 menu_label(EDIT_REDO, (text = "Redo", shortcut = "Ctrl+Y",
87 selected = false, enabled = false))
88 }
89 }
90
91 SetNextWindowPos(ImVec2(60.0, 80.0), ImGuiCond.Always)
92 SetNextWindowSize(ImVec2(680.0, 320.0), ImGuiCond.Always)
93 window(BODY, (text = "State readout", closable = false,
94 flags = ImGuiWindowFlags.None)) {
95 text("Click items in the bar above; the snapshot updates below.")
96 separator(BODY_SEP)
97 text("MI_DARKMODE.value = {MI_DARKMODE.value}")
98 text("MI_AUTOSAVE.value = {MI_AUTOSAVE.value}")
99 text("EDIT_UNDO.click_count = {EDIT_UNDO.click_count}")
100 text("MI_QUIT.value = {MI_QUIT.value}")
101 }
102
103 end_of_frame()
104 Render()
105 var w, h : int
106 live_get_framebuffer_size(w, h)
107 glViewport(0, 0, w, h)
108 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
109 glClear(GL_COLOR_BUFFER_BIT)
110 live_imgui_render()
111
112 live_end_frame()
113}
114
115[export]
116def shutdown() {
117 live_imgui_shutdown()
118 live_destroy_window()
119}
120
121[export]
122def main() {
123 init()
124 while (!exit_requested()) {
125 update()
126 maybe_collect_gc()
127 }
128 shutdown()
129}
8.19.21.1.1. Requires
Baseline boost layer, no extra modules — main_menu_bar / menu_bar
/ menu come from imgui/imgui_containers_builtin, and
menu_item / menu_label / separator from
imgui/imgui_widgets_builtin.
8.19.21.1.2. main_menu_bar vs menu_bar
The two bar variants pick opposite anchor strategies:
main_menu_bar(IDENT) { ... }attaches to the viewport — sits at the top of the screen, persists across windows, ImGui draws the chrome. Use it when there’s exactly one bar in the app.menu_bar(IDENT) { ... }attaches to the current window — appears inside that window’s titlebar area. The host window must haveImGuiWindowFlags.MenuBarset, or the menu_bar render is a no-op. Use it for per-document menus inside a tabbed editor, the imgui_demo’s own menu bar, or any window-local command surface.
Both expose the same menu() / menu_item / menu_label children;
only the anchor differs.
8.19.21.1.3. menu_item vs menu_label
menu_item and menu_label look identical visually (text + optional
shortcut + optional check-mark) but back different state structs:
menu_itemcarriesToggleState— thevaluebool flips on every click. Use it for boolean app-state toggles (“Dark mode”, “Auto-save”, “Show hidden files”).menu_labelcarriesClickState— theclick_countint accumulates without touchingselected. Use it for actions (“Undo”, “Quit”, “About”) and for static labels with a fixed check (selected=true) or no check at all.
Combine selected= and enabled= on menu_label to get section
headers (selected=false, enabled=false greys the entry and shows no
check) or permanent-check items (selected=true, enabled=true shows
the check and stays clickable).
8.19.21.1.4. Driving from outside
The full menu hierarchy is routable once a menu is open:
curl -X POST -d '{"name":"imgui_click","args":{"target":"MAIN_BAR/FILE_MENU/MI_DARKMODE"}}' \
localhost:9090/command
The bar’s own header (MAIN_BAR) has a degenerate bbox (the chrome has
no clickable “header”). The first-level menu() children
(MAIN_BAR/FILE_MENU, MAIN_BAR/EDIT_MENU) DO register routable
headers. Submenu items only register a bbox while their parent menu is
open, so external drivers wanting to click a menu_item have to open the
parent first (synth click on the header, wait one frame, then click the
child). The walkthrough above does exactly this with real clicks, dropping
straight DOWN the open menu’s column onto each item: a diagonal travel to an
item that sits off-axis from its header would clip a sibling header, and
ImGui hover-switches the open menu mid-travel (the click then lands in the
wrong menu). Menu items span the full menu width, so a vertical drop at the
header’s center-x always lands on the item.
8.19.21.1.5. Snapshot shape
MI_DARKMODE.value is the live toggle bool; EDIT_UNDO.click_count
is the accumulating action counter. Both surface in the snapshot under
their qualified path:
curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command \
| jq '.globals."MAIN_BAR/FILE_MENU/MI_DARKMODE".payload'
The tutorial’s body window prints both per-frame so the state changes are visible alongside the menu interactions.
See also
Full source: modules/dasImgui/examples/tutorial/main_menu_bar.das
Integration tests: modules/dasImgui/tests/test_menu_main.das and modules/dasImgui/tests/test_menu_label_static.das.
Boost macros — the macro layer.