1use 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#[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 pub fn separator() -> PredefinedMenuItem {
39 PredefinedMenuItem::new::<&str>(PredefinedMenuItemType::Separator, None)
40 }
41
42 pub fn copy(text: Option<&str>) -> PredefinedMenuItem {
44 PredefinedMenuItem::new(PredefinedMenuItemType::Copy, text)
45 }
46
47 pub fn cut(text: Option<&str>) -> PredefinedMenuItem {
49 PredefinedMenuItem::new(PredefinedMenuItemType::Cut, text)
50 }
51
52 pub fn paste(text: Option<&str>) -> PredefinedMenuItem {
54 PredefinedMenuItem::new(PredefinedMenuItemType::Paste, text)
55 }
56
57 pub fn select_all(text: Option<&str>) -> PredefinedMenuItem {
59 PredefinedMenuItem::new(PredefinedMenuItemType::SelectAll, text)
60 }
61
62 pub fn redo(text: Option<&str>) -> PredefinedMenuItem {
64 PredefinedMenuItem::new(PredefinedMenuItemType::Redo, text)
65 }
66
67 pub fn minimize(text: Option<&str>) -> PredefinedMenuItem {
69 PredefinedMenuItem::new(PredefinedMenuItemType::Minimize, text)
70 }
71
72 pub fn maximize(text: Option<&str>) -> PredefinedMenuItem {
74 PredefinedMenuItem::new(PredefinedMenuItemType::Maximize, text)
75 }
76
77 pub fn hide(text: Option<&str>) -> PredefinedMenuItem {
79 PredefinedMenuItem::new(PredefinedMenuItemType::Hide, text)
80 }
81
82 pub fn hide_others(text: Option<&str>) -> PredefinedMenuItem {
84 PredefinedMenuItem::new(PredefinedMenuItemType::HideOthers, text)
85 }
86
87 pub fn close_window(text: Option<&str>) -> PredefinedMenuItem {
89 PredefinedMenuItem::new(PredefinedMenuItemType::CloseWindow, text)
90 }
91
92 pub fn quit(text: Option<&str>) -> PredefinedMenuItem {
94 PredefinedMenuItem::new(PredefinedMenuItemType::Quit, text)
95 }
96
97 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 pub fn id(&self) -> &MenuId {
115 &self.id
116 }
117
118 pub fn text(&self) -> String {
120 self.inner.borrow().text()
121 }
122
123 pub fn set_text<S: AsRef<str>>(&self, text: S) {
125 self.inner.borrow_mut().set_text(text.as_ref())
126 }
127
128 pub fn into_id(mut self) -> MenuId {
130 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}