1use crate::command::Command;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum MenuItem {
6 Action(ActionItem),
7 Separator(SeparatorItem),
8 SubMenu(SubMenuItem),
9}
10
11impl MenuItem {
12 pub fn text(&self) -> Option<&str> {
14 match self {
15 Self::Action(action) => Some(&action.label),
16 Self::SubMenu(submenu) => Some(&submenu.label),
17 Self::Separator(_) => None,
18 }
19 }
20
21 pub fn is_enabled(&self) -> bool {
23 match self {
24 Self::Action(action) => action.enabled,
25 Self::SubMenu(submenu) => submenu.enabled,
26 Self::Separator(_) => false, }
28 }
29
30 pub fn label(&self) -> Option<&str> {
32 match self {
33 Self::Action(action) => Some(&action.label),
34 Self::SubMenu(submenu) => Some(&submenu.label),
35 Self::Separator(_) => None,
36 }
37 }
38
39 pub fn hotkey(&self) -> Option<char> {
41 match self {
42 Self::Action(action) => action.hotkey,
43 Self::SubMenu(submenu) => submenu.hotkey,
44 Self::Separator(_) => None,
45 }
46 }
47
48 pub fn is_selectable(&self) -> bool {
50 !matches!(self, Self::Separator(_))
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct ActionItem {
57 pub label: String,
59
60 pub command: Command,
62
63 pub enabled: bool,
65
66 pub hotkey: Option<char>,
68
69 pub shortcut: Option<String>,
71
72 pub help_context: Option<String>,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct SeparatorItem;
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct SubMenuItem {
83 pub label: String,
85
86 pub items: Vec<MenuItem>,
88
89 pub enabled: bool,
91
92 pub hotkey: Option<char>,
94
95 pub help_context: Option<String>,
97
98 pub focused_item: Option<usize>,
100
101 pub is_open: bool,
103}
104
105impl Default for SeparatorItem {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111impl SeparatorItem {
112 pub fn new() -> Self {
114 Self
115 }
116}
117
118impl MenuItem {
120 pub fn new_action<S: Into<String>, C: Into<Command>>(label: S, command: C) -> Self {
122 Self::Action(ActionItem::new(label, command))
123 }
124
125 pub fn action_with_hotkey<S: Into<String>, C: Into<Command>>(
127 label: S,
128 command: C,
129 hotkey: char,
130 ) -> Self {
131 Self::Action(ActionItem::with_hotkey(label, command, hotkey))
132 }
133
134 pub fn action_with_all<S: Into<String>, C: Into<Command>>(
136 label: S,
137 command: C,
138 hotkey: Option<char>,
139 shortcut: Option<S>,
140 ) -> Self {
141 Self::Action(ActionItem::with_all(label, command, hotkey, shortcut))
142 }
143
144 pub fn separator() -> Self {
146 Self::Separator(SeparatorItem::new())
147 }
148
149 pub fn new_submenu<S: Into<String>>(label: S) -> Self {
151 Self::SubMenu(SubMenuItem::new(label))
152 }
153
154 pub fn submenu_with_hotkey<S: Into<String>>(label: S, hotkey: char) -> Self {
156 Self::SubMenu(SubMenuItem::with_hotkey(label, hotkey))
157 }
158
159 pub fn submenu_with_items<S: Into<String>>(
161 label: S,
162 hotkey: Option<char>,
163 items: Vec<MenuItem>,
164 ) -> Self {
165 Self::SubMenu(SubMenuItem::with_items(label, hotkey, items))
166 }
167
168 pub fn action<S: Into<String>, C: Into<Command>>(text: S, command: C) -> Self {
171 Self::new_action(text, command)
172 }
173
174 pub fn submenu<S: Into<String>>(text: S, items: Vec<MenuItem>) -> Self {
176 Self::SubMenu(SubMenuItem::with_items(text, None, items))
177 }
178
179 pub fn with_hotkey(mut self, hotkey: char) -> Self {
181 match &mut self {
182 Self::Action(action) => action.hotkey = Some(hotkey),
183 Self::SubMenu(submenu) => submenu.hotkey = Some(hotkey),
184 Self::Separator(_) => {} }
186 self
187 }
188
189 pub fn with_shortcut<S: Into<String>>(mut self, shortcut: S) -> Self {
191 if let Self::Action(action) = &mut self {
192 action.shortcut = Some(shortcut.into());
193 }
194 self
195 }
196
197 pub fn with_help_context<S: Into<String>>(mut self, help_context: S) -> Self {
199 match &mut self {
200 Self::Action(action) => action.help_context = Some(help_context.into()),
201 Self::SubMenu(submenu) => submenu.help_context = Some(help_context.into()),
202 Self::Separator(_) => {} }
204 self
205 }
206
207 pub fn with_enabled(mut self, enabled: bool) -> Self {
209 match &mut self {
210 Self::Action(action) => action.enabled = enabled,
211 Self::SubMenu(submenu) => submenu.enabled = enabled,
212 Self::Separator(_) => {} }
214 self
215 }
216}
217
218impl ActionItem {
220 pub fn new<S: Into<String>, C: Into<Command>>(label: S, command: C) -> Self {
222 Self {
223 label: label.into(),
224 command: command.into(),
225 enabled: true,
226 hotkey: None,
227 shortcut: None,
228 help_context: None,
229 }
230 }
231
232 pub fn with_hotkey<S: Into<String>, C: Into<Command>>(
234 label: S,
235 command: C,
236 hotkey: char,
237 ) -> Self {
238 Self {
239 label: label.into(),
240 command: command.into(),
241 enabled: true,
242 hotkey: Some(hotkey),
243 shortcut: None,
244 help_context: None,
245 }
246 }
247
248 pub fn with_all<S: Into<String>, C: Into<Command>>(
250 label: S,
251 command: C,
252 hotkey: Option<char>,
253 shortcut: Option<S>,
254 ) -> Self {
255 Self {
256 label: label.into(),
257 command: command.into(),
258 enabled: true,
259 hotkey,
260 shortcut: shortcut.map(|s| s.into()),
261 help_context: None,
262 }
263 }
264
265 pub fn hotkey(mut self, hotkey: char) -> Self {
267 self.hotkey = Some(hotkey);
268 self
269 }
270
271 pub fn shortcut<S: Into<String>>(mut self, shortcut: S) -> Self {
273 self.shortcut = Some(shortcut.into());
274 self
275 }
276
277 pub fn help_context<S: Into<String>>(mut self, help_context: S) -> Self {
279 self.help_context = Some(help_context.into());
280 self
281 }
282
283 pub fn enabled(mut self, enabled: bool) -> Self {
285 self.enabled = enabled;
286 self
287 }
288}
289
290impl SubMenuItem {
294 pub fn new<S: Into<String>>(label: S) -> Self {
296 Self {
297 label: label.into(),
298 items: Vec::new(),
299 enabled: true,
300 hotkey: None,
301 help_context: None,
302 focused_item: None,
303 is_open: false,
304 }
305 }
306
307 pub fn with_hotkey<S: Into<String>>(label: S, hotkey: char) -> Self {
309 Self {
310 label: label.into(),
311 items: Vec::new(),
312 enabled: true,
313 hotkey: Some(hotkey),
314 help_context: None,
315 focused_item: None,
316 is_open: false,
317 }
318 }
319
320 pub fn with_items<S: Into<String>>(
333 label: S,
334 hotkey: Option<char>,
335 items: Vec<MenuItem>,
336 ) -> Self {
337 Self {
338 label: label.into(),
339 items,
340 enabled: true,
341 hotkey,
342 help_context: None,
343 focused_item: None,
344 is_open: false,
345 }
346 }
347
348 pub fn hotkey(mut self, hotkey: char) -> Self {
350 self.hotkey = Some(hotkey);
351 self
352 }
353
354 pub fn item(mut self, item: MenuItem) -> Self {
356 self.items.push(item);
357 self
358 }
359
360 pub fn help_context<S: Into<String>>(mut self, help_context: S) -> Self {
362 self.help_context = Some(help_context.into());
363 self
364 }
365
366 pub fn enabled(mut self, enabled: bool) -> Self {
368 self.enabled = enabled;
369 self
370 }
371
372 pub fn add_item(&mut self, item: MenuItem) {
374 self.items.push(item);
375 }
376}