1use crate::{platform::ForegroundPlatform, Action, App, AppContext};
2
3pub struct Menu<'a> {
4 pub name: &'a str,
5 pub items: Vec<MenuItem<'a>>,
6}
7
8pub enum MenuItem<'a> {
9 Separator,
10 Submenu(Menu<'a>),
11 Action {
12 name: &'a str,
13 action: Box<dyn Action>,
14 os_action: Option<OsAction>,
15 },
16}
17
18impl<'a> MenuItem<'a> {
19 pub fn separator() -> Self {
20 Self::Separator
21 }
22
23 pub fn submenu(menu: Menu<'a>) -> Self {
24 Self::Submenu(menu)
25 }
26
27 pub fn action(name: &'a str, action: impl Action) -> Self {
28 Self::Action {
29 name,
30 action: Box::new(action),
31 os_action: None,
32 }
33 }
34
35 pub fn os_action(name: &'a str, action: impl Action, os_action: OsAction) -> Self {
36 Self::Action {
37 name,
38 action: Box::new(action),
39 os_action: Some(os_action),
40 }
41 }
42}
43
44#[derive(Copy, Clone, Eq, PartialEq)]
45pub enum OsAction {
46 Cut,
47 Copy,
48 Paste,
49 SelectAll,
50 Undo,
51 Redo,
52}
53
54impl AppContext {
55 pub fn set_menus(&mut self, menus: Vec<Menu>) {
56 self.foreground_platform
57 .set_menus(menus, &self.keystroke_matcher);
58 }
59}
60
61pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform, app: &App) {
62 foreground_platform.on_will_open_menu(Box::new({
63 let cx = app.0.clone();
64 move || {
65 let mut cx = cx.borrow_mut();
66 cx.keystroke_matcher.clear_pending();
67 }
68 }));
69 foreground_platform.on_validate_menu_command(Box::new({
70 let cx = app.0.clone();
71 move |action| {
72 let cx = cx.borrow_mut();
73 !cx.keystroke_matcher.has_pending_keystrokes() && cx.is_action_available(action)
74 }
75 }));
76 foreground_platform.on_menu_command(Box::new({
77 let cx = app.0.clone();
78 move |action| {
79 let mut cx = cx.borrow_mut();
80 if let Some(main_window) = cx.active_window() {
81 let dispatched = main_window
82 .update(&mut *cx, |cx| {
83 if let Some(view_id) = cx.focused_view_id() {
84 cx.dispatch_action(Some(view_id), action);
85 true
86 } else {
87 false
88 }
89 })
90 .unwrap_or(false);
91
92 if dispatched {
93 return;
94 }
95 }
96 cx.dispatch_global_action_any(action);
97 }
98 }));
99}