1use std::{cell::RefCell, mem, rc::Rc};
6
7use crate::{
8 dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId,
9 MenuItemKind, NativeIcon,
10};
11
12#[derive(Clone)]
16pub struct Submenu {
17 pub(crate) id: Rc<MenuId>,
18 pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
19}
20
21impl IsMenuItemBase for Submenu {}
22impl IsMenuItem for Submenu {
23 fn kind(&self) -> MenuItemKind {
24 MenuItemKind::Submenu(self.clone())
25 }
26
27 fn id(&self) -> &MenuId {
28 self.id()
29 }
30
31 fn into_id(self) -> MenuId {
32 self.into_id()
33 }
34}
35
36impl Submenu {
37 pub fn new<S: AsRef<str>>(text: S, enabled: bool) -> Self {
42 let submenu = crate::platform_impl::MenuChild::new_submenu(text.as_ref(), enabled, None);
43 Self {
44 id: Rc::new(submenu.id().clone()),
45 inner: Rc::new(RefCell::new(submenu)),
46 }
47 }
48
49 pub fn with_id<I: Into<MenuId>, S: AsRef<str>>(id: I, text: S, enabled: bool) -> Self {
54 let id = id.into();
55
56 Self {
57 id: Rc::new(id.clone()),
58 inner: Rc::new(RefCell::new(crate::platform_impl::MenuChild::new_submenu(
59 text.as_ref(),
60 enabled,
61 Some(id),
62 ))),
63 }
64 }
65
66 pub fn with_items<S: AsRef<str>>(
68 text: S,
69 enabled: bool,
70 items: &[&dyn IsMenuItem],
71 ) -> crate::Result<Self> {
72 let menu = Self::new(text, enabled);
73 menu.append_items(items)?;
74 Ok(menu)
75 }
76
77 pub fn with_id_and_items<I: Into<MenuId>, S: AsRef<str>>(
79 id: I,
80 text: S,
81 enabled: bool,
82 items: &[&dyn IsMenuItem],
83 ) -> crate::Result<Self> {
84 let menu = Self::with_id(id, text, enabled);
85 menu.append_items(items)?;
86 Ok(menu)
87 }
88
89 pub fn id(&self) -> &MenuId {
91 &self.id
92 }
93
94 pub fn append(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
96 self.inner.borrow_mut().add_menu_item(item, AddOp::Append)
97 }
98
99 pub fn append_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
101 for item in items {
102 self.append(*item)?
103 }
104
105 Ok(())
106 }
107
108 pub fn prepend(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
110 self.inner
111 .borrow_mut()
112 .add_menu_item(item, AddOp::Insert(0))
113 }
114
115 pub fn prepend_items(&self, items: &[&dyn IsMenuItem]) -> crate::Result<()> {
119 self.insert_items(items, 0)
120 }
121
122 pub fn insert(&self, item: &dyn IsMenuItem, position: usize) -> crate::Result<()> {
124 self.inner
125 .borrow_mut()
126 .add_menu_item(item, AddOp::Insert(position))
127 }
128
129 pub fn insert_items(&self, items: &[&dyn IsMenuItem], position: usize) -> crate::Result<()> {
131 for (i, item) in items.iter().enumerate() {
132 self.insert(*item, position + i)?
133 }
134
135 Ok(())
136 }
137
138 pub fn remove(&self, item: &dyn IsMenuItem) -> crate::Result<()> {
140 self.inner.borrow_mut().remove(item)
141 }
142
143 pub fn remove_at(&self, position: usize) -> Option<MenuItemKind> {
145 let mut items = self.items();
146 if items.len() > position {
147 let item = items.remove(position);
148 let _ = self.remove(item.as_ref());
149 Some(item)
150 } else {
151 None
152 }
153 }
154
155 pub fn items(&self) -> Vec<MenuItemKind> {
157 self.inner.borrow().items()
158 }
159
160 pub fn text(&self) -> String {
162 self.inner.borrow().text()
163 }
164
165 pub fn set_text<S: AsRef<str>>(&self, text: S) {
169 self.inner.borrow_mut().set_text(text.as_ref())
170 }
171
172 pub fn is_enabled(&self) -> bool {
174 self.inner.borrow().is_enabled()
175 }
176
177 pub fn set_enabled(&self, enabled: bool) {
179 self.inner.borrow_mut().set_enabled(enabled)
180 }
181
182 #[cfg(target_os = "macos")]
187 pub fn set_as_windows_menu_for_nsapp(&self) {
188 self.inner.borrow_mut().set_as_windows_menu_for_nsapp()
189 }
190
191 #[cfg(target_os = "macos")]
198 pub fn set_as_help_menu_for_nsapp(&self) {
199 self.inner.borrow_mut().set_as_help_menu_for_nsapp()
200 }
201
202 pub fn into_id(mut self) -> MenuId {
204 if let Some(id) = Rc::get_mut(&mut self.id) {
206 mem::take(id)
207 } else {
208 self.id().clone()
209 }
210 }
211
212 pub fn set_icon(&self, icon: Option<Icon>) {
214 self.inner.borrow_mut().set_icon(icon)
215 }
216
217 pub fn set_native_icon(&self, _icon: Option<NativeIcon>) {
223 #[cfg(target_os = "macos")]
224 self.inner.borrow_mut().set_native_icon(_icon)
225 }
226}
227
228impl ContextMenu for Submenu {
229 #[cfg(target_os = "windows")]
230 fn hpopupmenu(&self) -> isize {
231 self.inner.borrow().hpopupmenu()
232 }
233
234 #[cfg(target_os = "windows")]
235 unsafe fn show_context_menu_for_hwnd(&self, hwnd: isize, position: Option<Position>) -> bool {
236 self.inner
237 .borrow_mut()
238 .show_context_menu_for_hwnd(hwnd, position)
239 }
240
241 #[cfg(target_os = "windows")]
242 unsafe fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
243 self.inner.borrow().attach_menu_subclass_for_hwnd(hwnd)
244 }
245
246 #[cfg(target_os = "windows")]
247 unsafe fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
248 self.inner.borrow().detach_menu_subclass_from_hwnd(hwnd)
249 }
250
251 #[cfg(all(target_os = "linux", feature = "gtk"))]
252 fn show_context_menu_for_gtk_window(
253 &self,
254 w: >k::Window,
255 position: Option<Position>,
256 ) -> bool {
257 self.inner
258 .borrow_mut()
259 .show_context_menu_for_gtk_window(w, position)
260 }
261
262 #[cfg(all(target_os = "linux", feature = "gtk"))]
263 fn gtk_context_menu(&self) -> gtk::Menu {
264 self.inner.borrow_mut().gtk_context_menu()
265 }
266
267 #[cfg(target_os = "macos")]
268 unsafe fn show_context_menu_for_nsview(
269 &self,
270 view: *const std::ffi::c_void,
271 position: Option<Position>,
272 ) -> bool {
273 self.inner
274 .borrow_mut()
275 .show_context_menu_for_nsview(view, position)
276 }
277
278 #[cfg(target_os = "macos")]
279 fn ns_menu(&self) -> *mut std::ffi::c_void {
280 self.inner.borrow().ns_menu()
281 }
282
283 fn as_submenu(&self) -> Option<&Submenu> {
284 Some(self)
285 }
286}