1use std::ops::Deref;
2
3use pyo3::{marker::Ungil, prelude::*, types::PyString};
4use pyo3_utils::{
5 py_wrapper::{PyWrapper, PyWrapperT0},
6 ungil::UnsafeUngilExt,
7};
8use tauri::menu::{self, ContextMenu as _, IsMenuItem, MenuId};
9
10use crate::{
11 ext_mod::{self, manager_method_impl, ImplManager, PyAppHandleExt as _},
12 tauri_runtime::Runtime,
13 utils::TauriError,
14};
15
16type TauriMenu = menu::Menu<Runtime>;
17type TauriMenuItem = menu::MenuItem<Runtime>;
18type TauriSubmenu = menu::Submenu<Runtime>;
19type TauriPredefinedMenuItem = menu::PredefinedMenuItem<Runtime>;
20type TauriCheckMenuItem = menu::CheckMenuItem<Runtime>;
21type TauriIconMenuItem = menu::IconMenuItem<Runtime>;
22type TauriMenuItemKind = menu::MenuItemKind<Runtime>;
23
24pub type MenuID = PyString;
28pub type MenuEvent = MenuID;
32pub use menu::{HELP_SUBMENU_ID, WINDOW_SUBMENU_ID};
33
34#[derive(FromPyObject, IntoPyObject, IntoPyObjectRef)]
36pub enum MenuItemKind {
37 MenuItem(Py<MenuItem>),
38 Submenu(Py<Submenu>),
39 Predefined(Py<PredefinedMenuItem>),
40 Check(Py<CheckMenuItem>),
41 Icon(Py<IconMenuItem>),
42}
43
44impl MenuItemKind {
45 #[inline]
46 fn delegate_inner_ref<R>(&self, f: impl FnOnce(&dyn IsMenuItem<Runtime>) -> R) -> R {
47 match self {
48 MenuItemKind::MenuItem(v) => f(&*v.get().0.inner_ref()),
49 MenuItemKind::Submenu(v) => f(&*v.get().0.inner_ref()),
50 MenuItemKind::Predefined(v) => f(&*v.get().0.inner_ref()),
51 MenuItemKind::Check(v) => f(&*v.get().0.inner_ref()),
52 MenuItemKind::Icon(v) => f(&*v.get().0.inner_ref()),
53 }
54 }
55}
56
57trait TauriMenuProto {
58 fn append(&self, item: &dyn IsMenuItem<Runtime>) -> tauri::Result<()>;
59 fn prepend(&self, item: &dyn IsMenuItem<Runtime>) -> tauri::Result<()>;
60 fn insert(&self, item: &dyn IsMenuItem<Runtime>, position: usize) -> tauri::Result<()>;
61 fn remove(&self, item: &dyn IsMenuItem<Runtime>) -> tauri::Result<()>;
62}
63
64macro_rules! impl_tauri_menu_proto {
65 ($trait:ty => $($implementor:ty),* => $append:ident, $prepend:ident, $insert:ident, $remove:ident,) => {
66
67 $(
68 impl $trait for $implementor {
69 #[inline]
70 fn $append(&self, item: &dyn IsMenuItem<Runtime>) -> tauri::Result<()> {
71 self.$append(item)
72 }
73
74 #[inline]
75 fn $prepend(&self, item: &dyn IsMenuItem<Runtime>) -> tauri::Result<()> {
76 self.$prepend(item)
77 }
78
79 #[inline]
80 fn $insert(
81 &self,
82 item: &dyn IsMenuItem<Runtime>,
83 position: usize,
84 ) -> tauri::Result<()> {
85 self.$insert(item, position)
86 }
87
88 #[inline]
89 fn $remove(&self, item: &dyn IsMenuItem<Runtime>) -> tauri::Result<()> {
90 self.$remove(item)
91 }
92 }
93 )*
94
95 };
96}
97
98impl_tauri_menu_proto!(TauriMenuProto => TauriMenu, TauriSubmenu => append, prepend, insert, remove,);
99
100impl MenuItemKind {
101 #[inline]
102 fn append_to_menu(&self, menu: &impl TauriMenuProto) -> tauri::Result<()> {
103 self.delegate_inner_ref(|item| menu.append(item))
104 }
105
106 #[inline]
107 fn append_items_to_menu<'a>(
108 items: impl Iterator<Item = &'a Self>,
109 menu: &impl TauriMenuProto,
110 ) -> tauri::Result<()> {
111 for item_kind in items {
112 item_kind.append_to_menu(menu)?;
113 }
114 Ok(())
115 }
116
117 #[inline]
118 fn prepend_to_menu(&self, menu: &impl TauriMenuProto) -> tauri::Result<()> {
119 self.delegate_inner_ref(|item| menu.prepend(item))
120 }
121
122 #[inline]
123 fn prepend_items_to_menu<'a>(
124 items: impl Iterator<Item = &'a Self>,
125 menu: &impl TauriMenuProto,
126 ) -> tauri::Result<()> {
127 for item_kind in items {
128 item_kind.prepend_to_menu(menu)?;
129 }
130 Ok(())
131 }
132
133 #[inline]
134 fn insert_to_menu(&self, menu: &impl TauriMenuProto, position: usize) -> tauri::Result<()> {
135 self.delegate_inner_ref(|item| menu.insert(item, position))
136 }
137
138 #[inline]
139 fn insert_items_to_menu<'a>(
140 items: impl Iterator<Item = &'a Self>,
141 menu: &impl TauriMenuProto,
142 position: usize,
143 ) -> tauri::Result<()> {
144 for (idx, item_kind) in items.enumerate() {
145 item_kind.insert_to_menu(menu, position + idx)?;
146 }
147 Ok(())
148 }
149
150 #[inline]
151 fn remove_from_menu(&self, menu: &impl TauriMenuProto) -> tauri::Result<()> {
152 self.delegate_inner_ref(|item| menu.remove(item))
153 }
154}
155
156impl MenuItemKind {
157 fn from_tauri(py: Python<'_>, menu_kind: TauriMenuItemKind) -> PyResult<Self> {
158 let menu_kind = match menu_kind {
159 TauriMenuItemKind::Submenu(submenu) => {
160 MenuItemKind::Submenu(Submenu::new(submenu).into_pyobject(py)?.unbind())
161 }
162 TauriMenuItemKind::MenuItem(menu_item) => {
163 MenuItemKind::MenuItem(MenuItem::new(menu_item).into_pyobject(py)?.unbind())
164 }
165 TauriMenuItemKind::Check(check_menu_item) => MenuItemKind::Check(
166 CheckMenuItem::new(check_menu_item)
167 .into_pyobject(py)?
168 .unbind(),
169 ),
170 TauriMenuItemKind::Predefined(predefined_menu_item) => MenuItemKind::Predefined(
171 PredefinedMenuItem::new(predefined_menu_item)
172 .into_pyobject(py)?
173 .unbind(),
174 ),
175 TauriMenuItemKind::Icon(icon_menu_item) => MenuItemKind::Icon(
176 IconMenuItem::new(icon_menu_item)
177 .into_pyobject(py)?
178 .unbind(),
179 ),
180 };
181 Ok(menu_kind)
182 }
183}
184
185#[pyclass(frozen)]
187#[non_exhaustive]
188pub struct Menu(pub PyWrapper<PyWrapperT0<TauriMenu>>);
189
190impl Menu {
191 pub(crate) fn new(menu: TauriMenu) -> Self {
192 Self(PyWrapper::new0(menu))
193 }
194
195 #[inline]
196 fn new_impl(
197 py: Python<'_>,
198 manager: &impl tauri::Manager<Runtime>,
199 id: Option<impl Into<menu::MenuId> + Send>,
200 items: Option<Vec<MenuItemKind>>,
201 ) -> PyResult<Self> {
202 unsafe {
203 py.allow_threads_unsend(manager, |manager| {
204 let menu = if let Some(id) = id {
205 TauriMenu::with_id(manager, id)
206 } else {
207 TauriMenu::new(manager)
208 }?;
209
210 if let Some(items) = items {
211 MenuItemKind::append_items_to_menu(items.iter(), &menu)?;
212 }
213 tauri::Result::Ok(Self::new(menu))
214 })
215 }
216 .map_err(TauriError::from)
217 .map_err(PyErr::from)
218 }
219}
220
221#[pymethods]
223impl Menu {
224 #[new]
225 fn __new__(py: Python<'_>, manager: ImplManager) -> PyResult<Self> {
226 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
227 py,
228 manager,
229 None::<&str>,
230 None
231 ))?
232 }
233
234 #[staticmethod]
235 fn with_id(py: Python<'_>, manager: ImplManager, id: String) -> PyResult<Self> {
236 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
237 py,
238 manager,
239 Some(MenuId(id)),
240 None
241 ))?
242 }
243
244 #[staticmethod]
245 fn with_items(
246 py: Python<'_>,
247 manager: ImplManager,
248 items: Vec<MenuItemKind>,
249 ) -> PyResult<Self> {
250 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
251 py,
252 manager,
253 None::<&str>,
254 Some(items)
255 ))?
256 }
257
258 #[staticmethod]
259 fn with_id_and_items(
260 py: Python<'_>,
261 manager: ImplManager,
262 id: String,
263 items: Vec<MenuItemKind>,
264 ) -> PyResult<Self> {
265 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
266 py,
267 manager,
268 Some(MenuId(id)),
269 Some(items)
270 ))?
271 }
272
273 #[staticmethod]
274 fn default(py: Python<'_>, app_handle: Py<ext_mod::AppHandle>) -> PyResult<Self> {
275 py.allow_threads(|| {
276 let app_handle = app_handle.get().0.inner_ref();
277 let menu = TauriMenu::default(app_handle.deref()).map_err(TauriError::from)?;
278 Ok(Menu::new(menu))
279 })
280 }
281
282 fn app_handle(&self, py: Python<'_>) -> Py<ext_mod::AppHandle> {
283 let menu = self.0.inner_ref();
284 let app_handle = menu.app_handle().py_app_handle().clone_ref(py);
286 app_handle
287 }
288
289 fn id<'py>(&self, py: Python<'py>) -> Bound<'py, MenuID> {
290 let menu = self.0.inner_ref();
291 MenuID::intern(py, &menu.id().0)
292 }
293
294 fn append(&self, py: Python<'_>, item: MenuItemKind) -> PyResult<()> {
295 py.allow_threads(|| {
296 let menu = self.0.inner_ref();
297 item.append_to_menu(menu.deref())
298 .map_err(TauriError::from)?;
299 Ok(())
300 })
301 }
302
303 fn append_items(&self, py: Python<'_>, items: Vec<MenuItemKind>) -> PyResult<()> {
304 py.allow_threads(|| {
305 let menu = self.0.inner_ref();
306 MenuItemKind::append_items_to_menu(items.iter(), menu.deref())
307 .map_err(TauriError::from)?;
308 Ok(())
309 })
310 }
311
312 fn prepend(&self, py: Python<'_>, item: MenuItemKind) -> PyResult<()> {
313 py.allow_threads(|| {
314 let menu = self.0.inner_ref();
315 item.prepend_to_menu(menu.deref())
316 .map_err(TauriError::from)?;
317 Ok(())
318 })
319 }
320
321 fn prepend_items(&self, py: Python<'_>, items: Vec<MenuItemKind>) -> PyResult<()> {
322 py.allow_threads(|| {
323 let menu = self.0.inner_ref();
324 MenuItemKind::prepend_items_to_menu(items.iter(), menu.deref())
325 .map_err(TauriError::from)?;
326 Ok(())
327 })
328 }
329
330 fn insert(&self, py: Python<'_>, item: MenuItemKind, position: usize) -> PyResult<()> {
331 py.allow_threads(|| {
332 let menu = self.0.inner_ref();
333 item.insert_to_menu(menu.deref(), position)
334 .map_err(TauriError::from)?;
335 Ok(())
336 })
337 }
338
339 fn insert_items(
340 &self,
341 py: Python<'_>,
342 items: Vec<MenuItemKind>,
343 position: usize,
344 ) -> PyResult<()> {
345 py.allow_threads(|| {
346 let menu = self.0.inner_ref();
347 MenuItemKind::insert_items_to_menu(items.iter(), menu.deref(), position)
348 .map_err(TauriError::from)?;
349 Ok(())
350 })
351 }
352
353 fn remove(&self, py: Python<'_>, item: MenuItemKind) -> PyResult<()> {
354 py.allow_threads(|| {
355 let menu = self.0.inner_ref();
356 item.remove_from_menu(menu.deref())
357 .map_err(TauriError::from)?;
358 Ok(())
359 })
360 }
361
362 fn remove_at(&self, py: Python<'_>, position: usize) -> PyResult<Option<MenuItemKind>> {
363 let item_kind = py.allow_threads(|| {
364 let menu = self.0.inner_ref();
365 menu.remove_at(position)
366 .map_err(TauriError::from)
367 .map_err(PyErr::from)
368 })?;
369
370 let item_kind = match item_kind {
371 Some(item_kind) => Some(MenuItemKind::from_tauri(py, item_kind)?),
372 None => None,
373 };
374
375 Ok(item_kind)
376 }
377
378 fn get(&self, py: Python<'_>, id: &str) -> PyResult<Option<MenuItemKind>> {
379 let item_kind = py.allow_threads(|| {
380 let menu = self.0.inner_ref();
381 menu.get(id)
382 });
383
384 let item_kind = match item_kind {
385 Some(item_kind) => Some(MenuItemKind::from_tauri(py, item_kind)?),
386 None => None,
387 };
388
389 Ok(item_kind)
390 }
391
392 fn items(&self, py: Python<'_>) -> PyResult<Vec<MenuItemKind>> {
393 let items = py.allow_threads(|| {
394 let menu = self.0.inner_ref();
395 menu.items().map_err(TauriError::from)
396 })?;
397
398 let mut vec = Vec::with_capacity(items.len());
399 for items in items {
400 vec.push(MenuItemKind::from_tauri(py, items)?);
401 }
402 Ok(vec)
403 }
404
405 fn set_as_app_menu(&self, py: Python<'_>) -> PyResult<()> {
406 py.allow_threads(|| {
407 let menu = self.0.inner_ref();
408 menu.set_as_app_menu().map_err(TauriError::from)?;
409 Ok(())
410 })
411 }
412
413 fn set_as_window_menu(
414 &self,
415 py: Python<'_>,
416 window: Py<ext_mod::window::Window>,
417 ) -> PyResult<()> {
418 py.allow_threads(|| {
419 let menu = self.0.inner_ref();
420 let window = window.get().0.inner_ref();
421 menu.set_as_window_menu(window.deref())
422 .map_err(TauriError::from)?;
423 Ok(())
424 })
425 }
426}
427
428#[pyclass(frozen)]
430#[non_exhaustive]
431pub struct Submenu(pub PyWrapper<PyWrapperT0<TauriSubmenu>>);
432
433impl Submenu {
434 fn new(menu: TauriSubmenu) -> Self {
435 Self(PyWrapper::new0(menu))
436 }
437
438 #[inline]
439 fn new_impl(
440 py: Python<'_>,
441 manager: &impl tauri::Manager<Runtime>,
442 text: &str,
443 enabled: bool,
444 id: Option<impl Into<menu::MenuId> + Send>,
445 items: Option<Vec<MenuItemKind>>,
446 ) -> PyResult<Self> {
447 unsafe {
448 py.allow_threads_unsend(manager, |manager| {
449 let menu = if let Some(id) = id {
450 TauriSubmenu::with_id(manager, id, text, enabled)
451 } else {
452 TauriSubmenu::new(manager, text, enabled)
453 }?;
454
455 if let Some(items) = items {
456 MenuItemKind::append_items_to_menu(items.iter(), &menu)?;
457 }
458 tauri::Result::Ok(Self::new(menu))
459 })
460 }
461 .map_err(TauriError::from)
462 .map_err(PyErr::from)
463 }
464}
465
466#[pymethods]
468impl Submenu {
469 #[new]
470 fn __new__(py: Python<'_>, manager: ImplManager, text: &str, enabled: bool) -> PyResult<Self> {
471 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
472 py,
473 manager,
474 text,
475 enabled,
476 None::<&str>,
477 None
478 ))?
479 }
480
481 #[staticmethod]
482 fn with_id(
483 py: Python<'_>,
484 manager: ImplManager,
485 id: String,
486 text: &str,
487 enabled: bool,
488 ) -> PyResult<Self> {
489 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
490 py,
491 manager,
492 text,
493 enabled,
494 Some(MenuId(id)),
495 None
496 ))?
497 }
498
499 #[staticmethod]
500 fn with_items(
501 py: Python<'_>,
502 manager: ImplManager,
503 text: &str,
504 enabled: bool,
505 items: Vec<MenuItemKind>,
506 ) -> PyResult<Self> {
507 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
508 py,
509 manager,
510 text,
511 enabled,
512 None::<&str>,
513 Some(items)
514 ))?
515 }
516
517 #[staticmethod]
518 fn with_id_and_items(
519 py: Python<'_>,
520 manager: ImplManager,
521 id: String,
522 text: &str,
523 enabled: bool,
524 items: Vec<MenuItemKind>,
525 ) -> PyResult<Self> {
526 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
527 py,
528 manager,
529 text,
530 enabled,
531 Some(MenuId(id)),
532 Some(items),
533 ))?
534 }
535
536 fn app_handle(&self, py: Python<'_>) -> Py<ext_mod::AppHandle> {
537 let menu = self.0.inner_ref();
538 let app_handle = menu.app_handle().py_app_handle().clone_ref(py);
540 app_handle
541 }
542
543 fn id<'py>(&self, py: Python<'py>) -> Bound<'py, MenuID> {
544 let menu = self.0.inner_ref();
545 MenuID::intern(py, &menu.id().0)
546 }
547
548 fn append(&self, py: Python<'_>, item: MenuItemKind) -> PyResult<()> {
549 py.allow_threads(|| {
550 let menu = self.0.inner_ref();
551 item.append_to_menu(menu.deref())
552 .map_err(TauriError::from)?;
553 Ok(())
554 })
555 }
556
557 fn append_items(&self, py: Python<'_>, items: Vec<MenuItemKind>) -> PyResult<()> {
558 py.allow_threads(|| {
559 let menu = self.0.inner_ref();
560 MenuItemKind::append_items_to_menu(items.iter(), menu.deref())
561 .map_err(TauriError::from)?;
562 Ok(())
563 })
564 }
565
566 fn prepend(&self, py: Python<'_>, item: MenuItemKind) -> PyResult<()> {
567 py.allow_threads(|| {
568 let menu = self.0.inner_ref();
569 item.prepend_to_menu(menu.deref())
570 .map_err(TauriError::from)?;
571 Ok(())
572 })
573 }
574
575 fn prepend_items(&self, py: Python<'_>, items: Vec<MenuItemKind>) -> PyResult<()> {
576 py.allow_threads(|| {
577 let menu = self.0.inner_ref();
578 MenuItemKind::prepend_items_to_menu(items.iter(), menu.deref())
579 .map_err(TauriError::from)?;
580 Ok(())
581 })
582 }
583
584 fn insert(&self, py: Python<'_>, item: MenuItemKind, position: usize) -> PyResult<()> {
585 py.allow_threads(|| {
586 let menu = self.0.inner_ref();
587 item.insert_to_menu(menu.deref(), position)
588 .map_err(TauriError::from)?;
589 Ok(())
590 })
591 }
592
593 fn insert_items(
594 &self,
595 py: Python<'_>,
596 items: Vec<MenuItemKind>,
597 position: usize,
598 ) -> PyResult<()> {
599 py.allow_threads(|| {
600 let menu = self.0.inner_ref();
601 MenuItemKind::insert_items_to_menu(items.iter(), menu.deref(), position)
602 .map_err(TauriError::from)?;
603 Ok(())
604 })
605 }
606
607 fn remove(&self, py: Python<'_>, item: MenuItemKind) -> PyResult<()> {
608 py.allow_threads(|| {
609 let menu = self.0.inner_ref();
610 item.remove_from_menu(menu.deref())
611 .map_err(TauriError::from)?;
612 Ok(())
613 })
614 }
615
616 fn remove_at(&self, py: Python<'_>, position: usize) -> PyResult<Option<MenuItemKind>> {
617 let item_kind = py.allow_threads(|| {
618 let menu = self.0.inner_ref();
619 menu.remove_at(position)
620 .map_err(TauriError::from)
621 .map_err(PyErr::from)
622 })?;
623
624 let item_kind = match item_kind {
625 Some(item_kind) => Some(MenuItemKind::from_tauri(py, item_kind)?),
626 None => None,
627 };
628
629 Ok(item_kind)
630 }
631
632 fn get(&self, py: Python<'_>, id: &str) -> PyResult<Option<MenuItemKind>> {
633 let item_kind = py.allow_threads(|| {
634 let menu = self.0.inner_ref();
635 menu.get(id)
636 });
637
638 let item_kind = match item_kind {
639 Some(item_kind) => Some(MenuItemKind::from_tauri(py, item_kind)?),
640 None => None,
641 };
642
643 Ok(item_kind)
644 }
645
646 fn items(&self, py: Python<'_>) -> PyResult<Vec<MenuItemKind>> {
647 let items = py.allow_threads(|| {
648 let menu = self.0.inner_ref();
649 menu.items().map_err(TauriError::from)
650 })?;
651
652 let mut vec = Vec::with_capacity(items.len());
653 for items in items {
654 vec.push(MenuItemKind::from_tauri(py, items)?);
655 }
656 Ok(vec)
657 }
658
659 fn text(&self, py: Python<'_>) -> PyResult<String> {
660 py.allow_threads(|| {
661 let menu = self.0.inner_ref();
662 let text = menu.text().map_err(TauriError::from)?;
663 Ok(text)
664 })
665 }
666
667 fn set_text(&self, py: Python<'_>, text: &str) -> PyResult<()> {
668 py.allow_threads(|| {
669 let menu = self.0.inner_ref();
670 menu.set_text(text).map_err(TauriError::from)?;
671 Ok(())
672 })
673 }
674
675 fn is_enabled(&self, py: Python<'_>) -> PyResult<bool> {
676 py.allow_threads(|| {
677 let menu = self.0.inner_ref();
678 let enabled = menu.is_enabled().map_err(TauriError::from)?;
679 Ok(enabled)
680 })
681 }
682
683 fn set_enabled(&self, py: Python<'_>, enabled: bool) -> PyResult<()> {
684 py.allow_threads(|| {
685 let menu = self.0.inner_ref();
686 menu.set_enabled(enabled).map_err(TauriError::from)?;
687 Ok(())
688 })
689 }
690}
691
692#[pyclass(frozen)]
694#[non_exhaustive]
695pub struct MenuItem(pub PyWrapper<PyWrapperT0<TauriMenuItem>>);
696
697impl MenuItem {
698 fn new(menu: TauriMenuItem) -> Self {
699 Self(PyWrapper::new0(menu))
700 }
701
702 #[inline]
703 fn new_impl(
704 py: Python<'_>,
705 manager: &impl tauri::Manager<Runtime>,
706 text: &str,
707 enabled: bool,
708 accelerator: Option<&str>,
709 id: Option<impl Into<menu::MenuId> + Send>,
710 ) -> PyResult<Self> {
711 unsafe {
712 py.allow_threads_unsend(manager, |manager| {
713 let menu = if let Some(id) = id {
714 TauriMenuItem::with_id(manager, id, text, enabled, accelerator)
715 } else {
716 TauriMenuItem::new(manager, text, enabled, accelerator)
717 }?;
718
719 tauri::Result::Ok(Self::new(menu))
720 })
721 }
722 .map_err(TauriError::from)
723 .map_err(PyErr::from)
724 }
725}
726
727#[pymethods]
728impl MenuItem {
729 #[new]
730 #[pyo3(signature = (manager, text, enabled, accelerator=None))]
731 fn __new__(
732 py: Python<'_>,
733 manager: ImplManager,
734 text: &str,
735 enabled: bool,
736 accelerator: Option<&str>,
737 ) -> PyResult<Self> {
738 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
739 py,
740 manager,
741 text,
742 enabled,
743 accelerator,
744 None::<&str>
745 ))?
746 }
747
748 #[staticmethod]
749 #[pyo3(signature = (manager, id, text, enabled, accelerator=None))]
750 fn with_id(
751 py: Python<'_>,
752 manager: ImplManager,
753 id: String,
754 text: &str,
755 enabled: bool,
756 accelerator: Option<&str>,
757 ) -> PyResult<Self> {
758 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
759 py,
760 manager,
761 text,
762 enabled,
763 accelerator,
764 Some(MenuId(id)),
765 ))?
766 }
767
768 fn app_handle(&self, py: Python<'_>) -> Py<ext_mod::AppHandle> {
769 let menu = self.0.inner_ref();
770 let app_handle = menu.app_handle().py_app_handle().clone_ref(py);
772 app_handle
773 }
774
775 fn id<'py>(&self, py: Python<'py>) -> Bound<'py, MenuID> {
776 let menu = self.0.inner_ref();
777 MenuID::intern(py, &menu.id().0)
778 }
779
780 fn text(&self, py: Python<'_>) -> PyResult<String> {
781 py.allow_threads(|| {
782 let menu = self.0.inner_ref();
783 let text = menu.text().map_err(TauriError::from)?;
784 Ok(text)
785 })
786 }
787
788 fn set_text(&self, py: Python<'_>, text: &str) -> PyResult<()> {
789 py.allow_threads(|| {
790 let menu = self.0.inner_ref();
791 menu.set_text(text).map_err(TauriError::from)?;
792 Ok(())
793 })
794 }
795
796 fn is_enabled(&self, py: Python<'_>) -> PyResult<bool> {
797 py.allow_threads(|| {
798 let menu = self.0.inner_ref();
799 let enabled = menu.is_enabled().map_err(TauriError::from)?;
800 Ok(enabled)
801 })
802 }
803
804 fn set_enabled(&self, py: Python<'_>, enabled: bool) -> PyResult<()> {
805 py.allow_threads(|| {
806 let menu = self.0.inner_ref();
807 menu.set_enabled(enabled).map_err(TauriError::from)?;
808 Ok(())
809 })
810 }
811
812 #[pyo3(signature = (accelerator))]
813 fn set_accelerator(&self, py: Python<'_>, accelerator: Option<&str>) -> PyResult<()> {
814 py.allow_threads(|| {
815 let menu = self.0.inner_ref();
816 menu.set_accelerator(accelerator)
817 .map_err(TauriError::from)?;
818 Ok(())
819 })
820 }
821}
822
823#[pyclass(frozen)]
825#[non_exhaustive]
826pub struct PredefinedMenuItem(pub PyWrapper<PyWrapperT0<TauriPredefinedMenuItem>>);
827
828impl PredefinedMenuItem {
829 fn new(menu: TauriPredefinedMenuItem) -> Self {
830 Self(PyWrapper::new0(menu))
831 }
832
833 #[inline]
834 fn delegate_inner<M, F>(py: Python<'_>, manager: &M, func: F) -> PyResult<Self>
835 where
836 M: tauri::Manager<Runtime>,
837 F: FnOnce(&M) -> tauri::Result<TauriPredefinedMenuItem> + Ungil + Send,
838 {
839 unsafe {
840 py.allow_threads_unsend(manager, |manager| {
841 let menu = func(manager)?;
842 tauri::Result::Ok(Self::new(menu))
843 })
844 }
845 .map_err(TauriError::from)
846 .map_err(PyErr::from)
847 }
848}
849
850#[pymethods]
851impl PredefinedMenuItem {
852 #[staticmethod]
853 fn separator(py: Python<'_>, manager: ImplManager) -> PyResult<Self> {
854 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
855 py,
856 manager,
857 move |manager| { TauriPredefinedMenuItem::separator(manager) }
858 ))?
859 }
860
861 #[staticmethod]
862 #[pyo3(signature = (manager, text=None))]
863 fn copy(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
864 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
865 py,
866 manager,
867 move |manager| { TauriPredefinedMenuItem::copy(manager, text) }
868 ))?
869 }
870
871 #[staticmethod]
872 #[pyo3(signature = (manager, text=None))]
873 fn cut(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
874 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
875 py,
876 manager,
877 move |manager| { TauriPredefinedMenuItem::cut(manager, text) }
878 ))?
879 }
880
881 #[staticmethod]
882 #[pyo3(signature = (manager, text=None))]
883 fn paste(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
884 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
885 py,
886 manager,
887 move |manager| { TauriPredefinedMenuItem::paste(manager, text) }
888 ))?
889 }
890
891 #[staticmethod]
892 #[pyo3(signature = (manager, text=None))]
893 fn select_all(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
894 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
895 py,
896 manager,
897 move |manager| { TauriPredefinedMenuItem::select_all(manager, text) }
898 ))?
899 }
900
901 #[staticmethod]
902 #[pyo3(signature = (manager, text=None))]
903 fn undo(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
904 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
905 py,
906 manager,
907 move |manager| { TauriPredefinedMenuItem::undo(manager, text) }
908 ))?
909 }
910
911 #[staticmethod]
912 #[pyo3(signature = (manager, text=None))]
913 fn redo(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
914 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
915 py,
916 manager,
917 move |manager| { TauriPredefinedMenuItem::redo(manager, text) }
918 ))?
919 }
920
921 #[staticmethod]
922 #[pyo3(signature = (manager, text=None))]
923 fn minimize(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
924 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
925 py,
926 manager,
927 move |manager| { TauriPredefinedMenuItem::minimize(manager, text) }
928 ))?
929 }
930
931 #[staticmethod]
932 #[pyo3(signature = (manager, text=None))]
933 fn maximize(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
934 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
935 py,
936 manager,
937 move |manager| { TauriPredefinedMenuItem::maximize(manager, text) }
938 ))?
939 }
940
941 #[staticmethod]
942 #[pyo3(signature = (manager, text=None))]
943 fn fullscreen(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
944 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
945 py,
946 manager,
947 move |manager| { TauriPredefinedMenuItem::fullscreen(manager, text) }
948 ))?
949 }
950
951 #[staticmethod]
952 #[pyo3(signature = (manager, text=None))]
953 fn hide(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
954 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
955 py,
956 manager,
957 move |manager| { TauriPredefinedMenuItem::hide(manager, text) }
958 ))?
959 }
960
961 #[staticmethod]
962 #[pyo3(signature = (manager, text=None))]
963 fn hide_others(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
964 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
965 py,
966 manager,
967 move |manager| { TauriPredefinedMenuItem::hide_others(manager, text) }
968 ))?
969 }
970
971 #[staticmethod]
972 #[pyo3(signature = (manager, text=None))]
973 fn show_all(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
974 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
975 py,
976 manager,
977 move |manager| { TauriPredefinedMenuItem::show_all(manager, text) }
978 ))?
979 }
980
981 #[staticmethod]
982 #[pyo3(signature = (manager, text=None))]
983 fn close_window(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
984 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
985 py,
986 manager,
987 move |manager| { TauriPredefinedMenuItem::close_window(manager, text) }
988 ))?
989 }
990
991 #[staticmethod]
992 #[pyo3(signature = (manager, text=None))]
993 fn quit(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
994 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
995 py,
996 manager,
997 move |manager| { TauriPredefinedMenuItem::quit(manager, text) }
998 ))?
999 }
1000
1001 #[staticmethod]
1002 #[pyo3(signature = (manager, text=None, metadata=None))]
1003 fn about(
1004 py: Python<'_>,
1005 manager: ImplManager,
1006 text: Option<&str>,
1007 metadata: Option<Py<AboutMetadata>>,
1008 ) -> PyResult<Self> {
1009 let metadata = match metadata {
1010 Some(ref metadata) => Some(metadata.get().to_tauri(py)?),
1011 None => None,
1012 };
1013
1014 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
1015 py,
1016 manager,
1017 move |manager| { TauriPredefinedMenuItem::about(manager, text, metadata) }
1018 ))?
1019 }
1020
1021 #[staticmethod]
1022 #[pyo3(signature = (manager, text=None))]
1023 fn services(py: Python<'_>, manager: ImplManager, text: Option<&str>) -> PyResult<Self> {
1024 manager_method_impl!(py, &manager, |py, manager| Self::delegate_inner(
1025 py,
1026 manager,
1027 move |manager| { TauriPredefinedMenuItem::services(manager, text) }
1028 ))?
1029 }
1030
1031 fn app_handle(&self, py: Python<'_>) -> Py<ext_mod::AppHandle> {
1032 let menu = self.0.inner_ref();
1033 let app_handle = menu.app_handle().py_app_handle().clone_ref(py);
1035 app_handle
1036 }
1037
1038 fn id<'py>(&self, py: Python<'py>) -> Bound<'py, MenuID> {
1039 let menu = self.0.inner_ref();
1040 MenuID::intern(py, &menu.id().0)
1041 }
1042
1043 fn text(&self, py: Python<'_>) -> PyResult<String> {
1044 py.allow_threads(|| {
1045 let menu = self.0.inner_ref();
1046 let text = menu.text().map_err(TauriError::from)?;
1047 Ok(text)
1048 })
1049 }
1050
1051 fn set_text(&self, py: Python<'_>, text: &str) -> PyResult<()> {
1052 py.allow_threads(|| {
1053 let menu = self.0.inner_ref();
1054 menu.set_text(text).map_err(TauriError::from)?;
1055 Ok(())
1056 })
1057 }
1058}
1059
1060#[pyclass(frozen)]
1062#[non_exhaustive]
1063pub struct CheckMenuItem(pub PyWrapper<PyWrapperT0<TauriCheckMenuItem>>);
1064
1065impl CheckMenuItem {
1066 fn new(menu: TauriCheckMenuItem) -> Self {
1067 Self(PyWrapper::new0(menu))
1068 }
1069
1070 #[inline]
1071 fn new_impl(
1072 py: Python<'_>,
1073 manager: &impl tauri::Manager<Runtime>,
1074 text: &str,
1075 enabled: bool,
1076 checked: bool,
1077 accelerator: Option<&str>,
1078 id: Option<impl Into<menu::MenuId> + Send>,
1079 ) -> PyResult<Self> {
1080 unsafe {
1081 py.allow_threads_unsend(manager, |manager| {
1082 let menu = if let Some(id) = id {
1083 TauriCheckMenuItem::with_id(manager, id, text, enabled, checked, accelerator)
1084 } else {
1085 TauriCheckMenuItem::new(manager, text, enabled, checked, accelerator)
1086 }?;
1087
1088 tauri::Result::Ok(Self::new(menu))
1089 })
1090 }
1091 .map_err(TauriError::from)
1092 .map_err(PyErr::from)
1093 }
1094}
1095
1096#[pymethods]
1097impl CheckMenuItem {
1098 #[new]
1099 #[pyo3(signature = (manager, text, enabled, checked, accelerator=None))]
1100 fn __new__(
1101 py: Python<'_>,
1102 manager: ImplManager,
1103 text: &str,
1104 enabled: bool,
1105 checked: bool,
1106 accelerator: Option<&str>,
1107 ) -> PyResult<Self> {
1108 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
1109 py,
1110 manager,
1111 text,
1112 enabled,
1113 checked,
1114 accelerator,
1115 None::<&str>,
1116 ))?
1117 }
1118
1119 #[staticmethod]
1120 #[pyo3(signature = (manager, id, text, enabled, checked, accelerator=None))]
1121 fn with_id(
1122 py: Python<'_>,
1123 manager: ImplManager,
1124 id: String,
1125 text: &str,
1126 enabled: bool,
1127 checked: bool,
1128 accelerator: Option<&str>,
1129 ) -> PyResult<Self> {
1130 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
1131 py,
1132 manager,
1133 text,
1134 enabled,
1135 checked,
1136 accelerator,
1137 Some(MenuId(id)),
1138 ))?
1139 }
1140
1141 fn app_handle(&self, py: Python<'_>) -> Py<ext_mod::AppHandle> {
1142 let menu = self.0.inner_ref();
1143 let app_handle = menu.app_handle().py_app_handle().clone_ref(py);
1145 app_handle
1146 }
1147
1148 fn id<'py>(&self, py: Python<'py>) -> Bound<'py, MenuID> {
1149 let menu = self.0.inner_ref();
1150 MenuID::intern(py, &menu.id().0)
1151 }
1152
1153 fn text(&self, py: Python<'_>) -> PyResult<String> {
1154 py.allow_threads(|| {
1155 let menu = self.0.inner_ref();
1156 let text = menu.text().map_err(TauriError::from)?;
1157 Ok(text)
1158 })
1159 }
1160
1161 fn set_text(&self, py: Python<'_>, text: &str) -> PyResult<()> {
1162 py.allow_threads(|| {
1163 let menu = self.0.inner_ref();
1164 menu.set_text(text).map_err(TauriError::from)?;
1165 Ok(())
1166 })
1167 }
1168
1169 fn is_enabled(&self, py: Python<'_>) -> PyResult<bool> {
1170 py.allow_threads(|| {
1171 let menu = self.0.inner_ref();
1172 let enabled = menu.is_enabled().map_err(TauriError::from)?;
1173 Ok(enabled)
1174 })
1175 }
1176
1177 fn set_enabled(&self, py: Python<'_>, enabled: bool) -> PyResult<()> {
1178 py.allow_threads(|| {
1179 let menu = self.0.inner_ref();
1180 menu.set_enabled(enabled).map_err(TauriError::from)?;
1181 Ok(())
1182 })
1183 }
1184
1185 #[pyo3(signature = (accelerator))]
1186 fn set_accelerator(&self, py: Python<'_>, accelerator: Option<&str>) -> PyResult<()> {
1187 py.allow_threads(|| {
1188 let menu = self.0.inner_ref();
1189 menu.set_accelerator(accelerator)
1190 .map_err(TauriError::from)?;
1191 Ok(())
1192 })
1193 }
1194
1195 fn is_checked(&self, py: Python<'_>) -> PyResult<bool> {
1196 py.allow_threads(|| {
1197 let menu = self.0.inner_ref();
1198 let checked = menu.is_checked().map_err(TauriError::from)?;
1199 Ok(checked)
1200 })
1201 }
1202
1203 fn set_checked(&self, py: Python<'_>, checked: bool) -> PyResult<()> {
1204 py.allow_threads(|| {
1205 let menu = self.0.inner_ref();
1206 menu.set_checked(checked).map_err(TauriError::from)?;
1207 Ok(())
1208 })
1209 }
1210}
1211
1212trait PyStrToRs {
1213 type Output;
1214 fn to_rs(&self, py: Python<'_>) -> Self::Output;
1215}
1216
1217impl PyStrToRs for Py<PyString> {
1218 type Output = PyResult<String>;
1219 fn to_rs(&self, py: Python<'_>) -> Self::Output {
1220 Ok(self.to_cow(py)?.into_owned())
1222 }
1223}
1224
1225impl PyStrToRs for Option<Py<PyString>> {
1226 type Output = PyResult<Option<String>>;
1227 fn to_rs(&self, py: Python<'_>) -> Self::Output {
1228 match self {
1229 Some(py_str) => Ok(Some(py_str.to_rs(py)?)),
1230 None => Ok(None),
1231 }
1232 }
1233}
1234
1235impl PyStrToRs for Option<Vec<Py<PyString>>> {
1236 type Output = PyResult<Option<Vec<String>>>;
1237 fn to_rs(&self, py: Python<'_>) -> Self::Output {
1238 match self {
1239 Some(py_str_vec) => {
1240 let mut vec = Vec::with_capacity(py_str_vec.len());
1241 for py_str in py_str_vec {
1242 vec.push(py_str.to_rs(py)?);
1243 }
1244 Ok(Some(vec))
1245 }
1246 None => Ok(None),
1247 }
1248 }
1249}
1250
1251#[pyclass(frozen)]
1253#[non_exhaustive]
1254pub struct AboutMetadata {
1255 name: Option<Py<PyString>>,
1256 version: Option<Py<PyString>>,
1257 short_version: Option<Py<PyString>>,
1258 authors: Option<Vec<Py<PyString>>>,
1259 comments: Option<Py<PyString>>,
1260 copyright: Option<Py<PyString>>,
1261 license: Option<Py<PyString>>,
1262 website: Option<Py<PyString>>,
1263 website_label: Option<Py<PyString>>,
1264 credits: Option<Py<PyString>>,
1265 icon: Option<Py<ext_mod::image::Image>>,
1266}
1267
1268impl AboutMetadata {
1269 fn to_tauri<'a>(&'a self, py: Python<'_>) -> PyResult<menu::AboutMetadata<'a>> {
1270 let about_metadata = menu::AboutMetadata {
1271 name: self.name.to_rs(py)?,
1272 version: self.version.to_rs(py)?,
1273 short_version: self.short_version.to_rs(py)?,
1274 authors: self.authors.to_rs(py)?,
1275 comments: self.comments.to_rs(py)?,
1276 copyright: self.copyright.to_rs(py)?,
1277 license: self.license.to_rs(py)?,
1278 website: self.website.to_rs(py)?,
1279 website_label: self.website_label.to_rs(py)?,
1280 credits: self.credits.to_rs(py)?,
1281 icon: self.icon.as_ref().map(|icon| icon.get().to_tauri(py)),
1282 };
1283 Ok(about_metadata)
1284 }
1285}
1286
1287#[pymethods]
1288impl AboutMetadata {
1289 #[new]
1290 #[pyo3(signature = (
1291 *,
1292 name=None,
1293 version=None,
1294 short_version=None,
1295 authors=None,
1296 comments=None,
1297 copyright=None,
1298 license=None,
1299 website=None,
1300 website_label=None,
1301 credits=None,
1302 icon=None
1303 ))]
1304 #[expect(clippy::too_many_arguments)]
1305 const fn __new__(
1306 name: Option<Py<PyString>>,
1307 version: Option<Py<PyString>>,
1308 short_version: Option<Py<PyString>>,
1309 authors: Option<Vec<Py<PyString>>>,
1310 comments: Option<Py<PyString>>,
1311 copyright: Option<Py<PyString>>,
1312 license: Option<Py<PyString>>,
1313 website: Option<Py<PyString>>,
1314 website_label: Option<Py<PyString>>,
1315 credits: Option<Py<PyString>>,
1316 icon: Option<Py<ext_mod::image::Image>>,
1317 ) -> Self {
1318 Self {
1319 name,
1320 version,
1321 short_version,
1322 authors,
1323 comments,
1324 copyright,
1325 license,
1326 website,
1327 website_label,
1328 credits,
1329 icon,
1330 }
1331 }
1332}
1333
1334enum IconOrNative<'a> {
1335 Icon(Option<tauri::image::Image<'a>>),
1336 Native(Option<menu::NativeIcon>),
1337}
1338
1339#[pyclass(frozen)]
1341#[non_exhaustive]
1342pub struct IconMenuItem(pub PyWrapper<PyWrapperT0<TauriIconMenuItem>>);
1343
1344impl IconMenuItem {
1345 fn new(menu: TauriIconMenuItem) -> Self {
1346 Self(PyWrapper::new0(menu))
1347 }
1348
1349 #[inline]
1350 fn new_impl(
1351 py: Python<'_>,
1352 manager: &impl tauri::Manager<Runtime>,
1353 text: &str,
1354 enabled: bool,
1355 icon_or_native: IconOrNative<'_>,
1356 accelerator: Option<&str>,
1357 id: Option<impl Into<menu::MenuId> + Send>,
1358 ) -> PyResult<Self> {
1359 unsafe {
1360 py.allow_threads_unsend(manager, |manager| {
1361 let menu = if let Some(id) = id {
1362 match icon_or_native {
1363 IconOrNative::Icon(icon) => TauriIconMenuItem::with_id(
1364 manager,
1365 id,
1366 text,
1367 enabled,
1368 icon,
1369 accelerator,
1370 ),
1371 IconOrNative::Native(native_icon) => {
1372 TauriIconMenuItem::with_id_and_native_icon(
1373 manager,
1374 id,
1375 text,
1376 enabled,
1377 native_icon,
1378 accelerator,
1379 )
1380 }
1381 }
1382 } else {
1383 match icon_or_native {
1384 IconOrNative::Icon(icon) => {
1385 TauriIconMenuItem::new(manager, text, enabled, icon, accelerator)
1386 }
1387 IconOrNative::Native(native_icon) => TauriIconMenuItem::with_native_icon(
1388 manager,
1389 text,
1390 enabled,
1391 native_icon,
1392 accelerator,
1393 ),
1394 }
1395 }?;
1396
1397 tauri::Result::Ok(Self::new(menu))
1398 })
1399 }
1400 .map_err(TauriError::from)
1401 .map_err(PyErr::from)
1402 }
1403}
1404
1405#[pymethods]
1406impl IconMenuItem {
1407 #[new]
1408 #[pyo3(signature = (manager, text, enabled, icon=None, accelerator=None))]
1409 fn __new__(
1410 py: Python<'_>,
1411 manager: ImplManager,
1412 text: &str,
1413 enabled: bool,
1414 icon: Option<Py<ext_mod::image::Image>>,
1415 accelerator: Option<&str>,
1416 ) -> PyResult<Self> {
1417 let icon = icon.as_ref().map(|icon| icon.get().to_tauri(py));
1418 let icon = IconOrNative::Icon(icon);
1419
1420 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
1421 py,
1422 manager,
1423 text,
1424 enabled,
1425 icon,
1426 accelerator,
1427 None::<&str>,
1428 ))?
1429 }
1430
1431 #[staticmethod]
1432 #[pyo3(signature = (manager, id, text, enabled, icon=None, accelerator=None))]
1433 fn with_id(
1434 py: Python<'_>,
1435 manager: ImplManager,
1436 id: String,
1437 text: &str,
1438 enabled: bool,
1439 icon: Option<Py<ext_mod::image::Image>>,
1440 accelerator: Option<&str>,
1441 ) -> PyResult<Self> {
1442 let icon = icon.as_ref().map(|icon| icon.get().to_tauri(py));
1443 let icon = IconOrNative::Icon(icon);
1444
1445 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
1446 py,
1447 manager,
1448 text,
1449 enabled,
1450 icon,
1451 accelerator,
1452 Some(MenuId(id)),
1453 ))?
1454 }
1455
1456 #[staticmethod]
1457 #[pyo3(signature = (manager, text, enabled, native_icon=None, accelerator=None))]
1458 fn with_native_icon(
1459 py: Python<'_>,
1460 manager: ImplManager,
1461 text: &str,
1462 enabled: bool,
1463 native_icon: Option<NativeIcon>,
1464 accelerator: Option<&str>,
1465 ) -> PyResult<Self> {
1466 let native_icon = native_icon.map(|native_icon| native_icon.into());
1467 let native_icon = IconOrNative::Native(native_icon);
1468
1469 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
1470 py,
1471 manager,
1472 text,
1473 enabled,
1474 native_icon,
1475 accelerator,
1476 None::<&str>,
1477 ))?
1478 }
1479
1480 #[staticmethod]
1481 #[pyo3(signature = (manager, id, text, enabled, native_icon=None, accelerator=None))]
1482 fn with_id_and_native_icon(
1483 py: Python<'_>,
1484 manager: ImplManager,
1485 id: String,
1486 text: &str,
1487 enabled: bool,
1488 native_icon: Option<NativeIcon>,
1489 accelerator: Option<&str>,
1490 ) -> PyResult<Self> {
1491 let native_icon = native_icon.map(|native_icon| native_icon.into());
1492 let native_icon = IconOrNative::Native(native_icon);
1493
1494 manager_method_impl!(py, &manager, |py, manager| Self::new_impl(
1495 py,
1496 manager,
1497 text,
1498 enabled,
1499 native_icon,
1500 accelerator,
1501 Some(MenuId(id)),
1502 ))?
1503 }
1504
1505 fn app_handle(&self, py: Python<'_>) -> Py<ext_mod::AppHandle> {
1506 let menu = self.0.inner_ref();
1507 let app_handle = menu.app_handle().py_app_handle().clone_ref(py);
1509 app_handle
1510 }
1511
1512 fn id<'py>(&self, py: Python<'py>) -> Bound<'py, MenuID> {
1513 let menu = self.0.inner_ref();
1514 MenuID::intern(py, &menu.id().0)
1515 }
1516
1517 fn text(&self, py: Python<'_>) -> PyResult<String> {
1518 py.allow_threads(|| {
1519 let menu = self.0.inner_ref();
1520 let text = menu.text().map_err(TauriError::from)?;
1521 Ok(text)
1522 })
1523 }
1524
1525 fn set_text(&self, py: Python<'_>, text: &str) -> PyResult<()> {
1526 py.allow_threads(|| {
1527 let menu = self.0.inner_ref();
1528 menu.set_text(text).map_err(TauriError::from)?;
1529 Ok(())
1530 })
1531 }
1532
1533 fn is_enabled(&self, py: Python<'_>) -> PyResult<bool> {
1534 py.allow_threads(|| {
1535 let menu = self.0.inner_ref();
1536 let enabled = menu.is_enabled().map_err(TauriError::from)?;
1537 Ok(enabled)
1538 })
1539 }
1540
1541 fn set_enabled(&self, py: Python<'_>, enabled: bool) -> PyResult<()> {
1542 py.allow_threads(|| {
1543 let menu = self.0.inner_ref();
1544 menu.set_enabled(enabled).map_err(TauriError::from)?;
1545 Ok(())
1546 })
1547 }
1548
1549 #[pyo3(signature = (accelerator))]
1550 fn set_accelerator(&self, py: Python<'_>, accelerator: Option<&str>) -> PyResult<()> {
1551 py.allow_threads(|| {
1552 let menu = self.0.inner_ref();
1553 menu.set_accelerator(accelerator)
1554 .map_err(TauriError::from)?;
1555 Ok(())
1556 })
1557 }
1558
1559 #[pyo3(signature = (icon))]
1560 fn set_icon(&self, py: Python<'_>, icon: Option<Py<ext_mod::image::Image>>) -> PyResult<()> {
1561 let icon = icon.as_ref().map(|icon| icon.get().to_tauri(py));
1562 py.allow_threads(|| {
1563 let menu = self.0.inner_ref();
1564 menu.set_icon(icon).map_err(TauriError::from)?;
1565 Ok(())
1566 })
1567 }
1568
1569 #[pyo3(signature = (native_icon))]
1570 fn set_native_icon(&self, py: Python<'_>, native_icon: Option<NativeIcon>) -> PyResult<()> {
1571 let native_icon = native_icon.map(|native_icon| native_icon.into());
1572 py.allow_threads(|| {
1573 let menu = self.0.inner_ref();
1574 menu.set_native_icon(native_icon)
1575 .map_err(TauriError::from)?;
1576 Ok(())
1577 })
1578 }
1579}
1580
1581macro_rules! native_icon_impl {
1582 ($ident:ident => : $($variant:ident),*) => {
1583 #[pyclass(frozen, eq, eq_int)]
1585 #[derive(PartialEq, Clone, Copy)]
1586 pub enum $ident {
1587 $($variant,)*
1588 }
1589
1590 impl From<$ident> for tauri::menu::NativeIcon {
1591 fn from(icon: $ident) -> Self {
1592 match icon {
1593 $($ident::$variant => tauri::menu::NativeIcon::$variant,)*
1594 }
1595 }
1596 }
1597
1598 impl From<tauri::menu::NativeIcon> for $ident {
1599 fn from(icon: tauri::menu::NativeIcon) -> Self {
1600 match icon {
1601 $(tauri::menu::NativeIcon::$variant => $ident::$variant,)*
1602 }
1603 }
1604 }
1605
1606 };
1607}
1608
1609native_icon_impl!(
1610 NativeIcon => :
1611 Add,
1612 Advanced,
1613 Bluetooth,
1614 Bookmarks,
1615 Caution,
1616 ColorPanel,
1617 ColumnView,
1618 Computer,
1619 EnterFullScreen,
1620 Everyone,
1621 ExitFullScreen,
1622 FlowView,
1623 Folder,
1624 FolderBurnable,
1625 FolderSmart,
1626 FollowLinkFreestanding,
1627 FontPanel,
1628 GoLeft,
1629 GoRight,
1630 Home,
1631 IChatTheater,
1632 IconView,
1633 Info,
1634 InvalidDataFreestanding,
1635 LeftFacingTriangle,
1636 ListView,
1637 LockLocked,
1638 LockUnlocked,
1639 MenuMixedState,
1640 MenuOnState,
1641 MobileMe,
1642 MultipleDocuments,
1643 Network,
1644 Path,
1645 PreferencesGeneral,
1646 QuickLook,
1647 RefreshFreestanding,
1648 Refresh,
1649 Remove,
1650 RevealFreestanding,
1651 RightFacingTriangle,
1652 Share,
1653 Slideshow,
1654 SmartBadge,
1655 StatusAvailable,
1656 StatusNone,
1657 StatusPartiallyAvailable,
1658 StatusUnavailable,
1659 StopProgressFreestanding,
1660 StopProgress,
1661 TrashEmpty,
1662 TrashFull,
1663 User,
1664 UserAccounts,
1665 UserGroup,
1666 UserGuest
1667);
1668
1669#[derive(FromPyObject, IntoPyObject, IntoPyObjectRef)]
1671#[non_exhaustive]
1672pub enum ImplContextMenu {
1673 Menu(Py<Menu>),
1674 Submenu(Py<Submenu>),
1675}
1676
1677impl ImplContextMenu {
1678 pub(crate) fn _delegate_inner_ref<M, R>(menu: &M, f: impl FnOnce(&M) -> R) -> R
1679 where
1680 M: menu::ContextMenu,
1681 {
1682 f(menu)
1683 }
1684}
1685
1686macro_rules! context_menu_impl {
1688 ($menu:expr, $f0:expr, $f1:expr) => {{
1690 use $crate::ext_mod::menu::ImplContextMenu;
1691
1692 let menu: &ImplContextMenu = $menu;
1693 match menu {
1694 ImplContextMenu::Menu(v) => {
1695 ImplContextMenu::_delegate_inner_ref(&*v.get().0.inner_ref(), $f0)
1696 }
1697 ImplContextMenu::Submenu(v) => {
1698 ImplContextMenu::_delegate_inner_ref(&*v.get().0.inner_ref(), $f1)
1699 }
1700 }
1701 }};
1702
1703 ($menu:expr, $($f:tt)*) => {
1705 context_menu_impl!($menu, $($f)*, $($f)*)
1706 };
1707}
1708
1709pub(crate) use context_menu_impl;
1710
1711#[pyclass(frozen)]
1713#[non_exhaustive]
1714pub struct ContextMenu;
1715
1716#[pymethods]
1717impl ContextMenu {
1718 #[staticmethod]
1719 fn popup(
1720 py: Python<'_>,
1721 slf: ImplContextMenu,
1722 window: Py<ext_mod::window::Window>,
1723 ) -> PyResult<()> {
1724 py.allow_threads(|| {
1725 let window = window.get().0.inner_ref().to_owned();
1726 context_menu_impl!(&slf, |menu| {
1727 menu.popup(window)
1728 .map_err(TauriError::from)
1729 .map_err(PyErr::from)
1730 })
1731 })
1732 }
1733
1734 #[staticmethod]
1735 fn popup_at(
1736 py: Python<'_>,
1737 slf: ImplContextMenu,
1738 window: Py<ext_mod::window::Window>,
1739 position: Py<ext_mod::Position>,
1740 ) -> PyResult<()> {
1741 let position = position.get().to_tauri(py)?;
1742 py.allow_threads(|| {
1743 let window = window.get().0.inner_ref().to_owned();
1744 context_menu_impl!(&slf, |menu| {
1745 menu.popup_at(window, position)
1746 .map_err(TauriError::from)
1747 .map_err(PyErr::from)
1748 })
1749 })
1750 }
1751}