1#![allow(
7 clippy::cast_possible_truncation,
8 clippy::cast_sign_loss,
9 clippy::as_conversions
10)]
11use crate::MouseButton;
12use crate::sys;
13use crate::ui::Ui;
14use crate::window::WindowFlags;
15
16impl Ui {
18 #[doc(alias = "OpenPopup")]
29 pub fn open_popup(&self, str_id: impl AsRef<str>) {
30 let str_id_ptr = self.scratch_txt(str_id);
31 unsafe { sys::igOpenPopup_Str(str_id_ptr, PopupFlags::NONE.bits()) }
32 }
33
34 #[doc(alias = "OpenPopup")]
36 pub fn open_popup_with_flags(&self, str_id: impl AsRef<str>, flags: PopupFlags) {
37 let str_id_ptr = self.scratch_txt(str_id);
38 unsafe { sys::igOpenPopup_Str(str_id_ptr, flags.bits()) }
39 }
40
41 #[doc(alias = "OpenPopupOnItemClick")]
45 pub fn open_popup_on_item_click(&self, str_id: Option<&str>) {
46 self.open_popup_on_item_click_with_flags(str_id, PopupContextOptions::new());
47 }
48
49 #[doc(alias = "OpenPopupOnItemClick")]
51 pub fn open_popup_on_item_click_with_flags(
52 &self,
53 str_id: Option<&str>,
54 flags: impl Into<PopupContextOptions>,
55 ) {
56 let options = flags.into();
57 let str_id_ptr = str_id
58 .map(|s| self.scratch_txt(s))
59 .unwrap_or(std::ptr::null());
60 unsafe { sys::igOpenPopupOnItemClick(str_id_ptr, options.raw()) }
61 }
62
63 #[doc(alias = "BeginPopup")]
68 pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
69 self.begin_popup_with_flags(str_id, WindowFlags::empty())
70 }
71
72 #[doc(alias = "BeginPopup")]
74 pub fn begin_popup_with_flags(
75 &self,
76 str_id: impl AsRef<str>,
77 flags: WindowFlags,
78 ) -> Option<PopupToken<'_>> {
79 let str_id_ptr = self.scratch_txt(str_id);
80 let render = unsafe { sys::igBeginPopup(str_id_ptr, flags.bits()) };
81
82 if render {
83 Some(PopupToken::new(self))
84 } else {
85 None
86 }
87 }
88
89 #[doc(alias = "BeginPopup")]
94 pub fn popup<F>(&self, str_id: impl AsRef<str>, f: F)
95 where
96 F: FnOnce(),
97 {
98 if let Some(_token) = self.begin_popup(str_id) {
99 f();
100 }
101 }
102
103 #[doc(alias = "BeginPopupModal")]
107 pub fn begin_modal_popup(&self, name: impl AsRef<str>) -> Option<ModalPopupToken<'_>> {
108 let name_ptr = self.scratch_txt(name);
109 let render = unsafe {
110 sys::igBeginPopupModal(name_ptr, std::ptr::null_mut(), WindowFlags::empty().bits())
111 };
112
113 if render {
114 Some(ModalPopupToken::new(self))
115 } else {
116 None
117 }
118 }
119
120 #[doc(alias = "BeginPopupModal")]
129 pub fn begin_modal_popup_with_opened(
130 &self,
131 name: impl AsRef<str>,
132 opened: &mut bool,
133 ) -> Option<ModalPopupToken<'_>> {
134 let name_ptr = self.scratch_txt(name);
135 let opened_ptr = opened as *mut bool;
136 let render =
137 unsafe { sys::igBeginPopupModal(name_ptr, opened_ptr, WindowFlags::empty().bits()) };
138
139 if render {
140 Some(ModalPopupToken::new(self))
141 } else {
142 None
143 }
144 }
145
146 pub fn begin_modal_popup_config<'a>(&'a self, name: &'a str) -> ModalPopup<'a> {
148 ModalPopup {
149 name,
150 opened: None,
151 flags: WindowFlags::empty(),
152 ui: self,
153 }
154 }
155
156 pub fn modal_popup<F, R>(&self, name: impl AsRef<str>, f: F) -> Option<R>
160 where
161 F: FnOnce() -> R,
162 {
163 self.begin_modal_popup(name).map(|_token| f())
164 }
165
166 pub fn modal_popup_with_opened<F, R>(
171 &self,
172 name: impl AsRef<str>,
173 opened: &mut bool,
174 f: F,
175 ) -> Option<R>
176 where
177 F: FnOnce() -> R,
178 {
179 self.begin_modal_popup_with_opened(name, opened)
180 .map(|_token| f())
181 }
182
183 #[doc(alias = "CloseCurrentPopup")]
185 pub fn close_current_popup(&self) {
186 unsafe {
187 sys::igCloseCurrentPopup();
188 }
189 }
190
191 #[doc(alias = "IsPopupOpen")]
193 pub fn is_popup_open(&self, str_id: impl AsRef<str>) -> bool {
194 let str_id_ptr = self.scratch_txt(str_id);
195 unsafe { sys::igIsPopupOpen_Str(str_id_ptr, PopupFlags::NONE.bits()) }
196 }
197
198 #[doc(alias = "IsPopupOpen")]
200 pub fn is_popup_open_with_flags(&self, str_id: impl AsRef<str>, flags: PopupFlags) -> bool {
201 let str_id_ptr = self.scratch_txt(str_id);
202 unsafe { sys::igIsPopupOpen_Str(str_id_ptr, flags.bits()) }
203 }
204
205 #[doc(alias = "BeginPopupContextItem")]
207 pub fn begin_popup_context_item(&self) -> Option<PopupToken<'_>> {
208 self.begin_popup_context_item_with_flags(None, PopupContextOptions::new())
209 }
210
211 #[doc(alias = "BeginPopupContextItem")]
213 pub fn begin_popup_context_item_with_label(
214 &self,
215 str_id: Option<&str>,
216 ) -> Option<PopupToken<'_>> {
217 self.begin_popup_context_item_with_flags(str_id, PopupContextOptions::new())
218 }
219
220 #[doc(alias = "BeginPopupContextItem")]
222 pub fn begin_popup_context_item_with_flags(
223 &self,
224 str_id: Option<&str>,
225 flags: impl Into<PopupContextOptions>,
226 ) -> Option<PopupToken<'_>> {
227 let options = flags.into();
228 let str_id_ptr = str_id
229 .map(|s| self.scratch_txt(s))
230 .unwrap_or(std::ptr::null());
231
232 let render = unsafe { sys::igBeginPopupContextItem(str_id_ptr, options.raw()) };
233
234 render.then(|| PopupToken::new(self))
235 }
236
237 #[doc(alias = "BeginPopupContextWindow")]
239 pub fn begin_popup_context_window(&self) -> Option<PopupToken<'_>> {
240 self.begin_popup_context_window_with_flags(None, PopupContextOptions::new())
241 }
242
243 #[doc(alias = "BeginPopupContextWindow")]
245 pub fn begin_popup_context_window_with_label(
246 &self,
247 str_id: Option<&str>,
248 ) -> Option<PopupToken<'_>> {
249 self.begin_popup_context_window_with_flags(str_id, PopupContextOptions::new())
250 }
251
252 #[doc(alias = "BeginPopupContextWindow")]
254 pub fn begin_popup_context_window_with_flags(
255 &self,
256 str_id: Option<&str>,
257 flags: impl Into<PopupContextOptions>,
258 ) -> Option<PopupToken<'_>> {
259 let options = flags.into();
260 let str_id_ptr = str_id
261 .map(|s| self.scratch_txt(s))
262 .unwrap_or(std::ptr::null());
263
264 let render = unsafe { sys::igBeginPopupContextWindow(str_id_ptr, options.raw()) };
265
266 render.then(|| PopupToken::new(self))
267 }
268
269 #[doc(alias = "BeginPopupContextVoid")]
271 pub fn begin_popup_context_void(&self) -> Option<PopupToken<'_>> {
272 self.begin_popup_context_void_with_flags(None, PopupContextOptions::new())
273 }
274
275 #[doc(alias = "BeginPopupContextVoid")]
277 pub fn begin_popup_context_void_with_label(
278 &self,
279 str_id: Option<&str>,
280 ) -> Option<PopupToken<'_>> {
281 self.begin_popup_context_void_with_flags(str_id, PopupContextOptions::new())
282 }
283
284 #[doc(alias = "BeginPopupContextVoid")]
286 pub fn begin_popup_context_void_with_flags(
287 &self,
288 str_id: Option<&str>,
289 flags: impl Into<PopupContextOptions>,
290 ) -> Option<PopupToken<'_>> {
291 let options = flags.into();
292 let str_id_ptr = str_id
293 .map(|s| self.scratch_txt(s))
294 .unwrap_or(std::ptr::null());
295
296 let render = unsafe { sys::igBeginPopupContextVoid(str_id_ptr, options.raw()) };
297
298 render.then(|| PopupToken::new(self))
299 }
300}
301
302bitflags::bitflags! {
303 #[repr(transparent)]
308 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
309 pub struct PopupFlags: i32 {
310 const NONE = sys::ImGuiPopupFlags_None as i32;
312 const NO_REOPEN = sys::ImGuiPopupFlags_NoReopen as i32;
314 const NO_OPEN_OVER_EXISTING_POPUP = sys::ImGuiPopupFlags_NoOpenOverExistingPopup as i32;
316 const NO_OPEN_OVER_ITEMS = sys::ImGuiPopupFlags_NoOpenOverItems as i32;
318 const ANY_POPUP_ID = sys::ImGuiPopupFlags_AnyPopupId as i32;
320 const ANY_POPUP_LEVEL = sys::ImGuiPopupFlags_AnyPopupLevel as i32;
322 const ANY_POPUP = Self::ANY_POPUP_ID.bits() | Self::ANY_POPUP_LEVEL.bits();
324 }
325}
326
327#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
329pub enum PopupContextMouseButton {
330 Left,
332 #[default]
334 Right,
335 Middle,
337}
338
339impl PopupContextMouseButton {
340 #[inline]
341 const fn raw(self) -> i32 {
342 match self {
343 Self::Left => sys::ImGuiPopupFlags_MouseButtonLeft as i32,
344 Self::Right => sys::ImGuiPopupFlags_MouseButtonRight as i32,
345 Self::Middle => sys::ImGuiPopupFlags_MouseButtonMiddle as i32,
346 }
347 }
348}
349
350impl From<MouseButton> for PopupContextMouseButton {
351 fn from(button: MouseButton) -> Self {
352 match button {
353 MouseButton::Left => Self::Left,
354 MouseButton::Right => Self::Right,
355 MouseButton::Middle => Self::Middle,
356 MouseButton::Extra1 | MouseButton::Extra2 => {
357 panic!(
358 "Dear ImGui popup context helpers only support left, right, and middle buttons"
359 )
360 }
361 }
362 }
363}
364
365#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
368pub struct PopupContextOptions {
369 pub flags: PopupFlags,
370 pub mouse_button: PopupContextMouseButton,
371}
372
373impl PopupContextOptions {
374 pub const fn new() -> Self {
375 Self {
376 flags: PopupFlags::NONE,
377 mouse_button: PopupContextMouseButton::Right,
378 }
379 }
380
381 pub fn flags(mut self, flags: PopupFlags) -> Self {
382 self.flags = flags;
383 self
384 }
385
386 pub fn mouse_button(mut self, button: impl Into<PopupContextMouseButton>) -> Self {
387 self.mouse_button = button.into();
388 self
389 }
390
391 pub fn bits(self) -> i32 {
392 self.raw()
393 }
394
395 #[inline]
396 pub(crate) fn raw(self) -> i32 {
397 self.flags.bits() | self.mouse_button.raw()
398 }
399}
400
401impl Default for PopupContextOptions {
402 fn default() -> Self {
403 Self::new()
404 }
405}
406
407impl From<PopupFlags> for PopupContextOptions {
408 fn from(flags: PopupFlags) -> Self {
409 Self::new().flags(flags)
410 }
411}
412
413impl From<PopupContextMouseButton> for PopupContextOptions {
414 fn from(button: PopupContextMouseButton) -> Self {
415 Self::new().mouse_button(button)
416 }
417}
418
419impl From<MouseButton> for PopupContextOptions {
420 fn from(button: MouseButton) -> Self {
421 Self::new().mouse_button(button)
422 }
423}
424
425#[derive(Debug)]
427#[must_use]
428pub struct ModalPopup<'ui> {
429 name: &'ui str,
430 opened: Option<&'ui mut bool>,
431 flags: WindowFlags,
432 ui: &'ui Ui,
433}
434
435impl<'ui> ModalPopup<'ui> {
436 pub fn opened(mut self, opened: &'ui mut bool) -> Self {
438 self.opened = Some(opened);
439 self
440 }
441
442 pub fn flags(mut self, flags: WindowFlags) -> Self {
444 self.flags = flags;
445 self
446 }
447
448 pub fn begin(self) -> Option<ModalPopupToken<'ui>> {
450 let name_ptr = self.ui.scratch_txt(self.name);
451 let opened_ptr = self
452 .opened
453 .map(|o| o as *mut bool)
454 .unwrap_or(std::ptr::null_mut());
455
456 let render = unsafe { sys::igBeginPopupModal(name_ptr, opened_ptr, self.flags.bits()) };
457
458 if render {
459 Some(ModalPopupToken::new(self.ui))
460 } else {
461 None
462 }
463 }
464}
465
466#[must_use]
468pub struct PopupToken<'ui> {
469 _ui: &'ui Ui,
470}
471
472impl<'ui> PopupToken<'ui> {
473 fn new(ui: &'ui Ui) -> Self {
475 PopupToken { _ui: ui }
476 }
477
478 pub fn end(self) {
480 }
482}
483
484impl<'ui> Drop for PopupToken<'ui> {
485 fn drop(&mut self) {
486 unsafe {
487 sys::igEndPopup();
488 }
489 }
490}
491
492#[must_use]
494pub struct ModalPopupToken<'ui> {
495 _ui: &'ui Ui,
496}
497
498impl<'ui> ModalPopupToken<'ui> {
499 fn new(ui: &'ui Ui) -> Self {
501 ModalPopupToken { _ui: ui }
502 }
503
504 pub fn end(self) {
506 }
508}
509
510impl<'ui> Drop for ModalPopupToken<'ui> {
511 fn drop(&mut self) {
512 unsafe {
513 sys::igEndPopup();
514 }
515 }
516}