gpui/platform/
app_menu.rs1use crate::{Action, App, Platform, SharedString};
2use util::ResultExt;
3
4pub struct Menu {
6 pub name: SharedString,
8
9 pub icon: Option<Vec<u8>>,
11
12 pub items: Vec<MenuItem>,
14}
15
16impl Menu {
17 pub fn owned(self) -> OwnedMenu {
19 OwnedMenu {
20 name: self.name.to_string().into(),
21 icon: self.icon,
22 items: self.items.into_iter().map(|item| item.owned()).collect(),
23 }
24 }
25}
26
27pub struct OsMenu {
31 pub name: SharedString,
33
34 pub menu_type: SystemMenuType,
36}
37
38impl OsMenu {
39 pub fn owned(self) -> OwnedOsMenu {
41 OwnedOsMenu {
42 name: self.name.to_string().into(),
43 menu_type: self.menu_type,
44 }
45 }
46}
47
48#[derive(Copy, Clone, Eq, PartialEq)]
50pub enum SystemMenuType {
51 Services,
53}
54
55pub enum MenuItem {
57 Separator,
59
60 Submenu(Menu),
62
63 SystemMenu(OsMenu),
65
66 Action {
68 name: SharedString,
70
71 action: Box<dyn Action>,
73
74 os_action: Option<OsAction>,
77 },
78}
79
80impl MenuItem {
81 pub fn separator() -> Self {
83 Self::Separator
84 }
85
86 pub fn submenu(menu: Menu) -> Self {
88 Self::Submenu(menu)
89 }
90
91 pub fn os_submenu(name: impl Into<SharedString>, menu_type: SystemMenuType) -> Self {
93 Self::SystemMenu(OsMenu {
94 name: name.into(),
95 menu_type,
96 })
97 }
98
99 pub fn action(name: impl Into<SharedString>, action: impl Action) -> Self {
101 Self::Action {
102 name: name.into(),
103 action: Box::new(action),
104 os_action: None,
105 }
106 }
107
108 pub fn os_action(
110 name: impl Into<SharedString>,
111 action: impl Action,
112 os_action: OsAction,
113 ) -> Self {
114 Self::Action {
115 name: name.into(),
116 action: Box::new(action),
117 os_action: Some(os_action),
118 }
119 }
120
121 pub fn owned(self) -> OwnedMenuItem {
123 match self {
124 MenuItem::Separator => OwnedMenuItem::Separator,
125 MenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.owned()),
126 MenuItem::Action {
127 name,
128 action,
129 os_action,
130 } => OwnedMenuItem::Action {
131 name: name.into(),
132 action,
133 os_action,
134 },
135 MenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.owned()),
136 }
137 }
138}
139
140#[derive(Clone)]
144pub struct OwnedOsMenu {
145 pub name: SharedString,
147
148 pub menu_type: SystemMenuType,
150}
151
152#[derive(Clone)]
154pub struct OwnedMenu {
155 pub name: SharedString,
157
158 pub icon: Option<Vec<u8>>,
160
161 pub items: Vec<OwnedMenuItem>,
163}
164
165pub enum OwnedMenuItem {
167 Separator,
169
170 Submenu(OwnedMenu),
172
173 SystemMenu(OwnedOsMenu),
175
176 Action {
178 name: String,
180
181 action: Box<dyn Action>,
183
184 os_action: Option<OsAction>,
187 },
188}
189
190impl Clone for OwnedMenuItem {
191 fn clone(&self) -> Self {
192 match self {
193 OwnedMenuItem::Separator => OwnedMenuItem::Separator,
194 OwnedMenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.clone()),
195 OwnedMenuItem::Action {
196 name,
197 action,
198 os_action,
199 } => OwnedMenuItem::Action {
200 name: name.clone(),
201 action: action.boxed_clone(),
202 os_action: *os_action,
203 },
204 OwnedMenuItem::SystemMenu(os_menu) => OwnedMenuItem::SystemMenu(os_menu.clone()),
205 }
206 }
207}
208
209#[derive(Copy, Clone, Eq, PartialEq)]
217pub enum OsAction {
218 Cut,
220
221 Copy,
223
224 Paste,
226
227 SelectAll,
229
230 Undo,
232
233 Redo,
235}
236
237pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &App) {
238 platform.on_will_open_app_menu(Box::new({
239 let cx = cx.to_async();
240 move || {
241 cx.update(|cx| cx.clear_pending_keystrokes()).ok();
242 }
243 }));
244
245 platform.on_validate_app_menu_command(Box::new({
246 let cx = cx.to_async();
247 move |action| {
248 cx.update(|cx| cx.is_action_available(action))
249 .unwrap_or(false)
250 }
251 }));
252
253 platform.on_app_menu_action(Box::new({
254 let cx = cx.to_async();
255 move |action| {
256 cx.update(|cx| cx.dispatch_action(action)).log_err();
257 }
258 }));
259}