tauri/menu/builders/
menu.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5use crate::{image::Image, menu::*, Manager, Runtime};
6
7/// A builder type for [`Menu`]
8///
9/// ## Platform-specific:
10///
11/// - **macOS**: if using [`MenuBuilder`] for the global menubar, it can only contain [`Submenu`]s
12///
13/// # Example
14///
15/// ```no_run
16/// use tauri::menu::*;
17/// tauri::Builder::default()
18///   .setup(move |app| {
19///     let handle = app.handle();
20///     # let icon1 = tauri::image::Image::new(&[], 0, 0);
21///     let menu = MenuBuilder::new(handle)
22///       .item(&MenuItem::new(handle, "MenuItem 1", true, None::<&str>)?)
23///       .items(&[
24///         &CheckMenuItem::new(handle, "CheckMenuItem 1", true, true, None::<&str>)?,
25///         &IconMenuItem::new(handle, "IconMenuItem 1", true, Some(icon1), None::<&str>)?,
26///       ])
27///       .separator()
28///       .cut()
29///       .copy()
30///       .paste()
31///       .separator()
32///       .text("item2", "MenuItem 2")
33///       .check("checkitem2", "CheckMenuItem 2")
34///       .icon("iconitem2", "IconMenuItem 2", app.default_window_icon().cloned().unwrap())
35///       .build()?;
36///     app.set_menu(menu);
37///     Ok(())
38///   });
39/// ```
40pub struct MenuBuilder<'m, R: Runtime, M: Manager<R>> {
41  pub(crate) id: Option<MenuId>,
42  pub(crate) manager: &'m M,
43  pub(crate) items: Vec<crate::Result<MenuItemKind<R>>>,
44}
45
46impl<'m, R: Runtime, M: Manager<R>> MenuBuilder<'m, R, M> {
47  /// Create a new menu builder.
48  pub fn new(manager: &'m M) -> Self {
49    Self {
50      id: None,
51      items: Vec::new(),
52      manager,
53    }
54  }
55
56  /// Create a new menu builder with the specified id.
57  pub fn with_id<I: Into<MenuId>>(manager: &'m M, id: I) -> Self {
58    Self {
59      id: Some(id.into()),
60      items: Vec::new(),
61      manager,
62    }
63  }
64
65  /// Builds this menu
66  pub fn build(self) -> crate::Result<Menu<R>> {
67    let menu = if let Some(id) = self.id {
68      Menu::with_id(self.manager, id)?
69    } else {
70      Menu::new(self.manager)?
71    };
72
73    for item in self.items {
74      let item = item?;
75      menu.append(&item)?;
76    }
77
78    Ok(menu)
79  }
80}
81
82/// A builder type for [`Submenu`]
83///
84/// # Example
85///
86/// ```no_run
87/// use tauri::menu::*;
88/// tauri::Builder::default()
89///   .setup(move |app| {
90///     let handle = app.handle();
91///     # let icon1 = tauri::image::Image::new(&[], 0, 0);
92///     # let icon2 = icon1.clone();
93///     let menu = Menu::new(handle)?;
94///     let submenu = SubmenuBuilder::new(handle, "File")
95///       .item(&MenuItem::new(handle, "MenuItem 1", true, None::<&str>)?)
96///       .items(&[
97///         &CheckMenuItem::new(handle, "CheckMenuItem 1", true, true, None::<&str>)?,
98///         &IconMenuItem::new(handle, "IconMenuItem 1", true, Some(icon1), None::<&str>)?,
99///       ])
100///       .separator()
101///       .cut()
102///       .copy()
103///       .paste()
104///       .separator()
105///       .text("item2", "MenuItem 2")
106///       .check("checkitem2", "CheckMenuItem 2")
107///       .icon("iconitem2", "IconMenuItem 2", app.default_window_icon().cloned().unwrap())
108///       .build()?;
109///     menu.append(&submenu)?;
110///     app.set_menu(menu);
111///     Ok(())
112///   });
113/// ```
114pub struct SubmenuBuilder<'m, R: Runtime, M: Manager<R>> {
115  pub(crate) id: Option<MenuId>,
116  pub(crate) manager: &'m M,
117  pub(crate) text: String,
118  pub(crate) enabled: bool,
119  pub(crate) items: Vec<crate::Result<MenuItemKind<R>>>,
120  pub(crate) icon: Option<crate::image::Image<'m>>,
121  pub(crate) native_icon: Option<NativeIcon>,
122}
123
124impl<'m, R: Runtime, M: Manager<R>> SubmenuBuilder<'m, R, M> {
125  /// Create a new submenu builder.
126  ///
127  /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
128  ///   for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
129  pub fn new<S: AsRef<str>>(manager: &'m M, text: S) -> Self {
130    Self {
131      id: None,
132      items: Vec::new(),
133      text: text.as_ref().to_string(),
134      enabled: true,
135      manager,
136      icon: None,
137      native_icon: None,
138    }
139  }
140
141  /// Create a new submenu builder with the specified id.
142  ///
143  /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic
144  ///   for this menu item. To display a `&` without assigning a mnemenonic, use `&&`.
145  pub fn with_id<I: Into<MenuId>, S: AsRef<str>>(manager: &'m M, id: I, text: S) -> Self {
146    Self {
147      id: Some(id.into()),
148      text: text.as_ref().to_string(),
149      enabled: true,
150      items: Vec::new(),
151      manager,
152      icon: None,
153      native_icon: None,
154    }
155  }
156
157  /// Set an icon for the submenu.
158  /// Calling this method resets the native_icon.
159  pub fn submenu_icon(mut self, icon: crate::image::Image<'m>) -> Self {
160    self.icon = Some(icon);
161    self.native_icon = None;
162    self
163  }
164
165  /// Set a native icon for the submenu.
166  /// Calling this method resets the icon.
167  pub fn submenu_native_icon(mut self, icon: NativeIcon) -> Self {
168    self.native_icon = Some(icon);
169    self.icon = None;
170    self
171  }
172
173  /// Set the enabled state for the submenu.
174  pub fn enabled(mut self, enabled: bool) -> Self {
175    self.enabled = enabled;
176    self
177  }
178
179  /// Builds this submenu
180  pub fn build(self) -> crate::Result<Submenu<R>> {
181    let submenu = if let Some(id) = self.id {
182      if let Some(icon) = self.icon {
183        Submenu::with_id_and_icon(self.manager, id, self.text, self.enabled, Some(icon))?
184      } else if let Some(native_icon) = self.native_icon {
185        Submenu::with_id_and_native_icon(
186          self.manager,
187          id,
188          self.text,
189          self.enabled,
190          Some(native_icon),
191        )?
192      } else {
193        Submenu::with_id(self.manager, id, self.text, self.enabled)?
194      }
195    } else if let Some(icon) = self.icon {
196      Submenu::new_with_icon(self.manager, self.text, self.enabled, Some(icon))?
197    } else if let Some(native_icon) = self.native_icon {
198      Submenu::new_with_native_icon(self.manager, self.text, self.enabled, Some(native_icon))?
199    } else {
200      Submenu::new(self.manager, self.text, self.enabled)?
201    };
202
203    for item in self.items {
204      let item = item?;
205      submenu.append(&item)?;
206    }
207
208    Ok(submenu)
209  }
210}
211
212macro_rules! shared_menu_builder {
213  ($menu: ty) => {
214    impl<'m, R: Runtime, M: Manager<R>> $menu {
215      /// Set the id for this menu.
216      pub fn id<I: Into<MenuId>>(mut self, id: I) -> Self {
217        self.id.replace(id.into());
218        self
219      }
220
221      /// Add this item to the menu.
222      pub fn item(mut self, item: &dyn IsMenuItem<R>) -> Self {
223        self.items.push(Ok(item.kind()));
224        self
225      }
226
227      /// Add these items to the menu.
228      pub fn items(mut self, items: &[&dyn IsMenuItem<R>]) -> Self {
229        for item in items {
230          self = self.item(*item);
231        }
232        self
233      }
234
235      /// Add a [MenuItem] to the menu.
236      pub fn text<I: Into<MenuId>, S: AsRef<str>>(mut self, id: I, text: S) -> Self {
237        self
238          .items
239          .push(MenuItem::with_id(self.manager, id, text, true, None::<&str>).map(|i| i.kind()));
240        self
241      }
242
243      /// Add a [CheckMenuItem] to the menu.
244      pub fn check<I: Into<MenuId>, S: AsRef<str>>(mut self, id: I, text: S) -> Self {
245        self.items.push(
246          CheckMenuItem::with_id(self.manager, id, text, true, true, None::<&str>)
247            .map(|i| i.kind()),
248        );
249        self
250      }
251
252      /// Add an [IconMenuItem] to the menu.
253      pub fn icon<I: Into<MenuId>, S: AsRef<str>>(
254        mut self,
255        id: I,
256        text: S,
257        icon: Image<'_>,
258      ) -> Self {
259        self.items.push(
260          IconMenuItem::with_id(self.manager, id, text, true, Some(icon), None::<&str>)
261            .map(|i| i.kind()),
262        );
263        self
264      }
265
266      /// Add an [IconMenuItem] with a native icon to the menu.
267      ///
268      /// ## Platform-specific:
269      ///
270      /// - **Windows / Linux**: Unsupported.
271      pub fn native_icon<I: Into<MenuId>, S: AsRef<str>>(
272        mut self,
273        id: I,
274        text: S,
275        icon: NativeIcon,
276      ) -> Self {
277        self.items.push(
278          IconMenuItem::with_id_and_native_icon(
279            self.manager,
280            id,
281            text,
282            true,
283            Some(icon),
284            None::<&str>,
285          )
286          .map(|i| i.kind()),
287        );
288        self
289      }
290
291      /// Add Separator menu item to the menu.
292      pub fn separator(mut self) -> Self {
293        self
294          .items
295          .push(PredefinedMenuItem::separator(self.manager).map(|i| i.kind()));
296        self
297      }
298
299      /// Add Copy menu item to the menu.
300      pub fn copy(mut self) -> Self {
301        self
302          .items
303          .push(PredefinedMenuItem::copy(self.manager, None).map(|i| i.kind()));
304        self
305      }
306
307      /// Add Copy menu item with specified text to the menu.
308      pub fn copy_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
309        self
310          .items
311          .push(PredefinedMenuItem::copy(self.manager, Some(text.as_ref())).map(|i| i.kind()));
312        self
313      }
314
315      /// Add Cut menu item to the menu.
316      pub fn cut(mut self) -> Self {
317        self
318          .items
319          .push(PredefinedMenuItem::cut(self.manager, None).map(|i| i.kind()));
320        self
321      }
322
323      /// Add Cut menu item with specified text to the menu.
324      pub fn cut_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
325        self
326          .items
327          .push(PredefinedMenuItem::cut(self.manager, Some(text.as_ref())).map(|i| i.kind()));
328        self
329      }
330
331      /// Add Paste menu item to the menu.
332      pub fn paste(mut self) -> Self {
333        self
334          .items
335          .push(PredefinedMenuItem::paste(self.manager, None).map(|i| i.kind()));
336        self
337      }
338
339      /// Add Paste menu item with specified text to the menu.
340      pub fn paste_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
341        self
342          .items
343          .push(PredefinedMenuItem::paste(self.manager, Some(text.as_ref())).map(|i| i.kind()));
344        self
345      }
346
347      /// Add SelectAll menu item to the menu.
348      pub fn select_all(mut self) -> Self {
349        self
350          .items
351          .push(PredefinedMenuItem::select_all(self.manager, None).map(|i| i.kind()));
352        self
353      }
354
355      /// Add SelectAll menu item with specified text to the menu.
356      pub fn select_all_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
357        self.items.push(
358          PredefinedMenuItem::select_all(self.manager, Some(text.as_ref())).map(|i| i.kind()),
359        );
360        self
361      }
362
363      /// Add Undo menu item to the menu.
364      ///
365      /// ## Platform-specific:
366      ///
367      /// - **Windows / Linux:** Unsupported.
368      pub fn undo(mut self) -> Self {
369        self
370          .items
371          .push(PredefinedMenuItem::undo(self.manager, None).map(|i| i.kind()));
372        self
373      }
374
375      /// Add Undo menu item with specified text to the menu.
376      ///
377      /// ## Platform-specific:
378      ///
379      /// - **Windows / Linux:** Unsupported.
380      pub fn undo_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
381        self
382          .items
383          .push(PredefinedMenuItem::undo(self.manager, Some(text.as_ref())).map(|i| i.kind()));
384        self
385      }
386      /// Add Redo menu item to the menu.
387      ///
388      /// ## Platform-specific:
389      ///
390      /// - **Windows / Linux:** Unsupported.
391      pub fn redo(mut self) -> Self {
392        self
393          .items
394          .push(PredefinedMenuItem::redo(self.manager, None).map(|i| i.kind()));
395        self
396      }
397
398      /// Add Redo menu item with specified text to the menu.
399      ///
400      /// ## Platform-specific:
401      ///
402      /// - **Windows / Linux:** Unsupported.
403      pub fn redo_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
404        self
405          .items
406          .push(PredefinedMenuItem::redo(self.manager, Some(text.as_ref())).map(|i| i.kind()));
407        self
408      }
409
410      /// Add Minimize window menu item to the menu.
411      ///
412      /// ## Platform-specific:
413      ///
414      /// - **Linux:** Unsupported.
415      pub fn minimize(mut self) -> Self {
416        self
417          .items
418          .push(PredefinedMenuItem::minimize(self.manager, None).map(|i| i.kind()));
419        self
420      }
421
422      /// Add Minimize window menu item with specified text to the menu.
423      ///
424      /// ## Platform-specific:
425      ///
426      /// - **Linux:** Unsupported.
427      pub fn minimize_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
428        self
429          .items
430          .push(PredefinedMenuItem::minimize(self.manager, Some(text.as_ref())).map(|i| i.kind()));
431        self
432      }
433
434      /// Add Maximize window menu item to the menu.
435      ///
436      /// ## Platform-specific:
437      ///
438      /// - **Linux:** Unsupported.
439      pub fn maximize(mut self) -> Self {
440        self
441          .items
442          .push(PredefinedMenuItem::maximize(self.manager, None).map(|i| i.kind()));
443        self
444      }
445
446      /// Add Maximize window menu item with specified text to the menu.
447      ///
448      /// ## Platform-specific:
449      ///
450      /// - **Linux:** Unsupported.
451      pub fn maximize_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
452        self
453          .items
454          .push(PredefinedMenuItem::maximize(self.manager, Some(text.as_ref())).map(|i| i.kind()));
455        self
456      }
457
458      /// Add Fullscreen menu item to the menu.
459      ///
460      /// ## Platform-specific:
461      ///
462      /// - **Windows / Linux:** Unsupported.
463      pub fn fullscreen(mut self) -> Self {
464        self
465          .items
466          .push(PredefinedMenuItem::fullscreen(self.manager, None).map(|i| i.kind()));
467        self
468      }
469
470      /// Add Fullscreen menu item with specified text to the menu.
471      ///
472      /// ## Platform-specific:
473      ///
474      /// - **Windows / Linux:** Unsupported.
475      pub fn fullscreen_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
476        self.items.push(
477          PredefinedMenuItem::fullscreen(self.manager, Some(text.as_ref())).map(|i| i.kind()),
478        );
479        self
480      }
481
482      /// Add Hide window menu item to the menu.
483      ///
484      /// ## Platform-specific:
485      ///
486      /// - **Linux:** Unsupported.
487      pub fn hide(mut self) -> Self {
488        self
489          .items
490          .push(PredefinedMenuItem::hide(self.manager, None).map(|i| i.kind()));
491        self
492      }
493
494      /// Add Hide window menu item with specified text to the menu.
495      ///
496      /// ## Platform-specific:
497      ///
498      /// - **Linux:** Unsupported.
499      pub fn hide_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
500        self
501          .items
502          .push(PredefinedMenuItem::hide(self.manager, Some(text.as_ref())).map(|i| i.kind()));
503        self
504      }
505
506      /// Add Hide other windows menu item to the menu.
507      ///
508      /// ## Platform-specific:
509      ///
510      /// - **Linux:** Unsupported.
511      pub fn hide_others(mut self) -> Self {
512        self
513          .items
514          .push(PredefinedMenuItem::hide_others(self.manager, None).map(|i| i.kind()));
515        self
516      }
517
518      /// Add Hide other windows menu item with specified text to the menu.
519      ///
520      /// ## Platform-specific:
521      ///
522      /// - **Linux:** Unsupported.
523      pub fn hide_others_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
524        self.items.push(
525          PredefinedMenuItem::hide_others(self.manager, Some(text.as_ref())).map(|i| i.kind()),
526        );
527        self
528      }
529
530      /// Add Show all app windows menu item to the menu.
531      ///
532      /// ## Platform-specific:
533      ///
534      /// - **Windows / Linux:** Unsupported.
535      pub fn show_all(mut self) -> Self {
536        self
537          .items
538          .push(PredefinedMenuItem::show_all(self.manager, None).map(|i| i.kind()));
539        self
540      }
541
542      /// Add Show all app windows menu item with specified text to the menu.
543      ///
544      /// ## Platform-specific:
545      ///
546      /// - **Windows / Linux:** Unsupported.
547      pub fn show_all_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
548        self
549          .items
550          .push(PredefinedMenuItem::show_all(self.manager, Some(text.as_ref())).map(|i| i.kind()));
551        self
552      }
553
554      /// Add Close window menu item to the menu.
555      ///
556      /// ## Platform-specific:
557      ///
558      /// - **Linux:** Unsupported.
559      pub fn close_window(mut self) -> Self {
560        self
561          .items
562          .push(PredefinedMenuItem::close_window(self.manager, None).map(|i| i.kind()));
563        self
564      }
565
566      /// Add Close window menu item with specified text to the menu.
567      ///
568      /// ## Platform-specific:
569      ///
570      /// - **Linux:** Unsupported.
571      pub fn close_window_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
572        self.items.push(
573          PredefinedMenuItem::close_window(self.manager, Some(text.as_ref())).map(|i| i.kind()),
574        );
575        self
576      }
577
578      /// Add Quit app menu item to the menu.
579      ///
580      /// ## Platform-specific:
581      ///
582      /// - **Linux:** Unsupported.
583      pub fn quit(mut self) -> Self {
584        self
585          .items
586          .push(PredefinedMenuItem::quit(self.manager, None).map(|i| i.kind()));
587        self
588      }
589
590      /// Add Quit app menu item with specified text to the menu.
591      ///
592      /// ## Platform-specific:
593      ///
594      /// - **Linux:** Unsupported.
595      pub fn quit_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
596        self
597          .items
598          .push(PredefinedMenuItem::quit(self.manager, Some(text.as_ref())).map(|i| i.kind()));
599        self
600      }
601
602      /// Add About app menu item to the menu.
603      pub fn about(mut self, metadata: Option<AboutMetadata<'_>>) -> Self {
604        self
605          .items
606          .push(PredefinedMenuItem::about(self.manager, None, metadata).map(|i| i.kind()));
607        self
608      }
609
610      /// Add About app menu item with specified text to the menu.
611      pub fn about_with_text<S: AsRef<str>>(
612        mut self,
613        text: S,
614        metadata: Option<AboutMetadata<'_>>,
615      ) -> Self {
616        self.items.push(
617          PredefinedMenuItem::about(self.manager, Some(text.as_ref()), metadata).map(|i| i.kind()),
618        );
619        self
620      }
621
622      /// Add Services menu item to the menu.
623      ///
624      /// ## Platform-specific:
625      ///
626      /// - **Windows / Linux:** Unsupported.
627      pub fn services(mut self) -> Self {
628        self
629          .items
630          .push(PredefinedMenuItem::services(self.manager, None).map(|i| i.kind()));
631        self
632      }
633
634      /// Add Services menu item with specified text to the menu.
635      ///
636      /// ## Platform-specific:
637      ///
638      /// - **Windows / Linux:** Unsupported.
639      pub fn services_with_text<S: AsRef<str>>(mut self, text: S) -> Self {
640        self
641          .items
642          .push(PredefinedMenuItem::services(self.manager, Some(text.as_ref())).map(|i| i.kind()));
643        self
644      }
645    }
646  };
647}
648
649shared_menu_builder!(MenuBuilder<'m, R, M>);
650shared_menu_builder!(SubmenuBuilder<'m, R, M>);