Skip to main content

dear_imgui_rs/widget/popup/
ui.rs

1use crate::ui::Ui;
2use crate::window::{WindowFlags, validate_window_flags};
3use crate::{Id, sys};
4
5use super::context::PopupContextOptions;
6use super::flags::{validate_popup_open_flags, validate_popup_query_flags};
7use super::{ModalPopup, ModalPopupToken, PopupOpenFlags, PopupQueryFlags, PopupToken};
8
9/// # Popup Widgets
10impl Ui {
11    /// Instructs ImGui that a popup is open.
12    ///
13    /// You should **call this function once** while calling any of the following per-frame:
14    ///
15    /// - [`begin_popup`](Self::begin_popup)
16    /// - [`popup`](Self::popup)
17    /// - [`begin_modal_popup`](Self::begin_modal_popup)
18    /// - [`modal_popup`](Self::modal_popup)
19    ///
20    /// The confusing aspect to popups is that ImGui holds control over the popup itself.
21    ///
22    /// Returns `true` when this request toggles the popup toward its open state. Existing callers
23    /// that do not need to initialize popup-local state may ignore the result.
24    #[doc(alias = "OpenPopup")]
25    pub fn open_popup(&self, str_id: impl AsRef<str>) -> bool {
26        let str_id_ptr = self.scratch_txt(str_id);
27        self.run_with_bound_context(|| unsafe {
28            sys::igOpenPopup_Str(str_id_ptr, PopupOpenFlags::NONE.raw())
29        })
30    }
31
32    /// Instructs ImGui that a popup is open with flags.
33    ///
34    /// Returns `true` when this request toggles the popup toward its open state.
35    #[doc(alias = "OpenPopup")]
36    pub fn open_popup_with_flags(&self, str_id: impl AsRef<str>, flags: PopupOpenFlags) -> bool {
37        validate_popup_open_flags("Ui::open_popup_with_flags()", flags);
38        let str_id_ptr = self.scratch_txt(str_id);
39        self.run_with_bound_context(|| unsafe { sys::igOpenPopup_Str(str_id_ptr, flags.raw()) })
40    }
41
42    /// Opens a popup by an ID from the current ID stack.
43    ///
44    /// Returns `true` when the popup is toggled open.
45    #[doc(alias = "OpenPopup")]
46    pub fn open_popup_id(&self, id: Id) -> bool {
47        self.open_popup_id_with_flags(id, PopupOpenFlags::NONE)
48    }
49
50    /// Opens a popup by an ID from the current ID stack, with flags.
51    ///
52    /// Returns `true` when the popup is toggled open.
53    #[doc(alias = "OpenPopup")]
54    pub fn open_popup_id_with_flags(&self, id: Id, flags: PopupOpenFlags) -> bool {
55        assert!(
56            id.raw() != 0,
57            "Ui::open_popup_id_with_flags() id must be non-zero"
58        );
59        validate_popup_open_flags("Ui::open_popup_id_with_flags()", flags);
60        self.run_with_bound_context(|| unsafe { sys::igOpenPopup_ID(id.raw(), flags.raw()) })
61    }
62
63    /// Opens a popup when the last item is clicked (typically right-click).
64    ///
65    /// If `str_id` is `None`, the popup is associated with the last item ID.
66    /// Returns `true` only when the click opens the popup.
67    #[doc(alias = "OpenPopupOnItemClick")]
68    pub fn open_popup_on_item_click(&self, str_id: Option<&str>) -> bool {
69        self.open_popup_on_item_click_with_flags(str_id, PopupContextOptions::new())
70    }
71
72    /// Opens a popup when the last item is clicked, with explicit flags.
73    ///
74    /// Returns `true` only when the configured click opens the popup.
75    #[doc(alias = "OpenPopupOnItemClick")]
76    pub fn open_popup_on_item_click_with_flags(
77        &self,
78        str_id: Option<&str>,
79        flags: impl Into<PopupContextOptions>,
80    ) -> bool {
81        let options = flags.into();
82        options.validate("Ui::open_popup_on_item_click_with_flags()");
83        let str_id_ptr = str_id
84            .map(|s| self.scratch_txt(s))
85            .unwrap_or(std::ptr::null());
86        self.run_with_bound_context(|| unsafe {
87            sys::igOpenPopupOnItemClick(str_id_ptr, options.raw())
88        })
89    }
90
91    /// Construct a popup that can have any kind of content.
92    ///
93    /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
94    /// to signal that this popup is active.
95    #[doc(alias = "BeginPopup")]
96    pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
97        self.begin_popup_with_flags(str_id, WindowFlags::empty())
98    }
99
100    /// Construct a popup with window flags.
101    #[doc(alias = "BeginPopup")]
102    pub fn begin_popup_with_flags(
103        &self,
104        str_id: impl AsRef<str>,
105        flags: WindowFlags,
106    ) -> Option<PopupToken<'_>> {
107        validate_window_flags("Ui::begin_popup_with_flags()", flags);
108        let str_id_ptr = self.scratch_txt(str_id);
109        let render =
110            self.run_with_bound_context(|| unsafe { sys::igBeginPopup(str_id_ptr, flags.bits()) });
111
112        if render {
113            Some(PopupToken::new(self))
114        } else {
115            None
116        }
117    }
118
119    /// Construct a popup that can have any kind of content.
120    ///
121    /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
122    /// to signal that this popup is active.
123    #[doc(alias = "BeginPopup")]
124    pub fn popup<F>(&self, str_id: impl AsRef<str>, f: F)
125    where
126        F: FnOnce(),
127    {
128        if let Some(_token) = self.begin_popup(str_id) {
129            f();
130        }
131    }
132
133    /// Creates a modal popup.
134    ///
135    /// Modal popups block interaction with the rest of the application until closed.
136    #[doc(alias = "BeginPopupModal")]
137    pub fn begin_modal_popup(&self, name: impl AsRef<str>) -> Option<ModalPopupToken<'_>> {
138        let name_ptr = self.scratch_txt(name);
139        let render = self.run_with_bound_context(|| unsafe {
140            sys::igBeginPopupModal(name_ptr, std::ptr::null_mut(), WindowFlags::empty().bits())
141        });
142
143        if render {
144            Some(ModalPopupToken::new(self))
145        } else {
146            None
147        }
148    }
149
150    /// Creates a modal popup with an opened-state tracking variable.
151    ///
152    /// Passing `opened` enables the title-bar close button (X). When clicked, ImGui will set
153    /// `*opened = false` and close the popup.
154    ///
155    /// Notes:
156    /// - You still need to call [`open_popup`](Self::open_popup) once to open the modal.
157    /// - To pass window flags, use [`begin_modal_popup_config`](Self::begin_modal_popup_config).
158    #[doc(alias = "BeginPopupModal")]
159    pub fn begin_modal_popup_with_opened(
160        &self,
161        name: impl AsRef<str>,
162        opened: &mut bool,
163    ) -> Option<ModalPopupToken<'_>> {
164        let name_ptr = self.scratch_txt(name);
165        let opened_ptr = opened as *mut bool;
166        let render = self.run_with_bound_context(|| unsafe {
167            sys::igBeginPopupModal(name_ptr, opened_ptr, WindowFlags::empty().bits())
168        });
169
170        if render {
171            Some(ModalPopupToken::new(self))
172        } else {
173            None
174        }
175    }
176
177    /// Creates a modal popup builder.
178    pub fn begin_modal_popup_config<'a>(&'a self, name: &'a str) -> ModalPopup<'a> {
179        ModalPopup {
180            name,
181            opened: None,
182            flags: WindowFlags::empty(),
183            ui: self,
184        }
185    }
186
187    /// Creates a modal popup and runs a closure to construct the contents.
188    ///
189    /// Returns the result of the closure if the popup is open.
190    pub fn modal_popup<F, R>(&self, name: impl AsRef<str>, f: F) -> Option<R>
191    where
192        F: FnOnce() -> R,
193    {
194        let token = self.begin_modal_popup(name)?;
195        let result = f();
196        drop(token);
197        Some(result)
198    }
199
200    /// Creates a modal popup with an opened-state tracking variable and runs a closure to
201    /// construct the contents.
202    ///
203    /// Returns the result of the closure if the popup is open.
204    pub fn modal_popup_with_opened<F, R>(
205        &self,
206        name: impl AsRef<str>,
207        opened: &mut bool,
208        f: F,
209    ) -> Option<R>
210    where
211        F: FnOnce() -> R,
212    {
213        let token = self.begin_modal_popup_with_opened(name, opened)?;
214        let result = f();
215        drop(token);
216        Some(result)
217    }
218
219    /// Closes the current popup.
220    #[doc(alias = "CloseCurrentPopup")]
221    pub fn close_current_popup(&self) {
222        self.run_with_bound_context(|| unsafe {
223            sys::igCloseCurrentPopup();
224        });
225    }
226
227    /// Returns true if the popup is open.
228    #[doc(alias = "IsPopupOpen")]
229    pub fn is_popup_open(&self, str_id: impl AsRef<str>) -> bool {
230        let str_id_ptr = self.scratch_txt(str_id);
231        self.run_with_bound_context(|| unsafe {
232            sys::igIsPopupOpen_Str(str_id_ptr, PopupQueryFlags::NONE.raw())
233        })
234    }
235
236    /// Returns true if the popup is open with flags.
237    #[doc(alias = "IsPopupOpen")]
238    pub fn is_popup_open_with_flags(
239        &self,
240        str_id: impl AsRef<str>,
241        flags: PopupQueryFlags,
242    ) -> bool {
243        validate_popup_query_flags("Ui::is_popup_open_with_flags()", flags);
244        let str_id_ptr = self.scratch_txt(str_id);
245        self.run_with_bound_context(|| unsafe { sys::igIsPopupOpen_Str(str_id_ptr, flags.raw()) })
246    }
247
248    /// Begin a popup context menu for the last item.
249    #[doc(alias = "BeginPopupContextItem")]
250    pub fn begin_popup_context_item(&self) -> Option<PopupToken<'_>> {
251        self.begin_popup_context_item_with_flags(None, PopupContextOptions::new())
252    }
253
254    /// Begin a popup context menu for the last item with a custom label.
255    #[doc(alias = "BeginPopupContextItem")]
256    pub fn begin_popup_context_item_with_label(
257        &self,
258        str_id: Option<&str>,
259    ) -> Option<PopupToken<'_>> {
260        self.begin_popup_context_item_with_flags(str_id, PopupContextOptions::new())
261    }
262
263    /// Begin a popup context menu for the last item with explicit popup flags.
264    #[doc(alias = "BeginPopupContextItem")]
265    pub fn begin_popup_context_item_with_flags(
266        &self,
267        str_id: Option<&str>,
268        flags: impl Into<PopupContextOptions>,
269    ) -> Option<PopupToken<'_>> {
270        let options = flags.into();
271        options.validate("Ui::begin_popup_context_item_with_flags()");
272        let str_id_ptr = str_id
273            .map(|s| self.scratch_txt(s))
274            .unwrap_or(std::ptr::null());
275
276        let render = self.run_with_bound_context(|| unsafe {
277            sys::igBeginPopupContextItem(str_id_ptr, options.raw())
278        });
279
280        render.then(|| PopupToken::new(self))
281    }
282
283    /// Begin a popup context menu for the current window.
284    #[doc(alias = "BeginPopupContextWindow")]
285    pub fn begin_popup_context_window(&self) -> Option<PopupToken<'_>> {
286        self.begin_popup_context_window_with_flags(None, PopupContextOptions::new())
287    }
288
289    /// Begin a popup context menu for the current window with a custom label.
290    #[doc(alias = "BeginPopupContextWindow")]
291    pub fn begin_popup_context_window_with_label(
292        &self,
293        str_id: Option<&str>,
294    ) -> Option<PopupToken<'_>> {
295        self.begin_popup_context_window_with_flags(str_id, PopupContextOptions::new())
296    }
297
298    /// Begin a popup context menu for the current window with explicit popup flags.
299    #[doc(alias = "BeginPopupContextWindow")]
300    pub fn begin_popup_context_window_with_flags(
301        &self,
302        str_id: Option<&str>,
303        flags: impl Into<PopupContextOptions>,
304    ) -> Option<PopupToken<'_>> {
305        let options = flags.into();
306        options.validate("Ui::begin_popup_context_window_with_flags()");
307        let str_id_ptr = str_id
308            .map(|s| self.scratch_txt(s))
309            .unwrap_or(std::ptr::null());
310
311        let render = self.run_with_bound_context(|| unsafe {
312            sys::igBeginPopupContextWindow(str_id_ptr, options.raw())
313        });
314
315        render.then(|| PopupToken::new(self))
316    }
317
318    /// Begin a popup context menu for empty space (void).
319    #[doc(alias = "BeginPopupContextVoid")]
320    pub fn begin_popup_context_void(&self) -> Option<PopupToken<'_>> {
321        self.begin_popup_context_void_with_flags(None, PopupContextOptions::new())
322    }
323
324    /// Begin a popup context menu for empty space with a custom label.
325    #[doc(alias = "BeginPopupContextVoid")]
326    pub fn begin_popup_context_void_with_label(
327        &self,
328        str_id: Option<&str>,
329    ) -> Option<PopupToken<'_>> {
330        self.begin_popup_context_void_with_flags(str_id, PopupContextOptions::new())
331    }
332
333    /// Begin a popup context menu for empty space (void) with explicit popup flags.
334    #[doc(alias = "BeginPopupContextVoid")]
335    pub fn begin_popup_context_void_with_flags(
336        &self,
337        str_id: Option<&str>,
338        flags: impl Into<PopupContextOptions>,
339    ) -> Option<PopupToken<'_>> {
340        let options = flags.into();
341        options.validate("Ui::begin_popup_context_void_with_flags()");
342        let str_id_ptr = str_id
343            .map(|s| self.scratch_txt(s))
344            .unwrap_or(std::ptr::null());
345
346        let render = self.run_with_bound_context(|| unsafe {
347            sys::igBeginPopupContextVoid(str_id_ptr, options.raw())
348        });
349
350        render.then(|| PopupToken::new(self))
351    }
352}