8.18.1.49. Right-click context popups
Two [container] rails that attach a right-click context menu to a
target. popup_context_item attaches to the previously submitted
item; popup_context_window attaches to the enclosing window.
Both are stateless_finalize — ImGui owns open/close internally,
the body runs only while the popup is visible.
popup_context_item(IDENT, (str_id = "..",
flags = ImGuiPopupFlags.MouseButtonRight)) {
<menu body>
}
popup_context_window(IDENT, (str_id = "..",
flags = ImGuiPopupFlags.MouseButtonRight)) {
<menu body>
}
Source: modules/dasImgui/examples/tutorial/popups.das.
8.18.1.49.1. Walkthrough
The recording drives both menus with real synthetic right-clicks and
clicks a menu item from each, self-verifying that the action fired
(PO_RENAME then PO_REFRESH flip to value = true). After each
right-click it waits for the menu to actually render before
travelling onto the item — never a fixed sleep guessing the popup is
open. To explore live, run the tutorial under daslang-live and
right-click the button / empty space yourself:
bin/Release/daslang-live modules/dasImgui/examples/tutorial/popups.das
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: popups — two right-click context popup [container]s.
21//
22// popup_context_item(IDENT, (str_id = "..", — attaches to the
23// flags = ...)) { PREVIOUSLY submitted
24// <menu_item or any body> item; right-click
25// } THAT item to open.
26//
27// popup_context_window(IDENT, (str_id = "..", — attaches to the
28// flags = ...)) { enclosing window;
29// <menu_item or any body> right-click ANYWHERE
30// } inside opens.
31//
32// ImGui drives open/close internally — no pending_open / pending_close
33// fields on the state; the body runs only on the frames the popup is
34// visible. `str_id` is the popup's stack id; pass a unique non-empty
35// string per consumer (the C-default `"##ContextWindow"` fires only on
36// a NULL const char* which daslang's string binding doesn't expose).
37//
38// flags = ImGuiPopupFlags.MouseButtonRight is the conventional opener;
39// MouseButtonLeft / MouseButtonMiddle work too (rarely useful).
40//
41// The window popup ORs in NoOpenOverItems so a right-click ON the button
42// opens the ITEM menu, not the window menu. Without it the window popup
43// swallows every right-click inside the window, items included, and the
44// item menu can never open.
45//
46// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/popups.das
47// LIVE: daslang-live modules/dasImgui/examples/tutorial/popups.das
48// =============================================================================
49
50var private g_item_action : string = "(right-click the button)"
51var private g_win_action : string = "(right-click empty area)"
52
53[export]
54def init() {
55 live_create_window("dasImgui popups tutorial", 760, 560)
56 live_imgui_init(live_window)
57 let io & = unsafe(GetIO())
58 GetStyle().FontScaleMain = 1.4
59}
60
61[export]
62def update() {
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(720.0f, 520.0f), ImGuiCond.Always)
72 window(PO_WIN, (text = "popups tutorial", closable = false,
73 flags = ImGuiWindowFlags.None)) {
74
75 text("Right-click context popups - two flavors.")
76 text(PO_HINT, (text = "popup_context_item attaches to the previous item; popup_context_window attaches to the enclosing window."))
77 separator()
78
79 // ---- Stage 1: popup_context_item — attached to a specific item ----
80 text("Stage 1 - right-click the target button below:")
81 button(PO_TARGET, (text = "Right-click me"))
82 popup_context_item(PO_CTX_ITEM, (str_id = "ctx_item",
83 flags = ImGuiPopupFlags.MouseButtonRight)) {
84 if (menu_item(PO_RENAME, (text = "Rename", shortcut = "F2"))) {
85 g_item_action = "Rename"
86 }
87 if (menu_item(PO_DELETE, (text = "Delete", shortcut = "Del"))) {
88 g_item_action = "Delete"
89 }
90 separator()
91 if (menu_item(PO_INFO, (text = "Info", shortcut = ""))) {
92 g_item_action = "Info"
93 }
94 }
95 text("last item action: {g_item_action}")
96 spacing()
97 separator()
98
99 // ---- Stage 2: popup_context_window — anywhere in the window ----
100 text("Stage 2 - right-click empty space (not over an item):")
101 text("NoOpenOverItems cedes item right-clicks to the item menu above.")
102 popup_context_window(PO_CTX_WIN, (str_id = "ctx_win",
103 flags = ImGuiPopupFlags.MouseButtonRight |
104 ImGuiPopupFlags.NoOpenOverItems)) {
105 if (menu_item(PO_REFRESH, (text = "Refresh", shortcut = "F5"))) {
106 g_win_action = "Refresh"
107 }
108 if (menu_item(PO_RESET, (text = "Reset", shortcut = ""))) {
109 g_win_action = "Reset"
110 }
111 separator()
112 if (menu_item(PO_ABOUT, (text = "About", shortcut = ""))) {
113 g_win_action = "About"
114 }
115 }
116 text("last window action: {g_win_action}")
117 }
118
119 end_of_frame()
120 Render()
121 var w, h : int
122 live_get_framebuffer_size(w, h)
123 glViewport(0, 0, w, h)
124 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
125 glClear(GL_COLOR_BUFFER_BIT)
126 live_imgui_render()
127
128 live_end_frame()
129}
130
131[export]
132def shutdown() {
133 live_imgui_shutdown()
134 live_destroy_window()
135}
136
137[export]
138def main() {
139 init()
140 while (!exit_requested()) {
141 update()
142 }
143 shutdown()
144}
8.18.1.49.1.1. Requires
imgui/imgui_containers_builtin— both popup containers.imgui/imgui_widgets_builtin—button+menu_itemfor the target + menu rows.imgui/imgui_boost_runtime—PopupContextItemState/PopupContextWindowState(stateless markers).
8.18.1.49.1.2. popup_context_item — attached to an item
BeginPopupContextItem(str_id, flags) attaches the menu to whatever
item was most recently submitted. Submit the target first, then the
popup:
button(TARGET, (text = "Right-click me"))
popup_context_item(CTX, (str_id = "target_ctx",
flags = ImGuiPopupFlags.MouseButtonRight)) {
if (menu_item(RENAME, (text = "Rename", shortcut = "F2"))) { ... }
if (menu_item(DELETE, (text = "Delete", shortcut = "Del"))) { ... }
}
The order matters — popup_context_item calls IsItemHovered /
IsItemClicked internally against the previous item, so the
popup must come after the target.
str_id is the popup’s stack id, and must be non-empty. The
ImGui C-level default "##ContextItem" fires only on a NULL
const char*, which daslang’s string binding doesn’t expose. Pass
a unique non-empty string per consumer.
8.18.1.49.1.3. popup_context_window — attached to the window
Same shape; attaches to the enclosing window instead of a specific
item. When a window has both an item menu and a window menu, OR in
NoOpenOverItems so a right-click on an item opens that item’s menu
and only empty space opens the window menu:
window(MAIN_WIN, (text = "...")) {
button(TARGET, (text = "Right-click me"))
popup_context_item(ITEM_CTX, (str_id = "item_ctx",
flags = ImGuiPopupFlags.MouseButtonRight)) {
if (menu_item(RENAME, (text = "Rename"))) { ... }
}
popup_context_window(WIN_CTX, (str_id = "win_ctx",
flags = ImGuiPopupFlags.MouseButtonRight |
ImGuiPopupFlags.NoOpenOverItems)) {
if (menu_item(REFRESH, (text = "Refresh"))) { ... }
}
}
Without NoOpenOverItems the window popup opens on every
right-click inside the window — the button’s right-click included — so
the item menu can never open. The flag tells the window popup to ignore
right-clicks that land on an item, ceding them to that item’s own
context menu. This is the idiomatic pairing whenever a window carries
both an item menu and a window-wide menu.
8.18.1.49.1.4. Flags
ImGuiPopupFlags carries the trigger options:
MouseButtonLeft/MouseButtonRight/MouseButtonMiddle— which button opens.Rightis the conventional default.MouseButtonMask_— any of the three.NoOpenOverItems— forpopup_context_window: don’t open when the right-click lands on an item, so item context menus win (see above).NoOpenOverExistingPopup— don’t open if a popup is already open.
8.18.1.49.1.5. State model — stateless_finalize
The states (PopupContextItemState / PopupContextWindowState)
carry no pending_open / pending_close fields. ImGui drives
open/close internally on actual right-click events; there’s no
imgui_open / imgui_close channel. The widget body runs only
on the frames the popup is visible, so any per-frame logic inside the
body is naturally gated.
The asymmetry vs the popup_modal family (which DOES have
pending_open) reflects the fact that context popups are
reactive to mouse events, while modals are proactive — you
imgui_open a modal from logic, you don’t imgui_open a context
menu.
See also
Full source: modules/dasImgui/examples/tutorial/popups.das
Modal sibling: Modal popups — proactive popup
driven by pending_open.
Manual-trigger sibling: Popup window — stateless
BeginPopup + caller-driven OpenPopup.
Features-side demo: modules/dasImgui/examples/features/popup_context_item.das.
Boost macros — the macro layer.