gpui/platform/
app_menu.rs1use crate::{Action, App, Platform, SharedString};
2use util::ResultExt;
3
4pub struct Menu {
6 pub name: SharedString,
8
9 pub items: Vec<MenuItem>,
11}
12
13impl Menu {
14 pub fn owned(self) -> OwnedMenu {
16 OwnedMenu {
17 name: self.name.to_string().into(),
18 items: self.items.into_iter().map(|item| item.owned()).collect(),
19 }
20 }
21}
22
23pub struct OsMenu {
27 pub name: SharedString,
29
30 pub menu_type: SystemMenuType,
32}
33
34impl OsMenu {
35 pub fn owned(self) -> OwnedOsMenu {
37 OwnedOsMenu {
38 name: self.name.to_string().into(),
39 menu_type: self.menu_type,
40 }
41 }
42}
43
44#[derive(Copy, Clone, Eq, PartialEq)]
46pub enum SystemMenuType {
47 Services,
49}
50
51pub enum MenuItem {
53 Separator,
55
56 Submenu(Menu),
58
59 SystemMenu(OsMenu),
61
62 Action {
64 name: SharedString,
66
67 action: Box<dyn Action>,
69
70 os_action: Option<OsAction>,
73 },
74}
75
76impl MenuItem {
77 pub fn separator() -> Self {
79 Self::Separator
80 }
81
82 pub fn submenu(menu: Menu) -> Self {
84 Self::Submenu(menu)
85 }
86
87 pub fn os_submenu(name: impl Into<SharedString>, menu_type: SystemMenuType) -> Self {
89 Self::SystemMenu(OsMenu {
90 name: name.into(),
91 menu_type,
92 })
93 }
94
95 pub fn action(name: impl Into<SharedString>, action: impl Action) -> Self {
97 Self::Action {
98 name: name.into(),
99 action: Box::new(action),
100 os_action: None,
101 }
102 }
103
104 pub fn os_action(
106 name: impl Into<SharedString>,
107 action: impl Action,
108 os_action: OsAction,
109 ) -> Self {
110 Self::Action {
111 name: name.into(),
112 action: Box::new(action),
113 os_action: Some(os_action),
114 }
115 }
116
117 pub fn owned(self) -> OwnedMenuItem {
119 match self {
120 MenuItem::Separator => OwnedMenuItem::Separator,
121 MenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.owned()),
122 MenuItem::Action {
123 name,
124 action,
125 os_action,
126 } => OwnedMenuItem::Action {
127 name: name.into(),
128 action,
129 os_action,
130 },
131 MenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.owned()),
132 }
133 }
134}
135
136#[derive(Clone)]
140pub struct OwnedOsMenu {
141 pub name: SharedString,
143
144 pub menu_type: SystemMenuType,
146}
147
148#[derive(Clone)]
150pub struct OwnedMenu {
151 pub name: SharedString,
153
154 pub items: Vec<OwnedMenuItem>,
156}
157
158pub enum OwnedMenuItem {
160 Separator,
162
163 Submenu(OwnedMenu),
165
166 SystemMenu(OwnedOsMenu),
168
169 Action {
171 name: String,
173
174 action: Box<dyn Action>,
176
177 os_action: Option<OsAction>,
180 },
181}
182
183impl Clone for OwnedMenuItem {
184 fn clone(&self) -> Self {
185 match self {
186 OwnedMenuItem::Separator => OwnedMenuItem::Separator,
187 OwnedMenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.clone()),
188 OwnedMenuItem::Action {
189 name,
190 action,
191 os_action,
192 } => OwnedMenuItem::Action {
193 name: name.clone(),
194 action: action.boxed_clone(),
195 os_action: *os_action,
196 },
197 OwnedMenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.clone()),
198 }
199 }
200}
201
202#[derive(Copy, Clone, Eq, PartialEq)]
210pub enum OsAction {
211 Cut,
213
214 Copy,
216
217 Paste,
219
220 SelectAll,
222
223 Undo,
225
226 Redo,
228}
229
230pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &App) {
231 platform.on_will_open_app_menu(Box::new({
232 let cx = cx.to_async();
233 move || {
234 cx.update(|cx| cx.clear_pending_keystrokes()).ok();
235 }
236 }));
237
238 platform.on_validate_app_menu_command(Box::new({
239 let cx = cx.to_async();
240 move |action| {
241 cx.update(|cx| cx.is_action_available(action))
242 .unwrap_or(false)
243 }
244 }));
245
246 platform.on_app_menu_action(Box::new({
247 let cx = cx.to_async();
248 move |action| {
249 cx.update(|cx| cx.dispatch_action(action)).log_err();
250 }
251 }));
252}