Popup window
ImGui exposes three popup-opening patterns: auto-trigger from a preceding
item (BeginPopupContextItem), auto-trigger from anywhere in the window
(BeginPopupContextWindow), and manual-trigger — the caller decides
when to open. The boost layer wraps each with a dedicated [container]:
popup_context_item(IDENT, (str_id, flags))— right-click on the previously-submitted item opens the popup.popup_context_window(IDENT, (str_id, flags))— right-click anywhere inside the enclosing window opens it.popup_window(IDENT, (str_id, flags))— no auto-trigger; the caller drivesopen_popup(str_id, flags)from any custom predicate.
The manual form is what the cpp demo’s per-column table-context section
needs: one shared str_id across multiple PushID scopes, each
deciding for itself when to OpenPopup based on hover state, key
combinations, or any predicate that has no preceding “item” to attach to.
Source: examples/tutorial/popup_window.das.
Walkthrough
The recording drives both trigger scopes for real: clicking Open via
button fires open_popup and the popup body renders; picking a fruit
calls close_current_popup and the Last pick line updates;
right-clicking anywhere in the hover region opens the same popup through
the second scope. Each open is verified against the popup body’s
rendered-this-frame state and each pick against the status line, so a
trigger that failed to open or close the popup would abort the recording.
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_id_builtin
17require imgui/imgui_visual_aids
18
19// =============================================================================
20// TUTORIAL: popup_window — stateless BeginPopup/EndPopup with manual trigger.
21//
22// popup_window(IDENT, (str_id = "..", flags = ..)) { body }
23//
24// Distinct from the auto-triggered relatives:
25//
26// popup_context_item — right-click on the preceding item opens the popup.
27// popup_context_window — right-click anywhere in the window opens it.
28// popup_window — NO auto-trigger; caller drives open_popup(str_id).
29//
30// The manual form maps the cpp pattern from `imgui_demo.cpp`'s table-context
31// section: one shared str_id, multiple PushID-scoped instances, custom
32// hover/key/release predicates feeding the same OpenPopup call.
33//
34// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/popup_window.das
35// LIVE: daslang-live modules/dasImgui/examples/tutorial/popup_window.das
36// =============================================================================
37
38var private LAST_PICK : string = "(none yet)"
39
40[export]
41def init() {
42 live_create_window("dasImgui popup_window tutorial", 760, 480)
43 live_imgui_init(live_window)
44 let io & = unsafe(GetIO())
45 GetStyle().FontScaleMain = 1.4
46}
47
48[export]
49def update() {
50 if (!live_begin_frame()) return
51 begin_frame()
52
53 ImGui_ImplGlfw_NewFrame()
54 apply_synth_io_override()
55 NewFrame()
56
57 SetNextWindowPos(ImVec2(40.0f, 40.0f), ImGuiCond.Always)
58 SetNextWindowSize(ImVec2(560.0f, 340.0f), ImGuiCond.Always)
59 window(PW_WIN, (text = "popup_window", closable = false,
60 flags = ImGuiWindowFlags.None)) {
61 text("Two scopes share str_id \"fruit_picker\":")
62
63 // Scope A: explicit click trigger.
64 with_id("via_button") {
65 if (button(PW_BTN, (text = "Open via button"))) {
66 open_popup("fruit_picker")
67 }
68 popup_window(PW_BTN_POPUP, (str_id = "fruit_picker",
69 flags = ImGuiWindowFlags.None)) {
70 draw_picker_body()
71 }
72 }
73
74 separator()
75
76 // Scope B: hover-region + right-click — no preceding item involved.
77 with_id("via_region") {
78 text(PW_HOVER, (text = "(hover this region, right-click)"))
79 let region_min = GetItemRectMin()
80 let region_max = GetItemRectMax()
81 let in_region = (IsMouseHoveringRect(region_min, region_max, true) &&
82 !IsAnyItemHovered())
83 if (in_region && IsMouseReleased(ImGuiMouseButton.Right)) {
84 open_popup("fruit_picker")
85 }
86 popup_window(PW_REG_POPUP, (str_id = "fruit_picker",
87 flags = ImGuiWindowFlags.None)) {
88 draw_picker_body()
89 }
90 }
91
92 separator()
93 text(PW_STATUS, (text = "Last pick: {LAST_PICK}"))
94 }
95
96 end_of_frame()
97 Render()
98 var w, h : int
99 live_get_framebuffer_size(w, h)
100 glViewport(0, 0, w, h)
101 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
102 glClear(GL_COLOR_BUFFER_BIT)
103 live_imgui_render()
104
105 live_end_frame()
106}
107
108def private draw_picker_body() {
109 text("Pick a fruit:")
110 if (menu_label(PW_APPLE, (text = "Apple"))) {
111 LAST_PICK = "Apple"
112 close_current_popup()
113 }
114 if (menu_label(PW_BANANA, (text = "Banana"))) {
115 LAST_PICK = "Banana"
116 close_current_popup()
117 }
118 if (menu_label(PW_CHERRY, (text = "Cherry"))) {
119 LAST_PICK = "Cherry"
120 close_current_popup()
121 }
122 separator()
123 if (button(PW_CLOSE, (text = "Close"))) {
124 close_current_popup()
125 }
126}
127
128[export]
129def shutdown() {
130 live_imgui_shutdown()
131 live_destroy_window()
132}
133
134[export]
135def main() {
136 init()
137 while (!exit_requested()) {
138 update()
139 }
140 shutdown()
141}
Requires
Already in the baseline boost layer:
imgui/imgui_containers_builtin—popup_window,open_popup,close_current_popup.imgui/imgui_id_builtin—with_idfor the per-scope ID-stack push.
Two scopes, one str_id
ImGui keys a popup’s open state by str_id under the current ID stack.
When two render sites want the same logical popup (“pick a fruit”) but
need independent open state, push a distinct with_id("scope") { ... }
around each:
with_id("via_button") {
if (button(OPEN_BTN, (text = "Open via button"))) {
open_popup("fruit_picker")
}
popup_window(BTN_POPUP, (str_id = "fruit_picker", flags = ImGuiWindowFlags.None)) {
// body
}
}
with_id("via_region") {
// ...custom hover/release trigger...
if (in_region && IsMouseReleased(ImGuiMouseButton.Right)) {
open_popup("fruit_picker")
}
popup_window(REG_POPUP, (str_id = "fruit_picker", flags = ImGuiWindowFlags.None)) {
// body
}
}
The two scopes see the same str_id, but ImGui’s ID-stack push from
with_id mangles the open state per scope. The shared body factor lets
both scopes call the same private draw_picker_body() helper.
Custom predicates
Because there’s no auto-trigger, the caller is free to compose any predicate that should open the popup. The features demo combines a hover-region check with a right-release:
text(HINT, (text = "(hover this region, right-click)"))
let region_min = GetItemRectMin()
let region_max = GetItemRectMax()
let in_region = (IsMouseHoveringRect(region_min, region_max, true) &&
!IsAnyItemHovered())
if (in_region && IsMouseReleased(ImGuiMouseButton.Right)) {
open_popup("fruit_picker")
}
This shape is what the cpp imgui_demo’s "Tables > Context menus"
section uses to open one "MyPopup" per column. The
examples/imgui_demo/tables.das port reuses popup_window for
exactly that loop.
State
PopupWindowState is empty — ImGui owns the open lifecycle by
str_id and the boost wrapper just brackets the body. The state
global is what the registry walks to find the widget by IDENT; the
snapshot reports kind="popup_window" for every registered instance
regardless of whether the popup is currently visible.
Standalone vs live
Same convention as the other tutorials.
See also
Full source: examples/tutorial/popup_window.das
Features-side demo: examples/features/popup_window.das.
Integration test: tests/integration/test_popup_window.das.
Boost macros — the macro layer.