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_builtinbutton + menu_item for the target + menu rows.

  • imgui/imgui_boost_runtimePopupContextItemState / PopupContextWindowState (stateless markers).

8.18.1.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.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.