Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/platform/menu.rs
1use crate::actions::StandardAction;
2use crate::keymap::Keystroke;
3use crate::AppContext;
4 
5pub enum MenuItem {
6 Custom(CustomMenuItem),
7 Separator,
8 Standard(StandardAction),
9 
10 /// Services is a system-defined standard menu on macOS.
11 #[cfg(target_os = "macos")]
12 Services,
13}
14 
15// We allow dead_code here because the title is only read when compiling the
16// Mac bits.
17#[allow(dead_code)]
18pub struct Menu {
19 pub title: String,
20 pub menu_items: Vec<MenuItem>,
21}
22 
23impl Menu {
24 pub fn new<S: Into<String>>(title: S, menu_items: Vec<MenuItem>) -> Self {
25 Menu {
26 title: title.into(),
27 menu_items,
28 }
29 }
30 
31 pub fn is_window_menu(&self) -> bool {
32 &self.title == "Window"
33 }
34}
35 
36#[allow(dead_code)]
37pub struct MenuBar {
38 pub menus: Vec<Menu>,
39}
40 
41impl MenuBar {
42 pub fn new(menus: Vec<Menu>) -> Self {
43 MenuBar { menus }
44 }
45}
46 
47/// Properties of a menu item.
48#[derive(Clone, Debug, Default)]
49pub struct MenuItemProperties {
50 pub name: String,
51 pub keystroke: Option<Keystroke>,
52 pub disabled: bool,
53 /// If set, the item gets a checkmark.
54 pub checked: bool,
55}
56 
57impl MenuItemProperties {
58 pub fn apply(&mut self, changes: &MenuItemPropertyChanges) {
59 if let Some(name) = &changes.name {
60 self.name.clone_from(name);
61 }
62 if let Some(keystroke) = changes.keystroke.as_ref() {
63 self.keystroke.clone_from(keystroke);
64 }
65 if let Some(disabled) = changes.disabled {
66 self.disabled = disabled;
67 }
68 if let Some(checked) = changes.checked {
69 self.checked = checked;
70 }
71 }
72}
73 
74/// Changes to properties of a menu item.
75#[derive(Default)]
76pub struct MenuItemPropertyChanges {
77 pub name: Option<String>,
78 pub keystroke: Option<Option<Keystroke>>,
79 pub disabled: Option<bool>,
80 pub checked: Option<bool>,
81 pub submenu: Option<Submenu>,
82}
83 
84impl MenuItemPropertyChanges {
85 /// Returns a struct that unconditionally sets all properties, to be used
86 /// when initializing a menu item for the first time.
87 #[cfg_attr(target_os = "linux", allow(dead_code))]
88 pub fn for_new_item(props: MenuItemProperties, submenu: Submenu) -> Self {
89 Self {
90 name: Some(props.name),
91 keystroke: Some(props.keystroke),
92 disabled: Some(props.disabled),
93 checked: Some(props.checked),
94 submenu: Some(submenu),
95 }
96 }
97}
98 
99pub type ItemTriggeredCallback = Box<dyn Fn(&mut AppContext)>;
100 
101/// A callback function that is invoked when we may want to update
102/// a menu item.
103///
104/// It receives a reference to the current set of properties, and
105/// returns a structure indicating which properties should be updated
106/// and what the new values should be.
107pub type UpdateMenuItemCallback =
108 Box<dyn Fn(&MenuItemProperties, &mut AppContext) -> MenuItemPropertyChanges>;
109 
110pub type Submenu = Option<Vec<MenuItem>>;
111 
112pub struct CustomMenuItem {
113 pub properties: MenuItemProperties,
114 pub callback: ItemTriggeredCallback,
115 pub updater: UpdateMenuItemCallback,
116 pub submenu: Submenu,
117}
118 
119impl CustomMenuItem {
120 /// Construct a new CustomMenuItem with the given \p name.
121 /// \p callback will be invoked when the user triggers the menu item.
122 /// \p updater is invoked when the menu is opened or otherwise needs to be updated.
123 /// The function receives a bag of properties and may mutate it.
124 /// Any properties that are changed will be reflected in the menu item.
125 pub fn new<
126 Callback: 'static + Fn(&mut AppContext),
127 Updater: 'static + Fn(&MenuItemProperties, &mut AppContext) -> MenuItemPropertyChanges,
128 >(
129 name: &str,
130 callback: Callback,
131 updater: Updater,
132 keystroke: Option<Keystroke>,
133 ) -> Self {
134 Self {
135 properties: MenuItemProperties {
136 name: name.to_string(),
137 keystroke,
138 ..Default::default()
139 },
140 callback: Box::new(callback),
141 updater: Box::new(updater),
142 submenu: None,
143 }
144 }
145 
146 // Constructor that takes in additional submenu argument.
147 pub fn new_with_submenu<
148 Callback: 'static + Fn(&mut AppContext),
149 Updater: 'static + Fn(&MenuItemProperties, &mut AppContext) -> MenuItemPropertyChanges,
150 >(
151 name: &str,
152 callback: Callback,
153 updater: Updater,
154 keystroke: Option<Keystroke>,
155 submenu: Vec<MenuItem>,
156 ) -> Self {
157 Self {
158 properties: MenuItemProperties {
159 name: name.to_string(),
160 keystroke,
161 ..Default::default()
162 },
163 callback: Box::new(callback),
164 updater: Box::new(updater),
165 submenu: Some(submenu),
166 }
167 }
168}
169