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, validate_window_flags};
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 validate_popup_flags("Ui::open_popup_with_flags()", flags);
38 let str_id_ptr = self.scratch_txt(str_id);
39 unsafe { sys::igOpenPopup_Str(str_id_ptr, flags.bits()) }
40 }
41
42 #[doc(alias = "OpenPopupOnItemClick")]
46 pub fn open_popup_on_item_click(&self, str_id: Option<&str>) {
47 self.open_popup_on_item_click_with_flags(str_id, PopupContextOptions::new());
48 }
49
50 #[doc(alias = "OpenPopupOnItemClick")]
52 pub fn open_popup_on_item_click_with_flags(
53 &self,
54 str_id: Option<&str>,
55 flags: impl Into<PopupContextOptions>,
56 ) {
57 let options = flags.into();
58 options.validate("Ui::open_popup_on_item_click_with_flags()");
59 let str_id_ptr = str_id
60 .map(|s| self.scratch_txt(s))
61 .unwrap_or(std::ptr::null());
62 unsafe { sys::igOpenPopupOnItemClick(str_id_ptr, options.raw()) }
63 }
64
65 #[doc(alias = "BeginPopup")]
70 pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
71 self.begin_popup_with_flags(str_id, WindowFlags::empty())
72 }
73
74 #[doc(alias = "BeginPopup")]
76 pub fn begin_popup_with_flags(
77 &self,
78 str_id: impl AsRef<str>,
79 flags: WindowFlags,
80 ) -> Option<PopupToken<'_>> {
81 validate_window_flags("Ui::begin_popup_with_flags()", flags);
82 let str_id_ptr = self.scratch_txt(str_id);
83 let render = unsafe { sys::igBeginPopup(str_id_ptr, flags.bits()) };
84
85 if render {
86 Some(PopupToken::new(self))
87 } else {
88 None
89 }
90 }
91
92 #[doc(alias = "BeginPopup")]
97 pub fn popup<F>(&self, str_id: impl AsRef<str>, f: F)
98 where
99 F: FnOnce(),
100 {
101 if let Some(_token) = self.begin_popup(str_id) {
102 f();
103 }
104 }
105
106 #[doc(alias = "BeginPopupModal")]
110 pub fn begin_modal_popup(&self, name: impl AsRef<str>) -> Option<ModalPopupToken<'_>> {
111 let name_ptr = self.scratch_txt(name);
112 let render = unsafe {
113 sys::igBeginPopupModal(name_ptr, std::ptr::null_mut(), WindowFlags::empty().bits())
114 };
115
116 if render {
117 Some(ModalPopupToken::new(self))
118 } else {
119 None
120 }
121 }
122
123 #[doc(alias = "BeginPopupModal")]
132 pub fn begin_modal_popup_with_opened(
133 &self,
134 name: impl AsRef<str>,
135 opened: &mut bool,
136 ) -> Option<ModalPopupToken<'_>> {
137 let name_ptr = self.scratch_txt(name);
138 let opened_ptr = opened as *mut bool;
139 let render =
140 unsafe { sys::igBeginPopupModal(name_ptr, opened_ptr, WindowFlags::empty().bits()) };
141
142 if render {
143 Some(ModalPopupToken::new(self))
144 } else {
145 None
146 }
147 }
148
149 pub fn begin_modal_popup_config<'a>(&'a self, name: &'a str) -> ModalPopup<'a> {
151 ModalPopup {
152 name,
153 opened: None,
154 flags: WindowFlags::empty(),
155 ui: self,
156 }
157 }
158
159 pub fn modal_popup<F, R>(&self, name: impl AsRef<str>, f: F) -> Option<R>
163 where
164 F: FnOnce() -> R,
165 {
166 self.begin_modal_popup(name).map(|_token| f())
167 }
168
169 pub fn modal_popup_with_opened<F, R>(
174 &self,
175 name: impl AsRef<str>,
176 opened: &mut bool,
177 f: F,
178 ) -> Option<R>
179 where
180 F: FnOnce() -> R,
181 {
182 self.begin_modal_popup_with_opened(name, opened)
183 .map(|_token| f())
184 }
185
186 #[doc(alias = "CloseCurrentPopup")]
188 pub fn close_current_popup(&self) {
189 unsafe {
190 sys::igCloseCurrentPopup();
191 }
192 }
193
194 #[doc(alias = "IsPopupOpen")]
196 pub fn is_popup_open(&self, str_id: impl AsRef<str>) -> bool {
197 let str_id_ptr = self.scratch_txt(str_id);
198 unsafe { sys::igIsPopupOpen_Str(str_id_ptr, PopupFlags::NONE.bits()) }
199 }
200
201 #[doc(alias = "IsPopupOpen")]
203 pub fn is_popup_open_with_flags(&self, str_id: impl AsRef<str>, flags: PopupFlags) -> bool {
204 validate_popup_query_flags("Ui::is_popup_open_with_flags()", flags);
205 let str_id_ptr = self.scratch_txt(str_id);
206 unsafe { sys::igIsPopupOpen_Str(str_id_ptr, flags.bits()) }
207 }
208
209 #[doc(alias = "BeginPopupContextItem")]
211 pub fn begin_popup_context_item(&self) -> Option<PopupToken<'_>> {
212 self.begin_popup_context_item_with_flags(None, PopupContextOptions::new())
213 }
214
215 #[doc(alias = "BeginPopupContextItem")]
217 pub fn begin_popup_context_item_with_label(
218 &self,
219 str_id: Option<&str>,
220 ) -> Option<PopupToken<'_>> {
221 self.begin_popup_context_item_with_flags(str_id, PopupContextOptions::new())
222 }
223
224 #[doc(alias = "BeginPopupContextItem")]
226 pub fn begin_popup_context_item_with_flags(
227 &self,
228 str_id: Option<&str>,
229 flags: impl Into<PopupContextOptions>,
230 ) -> Option<PopupToken<'_>> {
231 let options = flags.into();
232 options.validate("Ui::begin_popup_context_item_with_flags()");
233 let str_id_ptr = str_id
234 .map(|s| self.scratch_txt(s))
235 .unwrap_or(std::ptr::null());
236
237 let render = unsafe { sys::igBeginPopupContextItem(str_id_ptr, options.raw()) };
238
239 render.then(|| PopupToken::new(self))
240 }
241
242 #[doc(alias = "BeginPopupContextWindow")]
244 pub fn begin_popup_context_window(&self) -> Option<PopupToken<'_>> {
245 self.begin_popup_context_window_with_flags(None, PopupContextOptions::new())
246 }
247
248 #[doc(alias = "BeginPopupContextWindow")]
250 pub fn begin_popup_context_window_with_label(
251 &self,
252 str_id: Option<&str>,
253 ) -> Option<PopupToken<'_>> {
254 self.begin_popup_context_window_with_flags(str_id, PopupContextOptions::new())
255 }
256
257 #[doc(alias = "BeginPopupContextWindow")]
259 pub fn begin_popup_context_window_with_flags(
260 &self,
261 str_id: Option<&str>,
262 flags: impl Into<PopupContextOptions>,
263 ) -> Option<PopupToken<'_>> {
264 let options = flags.into();
265 options.validate("Ui::begin_popup_context_window_with_flags()");
266 let str_id_ptr = str_id
267 .map(|s| self.scratch_txt(s))
268 .unwrap_or(std::ptr::null());
269
270 let render = unsafe { sys::igBeginPopupContextWindow(str_id_ptr, options.raw()) };
271
272 render.then(|| PopupToken::new(self))
273 }
274
275 #[doc(alias = "BeginPopupContextVoid")]
277 pub fn begin_popup_context_void(&self) -> Option<PopupToken<'_>> {
278 self.begin_popup_context_void_with_flags(None, PopupContextOptions::new())
279 }
280
281 #[doc(alias = "BeginPopupContextVoid")]
283 pub fn begin_popup_context_void_with_label(
284 &self,
285 str_id: Option<&str>,
286 ) -> Option<PopupToken<'_>> {
287 self.begin_popup_context_void_with_flags(str_id, PopupContextOptions::new())
288 }
289
290 #[doc(alias = "BeginPopupContextVoid")]
292 pub fn begin_popup_context_void_with_flags(
293 &self,
294 str_id: Option<&str>,
295 flags: impl Into<PopupContextOptions>,
296 ) -> Option<PopupToken<'_>> {
297 let options = flags.into();
298 options.validate("Ui::begin_popup_context_void_with_flags()");
299 let str_id_ptr = str_id
300 .map(|s| self.scratch_txt(s))
301 .unwrap_or(std::ptr::null());
302
303 let render = unsafe { sys::igBeginPopupContextVoid(str_id_ptr, options.raw()) };
304
305 render.then(|| PopupToken::new(self))
306 }
307}
308
309fn validate_popup_flags(caller: &str, flags: PopupFlags) {
310 let unsupported = flags.bits() & !PopupFlags::all().bits();
311 assert!(
312 unsupported == 0,
313 "{caller} received unsupported ImGuiPopupFlags bits: 0x{unsupported:X}"
314 );
315}
316
317fn validate_popup_query_flags(caller: &str, flags: PopupFlags) {
318 validate_popup_flags(caller, flags);
319 assert!(
320 !flags.contains(PopupFlags::ANY_POPUP_LEVEL) || flags.contains(PopupFlags::ANY_POPUP_ID),
321 "{caller} requires ANY_POPUP_ID when using ANY_POPUP_LEVEL with a string id"
322 );
323}
324
325bitflags::bitflags! {
326 #[repr(transparent)]
331 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
332 pub struct PopupFlags: i32 {
333 const NONE = sys::ImGuiPopupFlags_None as i32;
335 const NO_REOPEN = sys::ImGuiPopupFlags_NoReopen as i32;
337 const NO_OPEN_OVER_EXISTING_POPUP = sys::ImGuiPopupFlags_NoOpenOverExistingPopup as i32;
339 const NO_OPEN_OVER_ITEMS = sys::ImGuiPopupFlags_NoOpenOverItems as i32;
341 const ANY_POPUP_ID = sys::ImGuiPopupFlags_AnyPopupId as i32;
343 const ANY_POPUP_LEVEL = sys::ImGuiPopupFlags_AnyPopupLevel as i32;
345 const ANY_POPUP = Self::ANY_POPUP_ID.bits() | Self::ANY_POPUP_LEVEL.bits();
347 }
348}
349
350#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
352pub enum PopupContextMouseButton {
353 Left,
355 #[default]
357 Right,
358 Middle,
360}
361
362impl PopupContextMouseButton {
363 #[inline]
364 const fn raw(self) -> i32 {
365 match self {
366 Self::Left => sys::ImGuiPopupFlags_MouseButtonLeft as i32,
367 Self::Right => sys::ImGuiPopupFlags_MouseButtonRight as i32,
368 Self::Middle => sys::ImGuiPopupFlags_MouseButtonMiddle as i32,
369 }
370 }
371}
372
373impl From<MouseButton> for PopupContextMouseButton {
374 fn from(button: MouseButton) -> Self {
375 match button {
376 MouseButton::Left => Self::Left,
377 MouseButton::Right => Self::Right,
378 MouseButton::Middle => Self::Middle,
379 MouseButton::Extra1 | MouseButton::Extra2 => {
380 panic!(
381 "Dear ImGui popup context helpers only support left, right, and middle buttons"
382 )
383 }
384 }
385 }
386}
387
388#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
391pub struct PopupContextOptions {
392 pub flags: PopupFlags,
393 pub mouse_button: PopupContextMouseButton,
394}
395
396impl PopupContextOptions {
397 pub const fn new() -> Self {
398 Self {
399 flags: PopupFlags::NONE,
400 mouse_button: PopupContextMouseButton::Right,
401 }
402 }
403
404 pub fn flags(mut self, flags: PopupFlags) -> Self {
405 self.flags = flags;
406 self
407 }
408
409 pub fn mouse_button(mut self, button: impl Into<PopupContextMouseButton>) -> Self {
410 self.mouse_button = button.into();
411 self
412 }
413
414 pub fn bits(self) -> i32 {
415 self.raw()
416 }
417
418 #[inline]
419 pub(crate) fn raw(self) -> i32 {
420 self.flags.bits() | self.mouse_button.raw()
421 }
422
423 #[inline]
424 pub(crate) fn validate(self, caller: &str) {
425 validate_popup_flags(caller, self.flags);
426 assert!(
427 self.flags.bits() & (sys::ImGuiPopupFlags_MouseButtonMask_ as i32) == 0,
428 "{caller} received non-independent ImGuiPopupFlags mouse-button bits"
429 );
430 }
431}
432
433impl Default for PopupContextOptions {
434 fn default() -> Self {
435 Self::new()
436 }
437}
438
439impl From<PopupFlags> for PopupContextOptions {
440 fn from(flags: PopupFlags) -> Self {
441 Self::new().flags(flags)
442 }
443}
444
445impl From<PopupContextMouseButton> for PopupContextOptions {
446 fn from(button: PopupContextMouseButton) -> Self {
447 Self::new().mouse_button(button)
448 }
449}
450
451impl From<MouseButton> for PopupContextOptions {
452 fn from(button: MouseButton) -> Self {
453 Self::new().mouse_button(button)
454 }
455}
456
457#[derive(Debug)]
459#[must_use]
460pub struct ModalPopup<'ui> {
461 name: &'ui str,
462 opened: Option<&'ui mut bool>,
463 flags: WindowFlags,
464 ui: &'ui Ui,
465}
466
467impl<'ui> ModalPopup<'ui> {
468 pub fn opened(mut self, opened: &'ui mut bool) -> Self {
470 self.opened = Some(opened);
471 self
472 }
473
474 pub fn flags(mut self, flags: WindowFlags) -> Self {
476 self.flags = flags;
477 self
478 }
479
480 pub fn begin(self) -> Option<ModalPopupToken<'ui>> {
482 validate_window_flags("ModalPopup::begin()", self.flags);
483 let name_ptr = self.ui.scratch_txt(self.name);
484 let opened_ptr = self
485 .opened
486 .map(|o| o as *mut bool)
487 .unwrap_or(std::ptr::null_mut());
488
489 let render = unsafe { sys::igBeginPopupModal(name_ptr, opened_ptr, self.flags.bits()) };
490
491 if render {
492 Some(ModalPopupToken::new(self.ui))
493 } else {
494 None
495 }
496 }
497}
498
499#[must_use]
501pub struct PopupToken<'ui> {
502 _ui: &'ui Ui,
503}
504
505impl<'ui> PopupToken<'ui> {
506 fn new(ui: &'ui Ui) -> Self {
508 PopupToken { _ui: ui }
509 }
510
511 pub fn end(self) {
513 }
515}
516
517impl<'ui> Drop for PopupToken<'ui> {
518 fn drop(&mut self) {
519 unsafe {
520 sys::igEndPopup();
521 }
522 }
523}
524
525#[must_use]
527pub struct ModalPopupToken<'ui> {
528 _ui: &'ui Ui,
529}
530
531impl<'ui> ModalPopupToken<'ui> {
532 fn new(ui: &'ui Ui) -> Self {
534 ModalPopupToken { _ui: ui }
535 }
536
537 pub fn end(self) {
539 }
541}
542
543impl<'ui> Drop for ModalPopupToken<'ui> {
544 fn drop(&mut self) {
545 unsafe {
546 sys::igEndPopup();
547 }
548 }
549}