muda_win/items/
predefined.rs

1// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.inner
3// SPDX-License-Identifier: MIT
4
5use std::{cell::RefCell, mem, rc::Rc};
6
7use crate::{
8    accelerator::{Accelerator, CMD_OR_CTRL},
9    sealed::IsMenuItemBase,
10    AboutMetadata, IsMenuItem, MenuId, MenuItemKind,
11};
12use keyboard_types::{Code, Modifiers};
13
14/// A predefined (native) menu item which has a predfined behavior by the OS or by this crate.
15#[derive(Clone)]
16pub struct PredefinedMenuItem {
17    pub(crate) id: Rc<MenuId>,
18    pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
19}
20
21impl IsMenuItemBase for PredefinedMenuItem {}
22impl IsMenuItem for PredefinedMenuItem {
23    fn kind(&self) -> MenuItemKind {
24        MenuItemKind::Predefined(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 PredefinedMenuItem {
37    /// Separator menu item
38    pub fn separator() -> PredefinedMenuItem {
39        PredefinedMenuItem::new::<&str>(PredefinedMenuItemType::Separator, None)
40    }
41
42    /// Copy menu item
43    pub fn copy(text: Option<&str>) -> PredefinedMenuItem {
44        PredefinedMenuItem::new(PredefinedMenuItemType::Copy, text)
45    }
46
47    /// Cut menu item
48    pub fn cut(text: Option<&str>) -> PredefinedMenuItem {
49        PredefinedMenuItem::new(PredefinedMenuItemType::Cut, text)
50    }
51
52    /// Paste menu item
53    pub fn paste(text: Option<&str>) -> PredefinedMenuItem {
54        PredefinedMenuItem::new(PredefinedMenuItemType::Paste, text)
55    }
56
57    /// SelectAll menu item
58    pub fn select_all(text: Option<&str>) -> PredefinedMenuItem {
59        PredefinedMenuItem::new(PredefinedMenuItemType::SelectAll, text)
60    }
61
62    /// Redo menu item
63    pub fn redo(text: Option<&str>) -> PredefinedMenuItem {
64        PredefinedMenuItem::new(PredefinedMenuItemType::Redo, text)
65    }
66
67    /// Minimize window menu item
68    pub fn minimize(text: Option<&str>) -> PredefinedMenuItem {
69        PredefinedMenuItem::new(PredefinedMenuItemType::Minimize, text)
70    }
71
72    /// Maximize window menu item
73    pub fn maximize(text: Option<&str>) -> PredefinedMenuItem {
74        PredefinedMenuItem::new(PredefinedMenuItemType::Maximize, text)
75    }
76
77    /// Hide window menu item
78    pub fn hide(text: Option<&str>) -> PredefinedMenuItem {
79        PredefinedMenuItem::new(PredefinedMenuItemType::Hide, text)
80    }
81
82    /// Hide other windows menu item
83    pub fn hide_others(text: Option<&str>) -> PredefinedMenuItem {
84        PredefinedMenuItem::new(PredefinedMenuItemType::HideOthers, text)
85    }
86
87    /// Close window menu item
88    pub fn close_window(text: Option<&str>) -> PredefinedMenuItem {
89        PredefinedMenuItem::new(PredefinedMenuItemType::CloseWindow, text)
90    }
91
92    /// Quit app menu item
93    pub fn quit(text: Option<&str>) -> PredefinedMenuItem {
94        PredefinedMenuItem::new(PredefinedMenuItemType::Quit, text)
95    }
96
97    /// About app menu item
98    pub fn about(text: Option<&str>, metadata: Option<AboutMetadata>) -> PredefinedMenuItem {
99        PredefinedMenuItem::new(PredefinedMenuItemType::About(metadata), text)
100    }
101
102    fn new<S: AsRef<str>>(item: PredefinedMenuItemType, text: Option<S>) -> Self {
103        let item = crate::platform_impl::MenuChild::new_predefined(
104            item,
105            text.map(|t| t.as_ref().to_string()),
106        );
107        Self {
108            id: Rc::new(item.id().clone()),
109            inner: Rc::new(RefCell::new(item)),
110        }
111    }
112
113    /// Returns a unique identifier associated with this predefined menu item.
114    pub fn id(&self) -> &MenuId {
115        &self.id
116    }
117
118    /// Get the text for this predefined menu item.
119    pub fn text(&self) -> String {
120        self.inner.borrow().text()
121    }
122
123    /// Set the text for this predefined menu item.
124    pub fn set_text<S: AsRef<str>>(&self, text: S) {
125        self.inner.borrow_mut().set_text(text.as_ref())
126    }
127
128    /// Convert this menu item into its menu ID.
129    pub fn into_id(mut self) -> MenuId {
130        // Note: `Rc::into_inner` is available from Rust 1.70
131        if let Some(id) = Rc::get_mut(&mut self.id) {
132            mem::take(id)
133        } else {
134            self.id().clone()
135        }
136    }
137}
138
139#[test]
140fn test_about_metadata() {
141    assert_eq!(
142        AboutMetadata {
143            ..Default::default()
144        }
145        .full_version(),
146        None
147    );
148
149    assert_eq!(
150        AboutMetadata {
151            version: Some("Version: 1.inner".into()),
152            ..Default::default()
153        }
154        .full_version(),
155        Some("Version: 1.inner".into())
156    );
157
158    assert_eq!(
159        AboutMetadata {
160            version: Some("Version: 1.inner".into()),
161            short_version: Some("Universal".into()),
162            ..Default::default()
163        }
164        .full_version(),
165        Some("Version: 1.inner (Universal)".into())
166    );
167}
168
169#[derive(Debug, Clone)]
170#[non_exhaustive]
171#[allow(clippy::large_enum_variant, dead_code)]
172pub(crate) enum PredefinedMenuItemType {
173    Separator,
174    Copy,
175    Cut,
176    Paste,
177    SelectAll,
178    Undo,
179    Redo,
180    Minimize,
181    Maximize,
182    Fullscreen,
183    Hide,
184    HideOthers,
185    ShowAll,
186    CloseWindow,
187    Quit,
188    About(Option<AboutMetadata>),
189    Services,
190    BringAllToFront,
191    None,
192}
193
194impl Default for PredefinedMenuItemType {
195    fn default() -> Self {
196        Self::None
197    }
198}
199
200impl PredefinedMenuItemType {
201    pub(crate) fn text(&self) -> &str {
202        match self {
203            PredefinedMenuItemType::Separator => "",
204            PredefinedMenuItemType::Copy => "&Copy",
205            PredefinedMenuItemType::Cut => "Cu&t",
206            PredefinedMenuItemType::Paste => "&Paste",
207            PredefinedMenuItemType::SelectAll => "Select &All",
208            PredefinedMenuItemType::Undo => "Undo",
209            PredefinedMenuItemType::Redo => "Redo",
210            PredefinedMenuItemType::Minimize => "&Minimize",
211            PredefinedMenuItemType::Maximize => "Ma&ximize",
212            PredefinedMenuItemType::Fullscreen => "Toggle Full Screen",
213            PredefinedMenuItemType::Hide => "&Hide",
214            PredefinedMenuItemType::HideOthers => "Hide Others",
215            PredefinedMenuItemType::ShowAll => "Show All",
216            PredefinedMenuItemType::CloseWindow => "Close",
217            PredefinedMenuItemType::Quit => "&Exit",
218            PredefinedMenuItemType::About(_) => "&About",
219            PredefinedMenuItemType::Services => "Services",
220            PredefinedMenuItemType::BringAllToFront => "Bring All to Front",
221            PredefinedMenuItemType::None => "",
222        }
223    }
224
225    pub(crate) fn accelerator(&self) -> Option<Accelerator> {
226        match self {
227            PredefinedMenuItemType::Copy => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyC)),
228            PredefinedMenuItemType::Cut => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyX)),
229            PredefinedMenuItemType::Paste => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyV)),
230            PredefinedMenuItemType::Undo => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyZ)),
231            PredefinedMenuItemType::Redo => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyY)),
232            PredefinedMenuItemType::SelectAll => {
233                Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyA))
234            }
235            PredefinedMenuItemType::Minimize => {
236                Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyM))
237            }
238            PredefinedMenuItemType::Hide => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyH)),
239            PredefinedMenuItemType::HideOthers => Some(Accelerator::new(
240                Some(CMD_OR_CTRL | Modifiers::ALT),
241                Code::KeyH,
242            )),
243            PredefinedMenuItemType::CloseWindow => {
244                Some(Accelerator::new(Some(Modifiers::ALT), Code::F4))
245            }
246            _ => None,
247        }
248    }
249}