8.18.1.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.18.1.21.1. Walkthrough
1options gen2
2options _comment_hygiene = true
3
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: main_menu_bar — top-level menu bar + menu_item vs menu_label split.
21//
22// Two related v2 additions live in this tutorial:
23//
24// 1. `main_menu_bar(IDENT) { ... }` is the screen-top bar (`BeginMainMenuBar`)
25// — distinct from `menu_bar(IDENT) { ... }` which attaches to the current
26// window (`BeginMenuBar`, requires `ImGuiWindowFlags.MenuBar`).
27//
28// 2. `menu_item` vs `menu_label`:
29// - menu_item(IDENT, (text=, shortcut=, enabled=)) → toggleable.
30// ToggleState.value mutates on click.
31// - menu_label(IDENT, (text=, shortcut=, selected=, enabled=)) → static.
32// ClickState.click_count accumulates; `selected` is a fixed check-mark.
33//
34// Use menu_label for section headers (`selected=false, enabled=false`),
35// permanent-check labels (`selected=true`), and any "do something, don't
36// toggle" menu entry.
37//
38// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/main_menu_bar.das
39// LIVE: daslang-live modules/dasImgui/examples/tutorial/main_menu_bar.das
40//
41// DRIVE (when running live):
42// curl -X POST -d '{"name":"imgui_click","args":{"target":"MAIN_BAR/FILE_MENU/MI_DARKMODE"}}' \
43// localhost:9090/command
44// =============================================================================
45
46[export]
47def init() {
48 live_create_window("dasImgui main_menu_bar tutorial", 800, 480)
49 live_imgui_init(live_window)
50 let io & = unsafe(GetIO())
51 GetStyle().FontScaleMain = 1.4
52}
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 // The bar sits at screen top, NOT inside any window().
64 main_menu_bar(MAIN_BAR) {
65 menu(FILE_MENU, (text = "File", enabled = true)) {
66 // Toggleable — clicking flips MI_DARKMODE.value.
67 menu_item(MI_DARKMODE, (text = "Dark mode", shortcut = "Ctrl+D"))
68 menu_item(MI_AUTOSAVE, (text = "Auto-save", shortcut = ""))
69 separator(FILE_SEP)
70 // Section header — static, disabled, no check.
71 menu_label(FILE_HEADER, (text = "(recent files)",
72 shortcut = "", selected = false,
73 enabled = false))
74 menu_label(FILE_RECENT1, (text = "demo.das"))
75 menu_label(FILE_RECENT2, (text = "tour.das"))
76 separator(FILE_SEP2)
77 // Permanent check — selected=true makes it always-checked.
78 menu_label(FILE_STAR, (text = "Star this project",
79 shortcut = "", selected = true))
80 menu_item(MI_QUIT, (text = "Quit", shortcut = "Ctrl+Q"))
81 }
82 menu(EDIT_MENU, (text = "Edit", enabled = true)) {
83 // menu_label for an action — emits click_count, no toggle.
84 menu_label(EDIT_UNDO, (text = "Undo", shortcut = "Ctrl+Z"))
85 menu_label(EDIT_REDO, (text = "Redo", shortcut = "Ctrl+Y",
86 selected = false, enabled = false))
87 }
88 }
89
90 SetNextWindowPos(ImVec2(60.0, 80.0), ImGuiCond.Always)
91 SetNextWindowSize(ImVec2(680.0, 320.0), ImGuiCond.Always)
92 window(BODY, (text = "State readout", closable = false,
93 flags = ImGuiWindowFlags.None)) {
94 text("Click items in the bar above; the snapshot updates below.")
95 separator(BODY_SEP)
96 text("MI_DARKMODE.value = {MI_DARKMODE.value}")
97 text("MI_AUTOSAVE.value = {MI_AUTOSAVE.value}")
98 text("EDIT_UNDO.click_count = {EDIT_UNDO.click_count}")
99 text("MI_QUIT.value = {MI_QUIT.value}")
100 }
101
102 end_of_frame()
103 Render()
104 var w, h : int
105 live_get_framebuffer_size(w, h)
106 glViewport(0, 0, w, h)
107 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
108 glClear(GL_COLOR_BUFFER_BIT)
109 live_imgui_render()
110
111 live_end_frame()
112}
113
114[export]
115def shutdown() {
116 live_imgui_shutdown()
117 live_destroy_window()
118}
119
120[export]
121def main() {
122 init()
123 while (!exit_requested()) {
124 update()
125 }
126 shutdown()
127}
8.18.1.21.1.1. Requires
Baseline boost layer (imgui/imgui_boost_v2 re-exports
imgui/imgui_containers_builtin). No extra modules.
8.18.1.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.18.1.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.18.1.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.18.1.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.