Skip to main content

dear_imgui_rs/widget/popup/
ui.rs

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