arcdps_imgui/
popups.rs

1use std::ptr;
2
3use crate::sys;
4use crate::window::WindowFlags;
5use crate::Ui;
6
7/// Create a modal pop-up.
8///
9/// # Example
10/// ```rust,no_run
11/// # use arcdps_imgui::*;
12/// # let mut imgui = Context::create();
13/// # let ui = imgui.frame();
14/// if ui.button(im_str!("Show modal")) {
15///     ui.open_popup(im_str!("modal"));
16/// }
17/// if let Some(_token) = PopupModal::new(im_str!("modal")).begin_popup(&ui) {
18///     ui.text("Content of my modal");
19///     if ui.button(im_str!("OK")) {
20///         ui.close_current_popup();
21///     }
22/// };
23/// ```
24#[must_use]
25pub struct PopupModal<'p, Label> {
26    label: Label,
27    opened: Option<&'p mut bool>,
28    flags: WindowFlags,
29}
30
31impl<'p, Label: AsRef<str>> PopupModal<'p, Label> {
32    pub fn new(label: Label) -> Self {
33        PopupModal {
34            label,
35            opened: None,
36            flags: WindowFlags::empty(),
37        }
38    }
39    /// Pass a mutable boolean which will be updated to refer to the current
40    /// "open" state of the modal.
41    pub fn opened(mut self, opened: &'p mut bool) -> Self {
42        self.opened = Some(opened);
43        self
44    }
45    pub fn flags(mut self, flags: WindowFlags) -> Self {
46        self.flags = flags;
47        self
48    }
49    pub fn title_bar(mut self, value: bool) -> Self {
50        self.flags.set(WindowFlags::NO_TITLE_BAR, !value);
51        self
52    }
53    pub fn resizable(mut self, value: bool) -> Self {
54        self.flags.set(WindowFlags::NO_RESIZE, !value);
55        self
56    }
57    pub fn movable(mut self, value: bool) -> Self {
58        self.flags.set(WindowFlags::NO_MOVE, !value);
59        self
60    }
61    pub fn scroll_bar(mut self, value: bool) -> Self {
62        self.flags.set(WindowFlags::NO_SCROLLBAR, !value);
63        self
64    }
65    pub fn scrollable(mut self, value: bool) -> Self {
66        self.flags.set(WindowFlags::NO_SCROLL_WITH_MOUSE, !value);
67        self
68    }
69    pub fn collapsible(mut self, value: bool) -> Self {
70        self.flags.set(WindowFlags::NO_COLLAPSE, !value);
71        self
72    }
73    pub fn always_auto_resize(mut self, value: bool) -> Self {
74        self.flags.set(WindowFlags::ALWAYS_AUTO_RESIZE, value);
75        self
76    }
77    pub fn save_settings(mut self, value: bool) -> Self {
78        self.flags.set(WindowFlags::NO_SAVED_SETTINGS, !value);
79        self
80    }
81    pub fn inputs(mut self, value: bool) -> Self {
82        self.flags.set(WindowFlags::NO_INPUTS, !value);
83        self
84    }
85    pub fn menu_bar(mut self, value: bool) -> Self {
86        self.flags.set(WindowFlags::MENU_BAR, value);
87        self
88    }
89    pub fn horizontal_scrollbar(mut self, value: bool) -> Self {
90        self.flags.set(WindowFlags::HORIZONTAL_SCROLLBAR, value);
91        self
92    }
93    pub fn no_focus_on_appearing(mut self, value: bool) -> Self {
94        self.flags.set(WindowFlags::NO_FOCUS_ON_APPEARING, value);
95        self
96    }
97    pub fn no_bring_to_front_on_focus(mut self, value: bool) -> Self {
98        self.flags
99            .set(WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS, value);
100        self
101    }
102    pub fn always_vertical_scrollbar(mut self, value: bool) -> Self {
103        self.flags
104            .set(WindowFlags::ALWAYS_VERTICAL_SCROLLBAR, value);
105        self
106    }
107    pub fn always_horizontal_scrollbar(mut self, value: bool) -> Self {
108        self.flags
109            .set(WindowFlags::ALWAYS_HORIZONTAL_SCROLLBAR, value);
110        self
111    }
112    pub fn always_use_window_padding(mut self, value: bool) -> Self {
113        self.flags
114            .set(WindowFlags::ALWAYS_USE_WINDOW_PADDING, value);
115        self
116    }
117
118    /// Consume and draw the PopupModal.
119    /// Returns the result of the closure, if it is called.
120    #[doc(alias = "BeginPopupModal")]
121    pub fn build<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
122        self.begin_popup(ui).map(|_popup| f())
123    }
124
125    /// Consume and draw the PopupModal.
126    /// Construct a popup that can have any kind of content.
127    ///
128    /// This should be called *per frame*, whereas [`Ui::open_popup`]
129    /// should be called *once* when you want to actual create the popup.
130    #[doc(alias = "BeginPopupModal")]
131    pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option<PopupToken<'ui>> {
132        let render = unsafe {
133            sys::igBeginPopupModal(
134                ui.scratch_txt(self.label),
135                self.opened
136                    .map(|x| x as *mut bool)
137                    .unwrap_or(ptr::null_mut()),
138                self.flags.bits() as i32,
139            )
140        };
141
142        if render {
143            Some(PopupToken::new(ui))
144        } else {
145            None
146        }
147    }
148}
149
150// Widgets: Popups
151impl<'ui> Ui<'ui> {
152    /// Instructs ImGui to open a popup, which must be began with either [`begin_popup`](Self::begin_popup)
153    /// or [`popup`](Self::popup). You also use this function to begin [PopupModal].
154    ///
155    /// The confusing aspect to popups is that ImGui holds "control" over the popup fundamentally, so that ImGui
156    /// can also force close a popup when a user clicks outside a popup. If you do not want users to be
157    /// able to close a popup without selected an option, use [`PopupModal`].
158    #[doc(alias = "OpenPopup")]
159    pub fn open_popup(&self, str_id: impl AsRef<str>) {
160        unsafe { sys::igOpenPopup(self.scratch_txt(str_id), 0) };
161    }
162
163    /// Construct a popup that can have any kind of content.
164    ///
165    /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
166    /// when you want to actual create the popup.
167    #[doc(alias = "BeginPopup")]
168    pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
169        let render = unsafe {
170            sys::igBeginPopup(self.scratch_txt(str_id), WindowFlags::empty().bits() as i32)
171        };
172
173        if render {
174            Some(PopupToken::new(self))
175        } else {
176            None
177        }
178    }
179
180    /// Construct a popup that can have any kind of content.
181    ///
182    /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
183    /// when you want to actual create the popup.
184    #[doc(alias = "BeginPopup")]
185    pub fn popup<F>(&self, str_id: impl AsRef<str>, f: F)
186    where
187        F: FnOnce(),
188    {
189        if let Some(_t) = self.begin_popup(str_id) {
190            f();
191        }
192    }
193
194    /// Creates a PopupModal directly.
195    pub fn popup_modal<'p, Label: AsRef<str>>(&self, str_id: Label) -> PopupModal<'p, Label> {
196        PopupModal::new(str_id)
197    }
198
199    /// Close a popup. Should be called within the closure given as argument to
200    /// [`Ui::popup`] or [`Ui::popup_modal`].
201    #[doc(alias = "CloseCurrentPopup")]
202    pub fn close_current_popup(&self) {
203        unsafe { sys::igCloseCurrentPopup() };
204    }
205}
206
207create_token!(
208    /// Tracks a popup token that can be ended with `end` or by dropping.
209    pub struct PopupToken<'ui>;
210
211    /// Drops the popup token manually. You can also just allow this token
212    /// to drop on its own.
213    drop { sys::igEndPopup() }
214);