native_windows_gui2/controls/menu.rs
1use super::{ControlBase, ControlHandle};
2use crate::NwgError;
3use crate::win32::menu as mh;
4use std::ptr;
5
6const NOT_BOUND: &'static str = "Menu/MenuItem is not yet bound to a winapi object";
7const BAD_HANDLE: &'static str = "INTERNAL ERROR: Menu/MenuItem handle is not HMENU!";
8
9bitflags! {
10 /**
11 Menu flags to use with the `Menu::popup_with_flags` function.
12 Using `PopupMenuFlags::empty` is the same as `ALIGN_LEFT|ALIGN_TOP|LEFT_BUTTON`
13
14 Aligment flags:
15
16 * ALIGN_LEFT: Positions the shortcut menu so that its left side is aligned with the coordinate specified by the x parameter.
17 * ALIGN_H_CENTER: Centers the shortcut menu horizontally relative to the coordinate specified by the x parameter.
18 * ALIGN_RIGHT: Positions the shortcut menu so that its right side is aligned with the coordinate specified by the x parameter.
19 * ALIGN_BOTTOM: Positions the shortcut menu so that its bottom side is aligned with the coordinate specified by the y parameter.
20 * ALIGN_TOP: Positions the shortcut menu so that its top side is aligned with the coordinate specified by the y parameter.
21 * ALIGN_V_CENTER: Centers the shortcut menu vertically relative to the coordinate specified by the y parameter.
22
23 Button flags:
24
25 * LEFT_BUTTON: The user can select menu items with only the left mouse button.
26 * RIGHT_BUTTON: The user can select menu items with both the left **AND** right mouse buttons.
27
28 Animations flags:
29
30 * ANIMATE_NONE: Displays menu without animation.
31 * ANIMATE_RIGHT_TO_LEFT: Animates the menu from right to left.
32 * ANIMATE_LEFT_TO_RIGHT: Animates the menu from left to right.
33 * ANIMATE_BOTTOM_TO_TOP: Animates the menu from bottom to top.
34 * ANIMATE_TOP_TO_BOTTOM: Animates the menu from top to bottom.
35 */
36 pub struct PopupMenuFlags: u32 {
37 const ALIGN_LEFT = 0x0000;
38 const ALIGN_H_CENTER = 0x0004;
39 const ALIGN_RIGHT = 0x0008;
40
41 const ALIGN_BOTTOM = 0x0020;
42 const ALIGN_TOP = 0x0000;
43 const ALIGN_V_CENTER = 0x0010;
44
45 const LEFT_BUTTON = 0x0000;
46 const RIGHT_BUTTON = 0x0002;
47
48 const ANIMATE_NONE = 0x4000;
49 const ANIMATE_RIGHT_TO_LEFT = 0x8000;
50 const ANIMATE_LEFT_TO_RIGHT = 0x4000;
51 const ANIMATE_BOTTOM_TO_TOP = 0x2000;
52 const ANIMATE_TOP_TO_BOTTOM = 0x1000;
53 }
54}
55
56/**
57 A windows menu. Can represent a menu in a window menubar, a context menu, or a submenu in another menu
58
59 Requires the `menu` feature.
60
61 **Builder parameters:**
62 - text: The text of the menu
63 - disabled: If the menu can be selected by the user
64 - popup: The menu is a context menu
65 - parent: A top level window, a menu or None. With a top level window, the menu is added to the menu bar if popup is set to false.
66
67 **Control events:**
68 - OnMenuOpen: Sent when a drop-down menu or submenu is about to become active.
69 - OnMenuHover: When the user hovers the menu
70 - OnMenuEnter: When the user enters the menu. Technically, when the user enters the menu modal loop.
71 - OnMenuExit: When the menu is closed. Technically, when the user exits the menu modal loop.
72
73 **Menu Access Keys**
74
75 Menu can have access keys. An access key is an underlined letter in the text of a menu item.
76 When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
77 The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
78 A menu is active when it is displayed.
79
80 To create an access key for a menu item, precede any character in the item's text string with an ampersand.
81 For example, the text string "&Move" causes the system to underline the letter "M".
82
83 ```rust
84 use native_windows_gui2 as nwg;
85
86 fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
87 nwg::Menu::builder()
88 .text("&Hello")
89 .disabled(false)
90 .parent(window)
91 .build(menu)
92 }
93 ```
94*/
95#[derive(Default, PartialEq, Eq)]
96pub struct Menu {
97 pub handle: ControlHandle,
98}
99
100impl Menu {
101 pub fn builder<'a>() -> MenuBuilder<'a> {
102 MenuBuilder {
103 text: "Menu",
104 disabled: false,
105 popup: false,
106 parent: None,
107 }
108 }
109
110 /// Return true if the control user can interact with the control, return false otherwise
111 pub fn enabled(&self) -> bool {
112 if self.handle.blank() {
113 panic!("{}", NOT_BOUND);
114 }
115 let (parent_handle, handle) = match self.handle {
116 ControlHandle::Menu(parent, menu) => (parent, menu),
117 ControlHandle::PopMenu(_, _) => {
118 return true;
119 }
120 _ => panic!("{}", BAD_HANDLE),
121 };
122
123 mh::is_menu_enabled(parent_handle, handle)
124 }
125
126 /// Enable or disable the control
127 /// A popup menu cannot be disabled
128 pub fn set_enabled(&self, v: bool) {
129 if self.handle.blank() {
130 panic!("{}", NOT_BOUND);
131 }
132 let (parent_handle, handle) = match self.handle {
133 ControlHandle::Menu(parent, menu) => (parent, menu),
134 ControlHandle::PopMenu(_, _) => {
135 return;
136 }
137 _ => panic!("{}", BAD_HANDLE),
138 };
139
140 mh::enable_menu(parent_handle, handle, v);
141 }
142
143 /// Show a popup menu as the selected position. Do nothing for menubar menu.
144 pub fn popup_with_flags(&self, x: i32, y: i32, flags: PopupMenuFlags) {
145 use winapi::ctypes::c_int;
146 use winapi::um::winuser::{SetForegroundWindow, TrackPopupMenu};
147
148 if self.handle.blank() {
149 panic!("Menu is not bound");
150 }
151 let (parent_handle, handle) = match self.handle.pop_hmenu() {
152 Some(v) => v,
153 None => {
154 return;
155 }
156 };
157
158 unsafe {
159 SetForegroundWindow(parent_handle);
160 TrackPopupMenu(
161 handle,
162 flags.bits(),
163 x as c_int,
164 y as c_int,
165 0,
166 parent_handle,
167 ptr::null(),
168 );
169 }
170 }
171
172 /// Show a popup menu as the selected position. Do nothing for menubar menu.
173 pub fn popup(&self, x: i32, y: i32) {
174 self.popup_with_flags(x, y, PopupMenuFlags::empty())
175 }
176}
177
178impl Drop for Menu {
179 fn drop(&mut self) {
180 self.handle.destroy();
181 }
182}
183
184pub struct MenuBuilder<'a> {
185 text: &'a str,
186 disabled: bool,
187 popup: bool,
188 parent: Option<ControlHandle>,
189}
190
191impl<'a> MenuBuilder<'a> {
192 pub fn text(mut self, text: &'a str) -> MenuBuilder<'a> {
193 self.text = text;
194 self
195 }
196
197 pub fn disabled(mut self, disabled: bool) -> MenuBuilder<'a> {
198 self.disabled = disabled;
199 self
200 }
201
202 pub fn popup(mut self, popup: bool) -> MenuBuilder<'a> {
203 self.popup = popup;
204 self
205 }
206
207 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuBuilder<'a> {
208 self.parent = Some(p.into());
209 self
210 }
211
212 pub fn build(self, menu: &mut Menu) -> Result<(), NwgError> {
213 if self.parent.is_none() {
214 return Err(NwgError::no_parent_menu());
215 }
216
217 menu.handle = ControlBase::build_hmenu()
218 .text(self.text)
219 .item(false)
220 .popup(self.popup)
221 .parent(self.parent.unwrap())
222 .build()?;
223
224 if self.disabled {
225 menu.set_enabled(false)
226 }
227
228 Ok(())
229 }
230}
231
232/**
233 A windows menu item. Can be added to a menubar or another menu.
234
235 Requires the `menu` feature.
236
237 **Builder parameters:**
238 - text: The text of the menu, including access key and shortcut label
239 - disabled: If the item can be selected by the user
240 - check: If the item should have a check mark next to it.
241 - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.
242
243 **Control events:**
244 - OnMenuItemSelected: When a menu item is selected. This can be done by clicking or using the hot-key.
245 - OnMenuHover: When the user hovers the menu
246
247
248 **Menu Access Keys**
249
250 Just like Menus, menu items can have access keys. An access key is an underlined letter in the text of a menu item.
251 When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
252 The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
253 A menu is active when it is displayed.
254
255 To create an access key for a menu item, precede any character in the item's text string with an ampersand.
256 For example, the text string "&Move" causes the system to underline the letter "M".
257
258 ```rust
259 use native_windows_gui2 as nwg;
260
261 fn menu_item(item: &mut nwg::MenuItem, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
262 nwg::MenuItem::builder()
263 .text("&Hello")
264 .disabled(true)
265 .parent(menu)
266 .build(item)
267 }
268 ```
269
270 **Shortcut Label**
271
272 A shortcut label like "Ctrl+O" can be added with the `text` field. By prefixing the shortcut with a tab character `\t` and
273 adding it as a suffix to the text label, it will be rendered right-aligned in the menu item.
274
275 For example, an "Exit" menu item could have both an access key "E" and a shortcut label "Alt+F4" with `text: "&Exit\tAlt+F4"`.
276
277 **note:** This will only add a text label to the menu item, the keyboard handling must be done through other means.
278
279 ```rust
280 use native_windows_gui2 as nwg;
281
282 fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
283 nwg::Menu::builder()
284 .text("&Exit\tAlt+F4")
285 .disabled(false)
286 .parent(window)
287 .build(menu)
288 }
289 ```
290*/
291#[derive(Default, Debug, PartialEq, Eq)]
292pub struct MenuItem {
293 pub handle: ControlHandle,
294}
295
296impl MenuItem {
297 pub fn builder<'a>() -> MenuItemBuilder<'a> {
298 MenuItemBuilder {
299 text: "Menu Item",
300 disabled: false,
301 check: false,
302 parent: None,
303 }
304 }
305
306 /// Return true if the control user can interact with the control, return false otherwise
307 pub fn enabled(&self) -> bool {
308 if self.handle.blank() {
309 panic!("{}", NOT_BOUND);
310 }
311 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
312
313 mh::is_menuitem_enabled(parent_handle, None, Some(id))
314 }
315
316 /// Enable or disable the control
317 pub fn set_enabled(&self, v: bool) {
318 if self.handle.blank() {
319 panic!("{}", NOT_BOUND);
320 }
321 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
322
323 mh::enable_menuitem(parent_handle, None, Some(id), v);
324 }
325
326 /// Sets the check state of a menu item
327 pub fn set_checked(&self, check: bool) {
328 if self.handle.blank() {
329 panic!("{}", NOT_BOUND);
330 }
331 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
332
333 mh::check_menu_item(parent_handle, id, check);
334 }
335
336 /// Returns the check state of a menu item
337 pub fn checked(&self) -> bool {
338 if self.handle.blank() {
339 panic!("{}", NOT_BOUND);
340 }
341 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
342
343 mh::menu_item_checked(parent_handle, id)
344 }
345}
346
347impl Drop for MenuItem {
348 fn drop(&mut self) {
349 self.handle.destroy();
350 }
351}
352
353pub struct MenuItemBuilder<'a> {
354 text: &'a str,
355 disabled: bool,
356 check: bool,
357 parent: Option<ControlHandle>,
358}
359
360impl<'a> MenuItemBuilder<'a> {
361 pub fn text(mut self, text: &'a str) -> MenuItemBuilder<'a> {
362 self.text = text;
363 self
364 }
365
366 pub fn disabled(mut self, disabled: bool) -> MenuItemBuilder<'a> {
367 self.disabled = disabled;
368 self
369 }
370
371 pub fn check(mut self, check: bool) -> MenuItemBuilder<'a> {
372 self.check = check;
373 self
374 }
375
376 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuItemBuilder<'a> {
377 self.parent = Some(p.into());
378 self
379 }
380
381 pub fn build(self, item: &mut MenuItem) -> Result<(), NwgError> {
382 if self.parent.is_none() {
383 return Err(NwgError::no_parent_menu());
384 }
385
386 item.handle = ControlBase::build_hmenu()
387 .text(self.text)
388 .item(true)
389 .parent(self.parent.unwrap())
390 .build()?;
391
392 if self.disabled {
393 item.set_enabled(false);
394 }
395
396 if self.check {
397 item.set_checked(true);
398 }
399
400 Ok(())
401 }
402}
403
404/**
405 A menu separator. Can be added between two menu item to separte them. Cannot be added to a menubar.
406
407 Requires the `menu` feature.
408
409 **Builder parameters:**
410 - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.
411
412 **Control events:**
413 - OnMenuHover: When the user hovers the menu
414
415 ```rust
416 use native_windows_gui2 as nwg;
417
418 fn separator(sep: &mut nwg::MenuSeparator, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
419 nwg::MenuSeparator::builder()
420 .parent(menu)
421 .build(sep)
422 }
423 ```
424*/
425#[derive(Default, Debug, PartialEq, Eq)]
426pub struct MenuSeparator {
427 pub handle: ControlHandle,
428}
429
430impl MenuSeparator {
431 pub fn builder() -> MenuSeparatorBuilder {
432 MenuSeparatorBuilder { parent: None }
433 }
434}
435
436pub struct MenuSeparatorBuilder {
437 parent: Option<ControlHandle>,
438}
439
440impl MenuSeparatorBuilder {
441 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuSeparatorBuilder {
442 self.parent = Some(p.into());
443 self
444 }
445
446 pub fn build(self, sep: &mut MenuSeparator) -> Result<(), NwgError> {
447 if self.parent.is_none() {
448 return Err(NwgError::no_parent_menu());
449 }
450
451 sep.handle = ControlBase::build_hmenu()
452 .separator(true)
453 .parent(self.parent.unwrap())
454 .build()?;
455
456 Ok(())
457 }
458}
459
460impl Drop for MenuSeparator {
461 fn drop(&mut self) {
462 self.handle.destroy();
463 }
464}