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

8.19.49.1.1. Requires

  • imgui/imgui_containers_builtin — both popup containers.

  • imgui/imgui_widgets_builtinbutton + menu_item for the target + menu rows.

  • imgui/imgui_boost_runtimePopupContextItemState / PopupContextWindowState (stateless markers).

8.19.49.1.4. Flags

ImGuiPopupFlags carries the trigger options:

  • MouseButtonLeft / MouseButtonRight / MouseButtonMiddle — which button opens. Right is the conventional default.

  • MouseButtonMask_ — any of the three.

  • NoOpenOverItems — for popup_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.19.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.