dear_imgui_rs/widget/
menu.rs

1//! Menus and menu bars
2//!
3//! Helpers to build main-menu bars, menu bars within windows, and nested menus
4//! with optional enable/disable states and separators.
5//!
6use crate::sys;
7use crate::ui::Ui;
8
9/// # Menu Widgets
10impl Ui {
11    /// Creates and starts appending to a full-screen menu bar.
12    ///
13    /// Returns `Some(MainMenuBarToken)` if the menu bar is visible. After content has been
14    /// rendered, the token must be ended by calling `.end()`.
15    ///
16    /// Returns `None` if the menu bar is not visible and no content should be rendered.
17    #[must_use]
18    #[doc(alias = "BeginMainMenuBar")]
19    pub fn begin_main_menu_bar(&self) -> Option<MainMenuBarToken<'_>> {
20        if unsafe { sys::igBeginMainMenuBar() } {
21            Some(MainMenuBarToken::new(self))
22        } else {
23            None
24        }
25    }
26
27    /// Creates and starts appending to a menu bar for a window.
28    ///
29    /// Returns `Some(MenuBarToken)` if the menu bar is visible. After content has been
30    /// rendered, the token must be ended by calling `.end()`.
31    ///
32    /// Returns `None` if the menu bar is not visible and no content should be rendered.
33    #[must_use]
34    #[doc(alias = "BeginMenuBar")]
35    pub fn begin_menu_bar(&self) -> Option<MenuBarToken<'_>> {
36        if unsafe { sys::igBeginMenuBar() } {
37            Some(MenuBarToken::new(self))
38        } else {
39            None
40        }
41    }
42
43    /// Creates a menu and starts appending to it.
44    ///
45    /// Returns `Some(MenuToken)` if the menu is open. After content has been
46    /// rendered, the token must be ended by calling `.end()`.
47    ///
48    /// Returns `None` if the menu is not open and no content should be rendered.
49    #[must_use]
50    #[doc(alias = "BeginMenu")]
51    pub fn begin_menu(&self, label: impl AsRef<str>) -> Option<MenuToken<'_>> {
52        self.begin_menu_with_enabled(label, true)
53    }
54
55    /// Creates a menu with enabled state and starts appending to it.
56    ///
57    /// Returns `Some(MenuToken)` if the menu is open. After content has been
58    /// rendered, the token must be ended by calling `.end()`.
59    ///
60    /// Returns `None` if the menu is not open and no content should be rendered.
61    #[must_use]
62    #[doc(alias = "BeginMenu")]
63    pub fn begin_menu_with_enabled(
64        &self,
65        label: impl AsRef<str>,
66        enabled: bool,
67    ) -> Option<MenuToken<'_>> {
68        let label_ptr = self.scratch_txt(label);
69        if unsafe { sys::igBeginMenu(label_ptr, enabled) } {
70            Some(MenuToken::new(self))
71        } else {
72            None
73        }
74    }
75
76    /// Creates a menu and runs a closure to construct the contents.
77    ///
78    /// Note: the closure is not called if the menu is not visible.
79    ///
80    /// This is the equivalent of [menu_with_enabled](Self::menu_with_enabled)
81    /// with `enabled` set to `true`.
82    #[doc(alias = "BeginMenu")]
83    pub fn menu<F: FnOnce()>(&self, label: impl AsRef<str>, f: F) {
84        self.menu_with_enabled(label, true, f);
85    }
86
87    /// Creates a menu and runs a closure to construct the contents.
88    ///
89    /// Note: the closure is not called if the menu is not visible.
90    #[doc(alias = "BeginMenu")]
91    pub fn menu_with_enabled<F: FnOnce()>(&self, label: impl AsRef<str>, enabled: bool, f: F) {
92        if let Some(_menu) = self.begin_menu_with_enabled(label, enabled) {
93            f();
94        }
95    }
96
97    /// Creates a menu item.
98    ///
99    /// Returns true if the menu item is activated.
100    #[doc(alias = "MenuItem")]
101    pub fn menu_item(&self, label: impl AsRef<str>) -> bool {
102        let label_ptr = self.scratch_txt(label);
103        unsafe { sys::igMenuItemEx(label_ptr, std::ptr::null(), std::ptr::null(), false, true) }
104    }
105
106    /// Creates a menu item with a shortcut.
107    ///
108    /// Returns true if the menu item is activated.
109    #[doc(alias = "MenuItem")]
110    pub fn menu_item_with_shortcut(
111        &self,
112        label: impl AsRef<str>,
113        shortcut: impl AsRef<str>,
114    ) -> bool {
115        let (label_ptr, shortcut_ptr) = self.scratch_txt_two(label, shortcut);
116        unsafe { sys::igMenuItemEx(label_ptr, std::ptr::null(), shortcut_ptr, false, true) }
117    }
118
119    /// Creates a menu item with explicit enabled/selected state.
120    /// Returns true if the menu item is activated.
121    #[doc(alias = "MenuItem")]
122    pub fn menu_item_enabled_selected(
123        &self,
124        label: impl AsRef<str>,
125        shortcut: Option<impl AsRef<str>>,
126        selected: bool,
127        enabled: bool,
128    ) -> bool {
129        let label = label.as_ref();
130        match shortcut {
131            Some(shortcut) => {
132                let (label_ptr, shortcut_ptr) = self.scratch_txt_two(label, shortcut.as_ref());
133                unsafe { sys::igMenuItem_Bool(label_ptr, shortcut_ptr, selected, enabled) }
134            }
135            None => {
136                let label_ptr = self.scratch_txt(label);
137                unsafe { sys::igMenuItem_Bool(label_ptr, std::ptr::null(), selected, enabled) }
138            }
139        }
140    }
141
142    /// Creates a menu item with explicit enabled/selected state (no shortcut).
143    ///
144    /// Returns true if the menu item is activated.
145    #[doc(alias = "MenuItem")]
146    pub fn menu_item_enabled_selected_no_shortcut(
147        &self,
148        label: impl AsRef<str>,
149        selected: bool,
150        enabled: bool,
151    ) -> bool {
152        self.menu_item_enabled_selected(label, None::<&str>, selected, enabled)
153    }
154
155    /// Creates a menu item with explicit enabled/selected state and a shortcut.
156    ///
157    /// Returns true if the menu item is activated.
158    #[doc(alias = "MenuItem")]
159    pub fn menu_item_enabled_selected_with_shortcut(
160        &self,
161        label: impl AsRef<str>,
162        shortcut: impl AsRef<str>,
163        selected: bool,
164        enabled: bool,
165    ) -> bool {
166        self.menu_item_enabled_selected(label, Some(shortcut), selected, enabled)
167    }
168
169    /// Creates a toggleable menu item bound to `selected` (updated in place).
170    /// Returns true if the menu item is activated.
171    #[doc(alias = "MenuItem")]
172    pub fn menu_item_toggle(
173        &self,
174        label: impl AsRef<str>,
175        shortcut: Option<impl AsRef<str>>,
176        selected: &mut bool,
177        enabled: bool,
178    ) -> bool {
179        let label = label.as_ref();
180        match shortcut {
181            Some(shortcut) => {
182                let (label_ptr, shortcut_ptr) = self.scratch_txt_two(label, shortcut.as_ref());
183                unsafe { sys::igMenuItem_BoolPtr(label_ptr, shortcut_ptr, selected, enabled) }
184            }
185            None => {
186                let label_ptr = self.scratch_txt(label);
187                unsafe { sys::igMenuItem_BoolPtr(label_ptr, std::ptr::null(), selected, enabled) }
188            }
189        }
190    }
191
192    /// Creates a toggleable menu item bound to `selected` (no shortcut).
193    ///
194    /// Returns true if the menu item is activated.
195    #[doc(alias = "MenuItem")]
196    pub fn menu_item_toggle_no_shortcut(
197        &self,
198        label: impl AsRef<str>,
199        selected: &mut bool,
200        enabled: bool,
201    ) -> bool {
202        self.menu_item_toggle(label, None::<&str>, selected, enabled)
203    }
204
205    /// Creates a toggleable menu item bound to `selected` with a shortcut.
206    ///
207    /// Returns true if the menu item is activated.
208    #[doc(alias = "MenuItem")]
209    pub fn menu_item_toggle_with_shortcut(
210        &self,
211        label: impl AsRef<str>,
212        shortcut: impl AsRef<str>,
213        selected: &mut bool,
214        enabled: bool,
215    ) -> bool {
216        self.menu_item_toggle(label, Some(shortcut), selected, enabled)
217    }
218}
219
220/// Tracks a main menu bar that can be ended by calling `.end()` or by dropping
221#[must_use]
222pub struct MainMenuBarToken<'ui> {
223    ui: &'ui Ui,
224}
225
226impl<'ui> MainMenuBarToken<'ui> {
227    /// Creates a new main menu bar token
228    fn new(ui: &'ui Ui) -> Self {
229        MainMenuBarToken { ui }
230    }
231
232    /// Ends the main menu bar
233    pub fn end(self) {
234        // The drop implementation will handle the actual ending
235    }
236}
237
238impl<'ui> Drop for MainMenuBarToken<'ui> {
239    fn drop(&mut self) {
240        unsafe {
241            sys::igEndMainMenuBar();
242        }
243    }
244}
245
246/// Tracks a menu bar that can be ended by calling `.end()` or by dropping
247#[must_use]
248pub struct MenuBarToken<'ui> {
249    ui: &'ui Ui,
250}
251
252impl<'ui> MenuBarToken<'ui> {
253    /// Creates a new menu bar token
254    fn new(ui: &'ui Ui) -> Self {
255        MenuBarToken { ui }
256    }
257
258    /// Ends the menu bar
259    pub fn end(self) {
260        // The drop implementation will handle the actual ending
261    }
262}
263
264impl<'ui> Drop for MenuBarToken<'ui> {
265    fn drop(&mut self) {
266        unsafe {
267            sys::igEndMenuBar();
268        }
269    }
270}
271
272/// Tracks a menu that can be ended by calling `.end()` or by dropping
273#[must_use]
274pub struct MenuToken<'ui> {
275    ui: &'ui Ui,
276}
277
278impl<'ui> MenuToken<'ui> {
279    /// Creates a new menu token
280    fn new(ui: &'ui Ui) -> Self {
281        MenuToken { ui }
282    }
283
284    /// Ends the menu
285    pub fn end(self) {
286        // The drop implementation will handle the actual ending
287    }
288}
289
290impl<'ui> Drop for MenuToken<'ui> {
291    fn drop(&mut self) {
292        unsafe {
293            sys::igEndMenu();
294        }
295    }
296}