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 = self.scratch_txt(label);
116        let shortcut_ptr = self.scratch_txt(shortcut);
117        unsafe { sys::igMenuItemEx(label_ptr, std::ptr::null(), shortcut_ptr, false, true) }
118    }
119
120    /// Creates a menu item with explicit enabled/selected state.
121    /// Returns true if the menu item is activated.
122    #[doc(alias = "MenuItem")]
123    pub fn menu_item_enabled_selected(
124        &self,
125        label: impl AsRef<str>,
126        shortcut: Option<impl AsRef<str>>,
127        selected: bool,
128        enabled: bool,
129    ) -> bool {
130        let label_ptr = self.scratch_txt(label);
131        let shortcut_ptr = shortcut
132            .as_ref()
133            .map(|s| self.scratch_txt(s.as_ref()))
134            .unwrap_or(std::ptr::null());
135        unsafe { sys::igMenuItem_Bool(label_ptr, shortcut_ptr, selected, enabled) }
136    }
137
138    /// Creates a toggleable menu item bound to `selected` (updated in place).
139    /// Returns true if the menu item is activated.
140    #[doc(alias = "MenuItem")]
141    pub fn menu_item_toggle(
142        &self,
143        label: impl AsRef<str>,
144        shortcut: Option<impl AsRef<str>>,
145        selected: &mut bool,
146        enabled: bool,
147    ) -> bool {
148        let label_ptr = self.scratch_txt(label);
149        let shortcut_ptr = shortcut
150            .as_ref()
151            .map(|s| self.scratch_txt(s.as_ref()))
152            .unwrap_or(std::ptr::null());
153        unsafe { sys::igMenuItem_BoolPtr(label_ptr, shortcut_ptr, selected, enabled) }
154    }
155}
156
157/// Tracks a main menu bar that can be ended by calling `.end()` or by dropping
158#[must_use]
159pub struct MainMenuBarToken<'ui> {
160    ui: &'ui Ui,
161}
162
163impl<'ui> MainMenuBarToken<'ui> {
164    /// Creates a new main menu bar token
165    fn new(ui: &'ui Ui) -> Self {
166        MainMenuBarToken { ui }
167    }
168
169    /// Ends the main menu bar
170    pub fn end(self) {
171        // The drop implementation will handle the actual ending
172    }
173}
174
175impl<'ui> Drop for MainMenuBarToken<'ui> {
176    fn drop(&mut self) {
177        unsafe {
178            sys::igEndMainMenuBar();
179        }
180    }
181}
182
183/// Tracks a menu bar that can be ended by calling `.end()` or by dropping
184#[must_use]
185pub struct MenuBarToken<'ui> {
186    ui: &'ui Ui,
187}
188
189impl<'ui> MenuBarToken<'ui> {
190    /// Creates a new menu bar token
191    fn new(ui: &'ui Ui) -> Self {
192        MenuBarToken { ui }
193    }
194
195    /// Ends the menu bar
196    pub fn end(self) {
197        // The drop implementation will handle the actual ending
198    }
199}
200
201impl<'ui> Drop for MenuBarToken<'ui> {
202    fn drop(&mut self) {
203        unsafe {
204            sys::igEndMenuBar();
205        }
206    }
207}
208
209/// Tracks a menu that can be ended by calling `.end()` or by dropping
210#[must_use]
211pub struct MenuToken<'ui> {
212    ui: &'ui Ui,
213}
214
215impl<'ui> MenuToken<'ui> {
216    /// Creates a new menu token
217    fn new(ui: &'ui Ui) -> Self {
218        MenuToken { ui }
219    }
220
221    /// Ends the menu
222    pub fn end(self) {
223        // The drop implementation will handle the actual ending
224    }
225}
226
227impl<'ui> Drop for MenuToken<'ui> {
228    fn drop(&mut self) {
229        unsafe {
230            sys::igEndMenu();
231        }
232    }
233}