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: examples/tutorial/popups.das.
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
2
3require imgui
4require imgui_app
5require opengl/opengl_boost
6require live/glfw_live
7require live/live_api
8require live/live_commands
9require live/live_vars
10require live_host
11require imgui/imgui_live
12require imgui/imgui_boost_runtime
13require imgui/imgui_boost_v2
14require imgui/imgui_widgets_builtin
15require imgui/imgui_containers_builtin
16require imgui/imgui_visual_aids
17
18// =============================================================================
19// TUTORIAL: popups — two right-click context popup [container]s.
20//
21// popup_context_item(IDENT, (str_id = "..", — attaches to the
22// flags = ...)) { PREVIOUSLY submitted
23// <menu_item or any body> item; right-click
24// } THAT item to open.
25//
26// popup_context_window(IDENT, (str_id = "..", — attaches to the
27// flags = ...)) { enclosing window;
28// <menu_item or any body> right-click ANYWHERE
29// } inside opens.
30//
31// ImGui drives open/close internally — no pending_open / pending_close
32// fields on the state; the body runs only on the frames the popup is
33// visible. `str_id` is the popup's stack id; pass a unique non-empty
34// string per consumer (the C-default `"##ContextWindow"` fires only on
35// a NULL const char* which daslang's string binding doesn't expose).
36//
37// flags = ImGuiPopupFlags.MouseButtonRight is the conventional opener;
38// MouseButtonLeft / MouseButtonMiddle work too (rarely useful).
39//
40// The window popup ORs in NoOpenOverItems so a right-click ON the button
41// opens the ITEM menu, not the window menu. Without it the window popup
42// swallows every right-click inside the window, items included, and the
43// item menu can never open.
44//
45// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/popups.das
46// LIVE: daslang-live modules/dasImgui/examples/tutorial/popups.das
47// =============================================================================
48
49var private g_item_action : string = "(right-click the button)"
50var private g_win_action : string = "(right-click empty area)"
51
52[export]
53def init() {
54 live_create_window("dasImgui popups tutorial", 760, 560)
55 live_imgui_init(live_window)
56 let io & = unsafe(GetIO())
57 GetStyle().FontScaleMain = 1.4
58}
59
60[export]
61def update() {
62 if (!live_begin_frame()) return
63 begin_frame()
64
65 ImGui_ImplGlfw_NewFrame()
66 apply_synth_io_override()
67 NewFrame()
68
69 SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
70 SetNextWindowSize(ImVec2(720.0f, 520.0f), ImGuiCond.Always)
71 window(PO_WIN, (text = "popups tutorial", closable = false,
72 flags = ImGuiWindowFlags.None)) {
73
74 text("Right-click context popups - two flavors.")
75 text(PO_HINT, (text = "popup_context_item attaches to the previous item; popup_context_window attaches to the enclosing window."))
76 separator()
77
78 // ---- Stage 1: popup_context_item — attached to a specific item ----
79 text("Stage 1 - right-click the target button below:")
80 button(PO_TARGET, (text = "Right-click me"))
81 popup_context_item(PO_CTX_ITEM, (str_id = "ctx_item",
82 flags = ImGuiPopupFlags.MouseButtonRight)) {
83 if (menu_item(PO_RENAME, (text = "Rename", shortcut = "F2"))) {
84 g_item_action = "Rename"
85 }
86 if (menu_item(PO_DELETE, (text = "Delete", shortcut = "Del"))) {
87 g_item_action = "Delete"
88 }
89 separator()
90 if (menu_item(PO_INFO, (text = "Info", shortcut = ""))) {
91 g_item_action = "Info"
92 }
93 }
94 text("last item action: {g_item_action}")
95 spacing()
96 separator()
97
98 // ---- Stage 2: popup_context_window — anywhere in the window ----
99 text("Stage 2 - right-click empty space (not over an item):")
100 text("NoOpenOverItems cedes item right-clicks to the item menu above.")
101 popup_context_window(PO_CTX_WIN, (str_id = "ctx_win",
102 flags = ImGuiPopupFlags.MouseButtonRight |
103 ImGuiPopupFlags.NoOpenOverItems)) {
104 if (menu_item(PO_REFRESH, (text = "Refresh", shortcut = "F5"))) {
105 g_win_action = "Refresh"
106 }
107 if (menu_item(PO_RESET, (text = "Reset", shortcut = ""))) {
108 g_win_action = "Reset"
109 }
110 separator()
111 if (menu_item(PO_ABOUT, (text = "About", shortcut = ""))) {
112 g_win_action = "About"
113 }
114 }
115 text("last window action: {g_win_action}")
116 }
117
118 end_of_frame()
119 Render()
120 var w, h : int
121 live_get_framebuffer_size(w, h)
122 glViewport(0, 0, w, h)
123 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
124 glClear(GL_COLOR_BUFFER_BIT)
125 live_imgui_render()
126
127 live_end_frame()
128}
129
130[export]
131def shutdown() {
132 live_imgui_shutdown()
133 live_destroy_window()
134}
135
136[export]
137def main() {
138 init()
139 while (!exit_requested()) {
140 update()
141 }
142 shutdown()
143}
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).
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.
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.
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.
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: 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: examples/features/popup_context_item.das.
Boost macros — the macro layer.