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        self.begin_modal_popup(name).map(|_token| f())
195    }
196
197    /// Creates a modal popup with an opened-state tracking variable and runs a closure to
198    /// construct the contents.
199    ///
200    /// Returns the result of the closure if the popup is open.
201    pub fn modal_popup_with_opened<F, R>(
202        &self,
203        name: impl AsRef<str>,
204        opened: &mut bool,
205        f: F,
206    ) -> Option<R>
207    where
208        F: FnOnce() -> R,
209    {
210        self.begin_modal_popup_with_opened(name, opened)
211            .map(|_token| f())
212    }
213
214    /// Closes the current popup.
215    #[doc(alias = "CloseCurrentPopup")]
216    pub fn close_current_popup(&self) {
217        self.run_with_bound_context(|| unsafe {
218            sys::igCloseCurrentPopup();
219        });
220    }
221
222    /// Returns true if the popup is open.
223    #[doc(alias = "IsPopupOpen")]
224    pub fn is_popup_open(&self, str_id: impl AsRef<str>) -> bool {
225        let str_id_ptr = self.scratch_txt(str_id);
226        self.run_with_bound_context(|| unsafe {
227            sys::igIsPopupOpen_Str(str_id_ptr, PopupQueryFlags::NONE.raw())
228        })
229    }
230
231    /// Returns true if the popup is open with flags.
232    #[doc(alias = "IsPopupOpen")]
233    pub fn is_popup_open_with_flags(
234        &self,
235        str_id: impl AsRef<str>,
236        flags: PopupQueryFlags,
237    ) -> bool {
238        validate_popup_query_flags("Ui::is_popup_open_with_flags()", flags);
239        let str_id_ptr = self.scratch_txt(str_id);
240        self.run_with_bound_context(|| unsafe { sys::igIsPopupOpen_Str(str_id_ptr, flags.raw()) })
241    }
242
243    /// Begin a popup context menu for the last item.
244    #[doc(alias = "BeginPopupContextItem")]
245    pub fn begin_popup_context_item(&self) -> Option<PopupToken<'_>> {
246        self.begin_popup_context_item_with_flags(None, PopupContextOptions::new())
247    }
248
249    /// Begin a popup context menu for the last item with a custom label.
250    #[doc(alias = "BeginPopupContextItem")]
251    pub fn begin_popup_context_item_with_label(
252        &self,
253        str_id: Option<&str>,
254    ) -> Option<PopupToken<'_>> {
255        self.begin_popup_context_item_with_flags(str_id, PopupContextOptions::new())
256    }
257
258    /// Begin a popup context menu for the last item with explicit popup flags.
259    #[doc(alias = "BeginPopupContextItem")]
260    pub fn begin_popup_context_item_with_flags(
261        &self,
262        str_id: Option<&str>,
263        flags: impl Into<PopupContextOptions>,
264    ) -> Option<PopupToken<'_>> {
265        let options = flags.into();
266        options.validate("Ui::begin_popup_context_item_with_flags()");
267        let str_id_ptr = str_id
268            .map(|s| self.scratch_txt(s))
269            .unwrap_or(std::ptr::null());
270
271        let render = self.run_with_bound_context(|| unsafe {
272            sys::igBeginPopupContextItem(str_id_ptr, options.raw())
273        });
274
275        render.then(|| PopupToken::new(self))
276    }
277
278    /// Begin a popup context menu for the current window.
279    #[doc(alias = "BeginPopupContextWindow")]
280    pub fn begin_popup_context_window(&self) -> Option<PopupToken<'_>> {
281        self.begin_popup_context_window_with_flags(None, PopupContextOptions::new())
282    }
283
284    /// Begin a popup context menu for the current window with a custom label.
285    #[doc(alias = "BeginPopupContextWindow")]
286    pub fn begin_popup_context_window_with_label(
287        &self,
288        str_id: Option<&str>,
289    ) -> Option<PopupToken<'_>> {
290        self.begin_popup_context_window_with_flags(str_id, PopupContextOptions::new())
291    }
292
293    /// Begin a popup context menu for the current window with explicit popup flags.
294    #[doc(alias = "BeginPopupContextWindow")]
295    pub fn begin_popup_context_window_with_flags(
296        &self,
297        str_id: Option<&str>,
298        flags: impl Into<PopupContextOptions>,
299    ) -> Option<PopupToken<'_>> {
300        let options = flags.into();
301        options.validate("Ui::begin_popup_context_window_with_flags()");
302        let str_id_ptr = str_id
303            .map(|s| self.scratch_txt(s))
304            .unwrap_or(std::ptr::null());
305
306        let render = self.run_with_bound_context(|| unsafe {
307            sys::igBeginPopupContextWindow(str_id_ptr, options.raw())
308        });
309
310        render.then(|| PopupToken::new(self))
311    }
312
313    /// Begin a popup context menu for empty space (void).
314    #[doc(alias = "BeginPopupContextVoid")]
315    pub fn begin_popup_context_void(&self) -> Option<PopupToken<'_>> {
316        self.begin_popup_context_void_with_flags(None, PopupContextOptions::new())
317    }
318
319    /// Begin a popup context menu for empty space with a custom label.
320    #[doc(alias = "BeginPopupContextVoid")]
321    pub fn begin_popup_context_void_with_label(
322        &self,
323        str_id: Option<&str>,
324    ) -> Option<PopupToken<'_>> {
325        self.begin_popup_context_void_with_flags(str_id, PopupContextOptions::new())
326    }
327
328    /// Begin a popup context menu for empty space (void) with explicit popup flags.
329    #[doc(alias = "BeginPopupContextVoid")]
330    pub fn begin_popup_context_void_with_flags(
331        &self,
332        str_id: Option<&str>,
333        flags: impl Into<PopupContextOptions>,
334    ) -> Option<PopupToken<'_>> {
335        let options = flags.into();
336        options.validate("Ui::begin_popup_context_void_with_flags()");
337        let str_id_ptr = str_id
338            .map(|s| self.scratch_txt(s))
339            .unwrap_or(std::ptr::null());
340
341        let render = self.run_with_bound_context(|| unsafe {
342            sys::igBeginPopupContextVoid(str_id_ptr, options.raw())
343        });
344
345        render.then(|| PopupToken::new(self))
346    }
347}