native_windows_gui/controls/menu.rs
1use crate::win32::menu as mh;
2use crate::NwgError;
3use super::{ControlBase, ControlHandle};
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_gui 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
102 pub fn builder<'a>() -> MenuBuilder<'a> {
103 MenuBuilder {
104 text: "Menu",
105 disabled: false,
106 popup: false,
107 parent: None
108 }
109 }
110
111 /// Return true if the control user can interact with the control, return false otherwise
112 pub fn enabled(&self) -> bool {
113 if self.handle.blank() { panic!("{}", NOT_BOUND); }
114 let (parent_handle, handle) = match self.handle {
115 ControlHandle::Menu(parent, menu) => (parent, menu),
116 ControlHandle::PopMenu(_, _) => { return true; },
117 _ => panic!("{}", BAD_HANDLE)
118 };
119
120 unsafe { mh::is_menu_enabled(parent_handle, handle) }
121 }
122
123 /// Enable or disable the control
124 /// A popup menu cannot be disabled
125 pub fn set_enabled(&self, v: bool) {
126 if self.handle.blank() { panic!("{}", NOT_BOUND); }
127 let (parent_handle, handle) = match self.handle {
128 ControlHandle::Menu(parent, menu) => (parent, menu),
129 ControlHandle::PopMenu(_, _) => { return; },
130 _ => panic!("{}", BAD_HANDLE)
131 };
132
133 unsafe { mh::enable_menu(parent_handle, handle, v); }
134 }
135
136 /// Show a popup menu as the selected position. Do nothing for menubar menu.
137 pub fn popup_with_flags(&self, x: i32, y: i32, flags: PopupMenuFlags) {
138 use winapi::um::winuser::{TrackPopupMenu, SetForegroundWindow};
139 use winapi::ctypes::c_int;
140
141 if self.handle.blank() { panic!("Menu is not bound"); }
142 let (parent_handle, handle) = match self.handle.pop_hmenu() {
143 Some(v) => v,
144 None => { return; }
145 };
146
147 unsafe {
148 SetForegroundWindow(parent_handle);
149 TrackPopupMenu(
150 handle,
151 flags.bits(),
152 x as c_int,
153 y as c_int,
154 0,
155 parent_handle,
156 ptr::null()
157 );
158 }
159 }
160
161 /// Show a popup menu as the selected position. Do nothing for menubar menu.
162 pub fn popup(&self, x: i32, y: i32) {
163 self.popup_with_flags(x, y, PopupMenuFlags::empty())
164 }
165
166}
167
168impl Drop for Menu {
169 fn drop(&mut self) {
170 self.handle.destroy();
171 }
172}
173
174pub struct MenuBuilder<'a> {
175 text: &'a str,
176 disabled: bool,
177 popup: bool,
178 parent: Option<ControlHandle>
179}
180
181impl<'a> MenuBuilder<'a> {
182
183 pub fn text(mut self, text: &'a str) -> MenuBuilder<'a> {
184 self.text = text;
185 self
186 }
187
188 pub fn disabled(mut self, disabled: bool) -> MenuBuilder<'a> {
189 self.disabled = disabled;
190 self
191 }
192
193 pub fn popup(mut self, popup: bool) -> MenuBuilder<'a> {
194 self.popup = popup;
195 self
196 }
197
198 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuBuilder<'a> {
199 self.parent = Some(p.into());
200 self
201 }
202
203 pub fn build(self, menu: &mut Menu) -> Result<(), NwgError> {
204 if self.parent.is_none() {
205 return Err(NwgError::no_parent_menu());
206 }
207
208 menu.handle = ControlBase::build_hmenu()
209 .text(self.text)
210 .item(false)
211 .popup(self.popup)
212 .parent(self.parent.unwrap())
213 .build()?;
214
215 if self.disabled {
216 menu.set_enabled(false)
217 }
218
219 Ok(())
220 }
221}
222
223
224/**
225 A windows menu item. Can be added to a menubar or another menu.
226
227 Requires the `menu` feature.
228
229 **Builder parameters:**
230 - text: The text of the menu, including access key and shortcut label
231 - disabled: If the item can be selected by the user
232 - check: If the item should have a check mark next to it.
233 - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.
234
235 **Control events:**
236 - OnMenuItemSelected: When a menu item is selected. This can be done by clicking or using the hot-key.
237 - OnMenuHover: When the user hovers the menu
238
239
240 **Menu Access Keys**
241
242 Just like Menus, menu items can have access keys. An access key is an underlined letter in the text of a menu item.
243 When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
244 The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
245 A menu is active when it is displayed.
246
247 To create an access key for a menu item, precede any character in the item's text string with an ampersand.
248 For example, the text string "&Move" causes the system to underline the letter "M".
249
250 ```rust
251 use native_windows_gui as nwg;
252
253 fn menu_item(item: &mut nwg::MenuItem, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
254 nwg::MenuItem::builder()
255 .text("&Hello")
256 .disabled(true)
257 .parent(menu)
258 .build(item)
259 }
260 ```
261
262 **Shortcut Label**
263
264 A shortcut label like "Ctrl+O" can be added with the `text` field. By prefixing the shortcut with a tab character `\t` and
265 adding it as a suffix to the text label, it will be rendered right-aligned in the menu item.
266
267 For example, an "Exit" menu item could have both an access key "E" and a shortcut label "Alt+F4" with `text: "&Exit\tAlt+F4"`.
268
269 **note:** This will only add a text label to the menu item, the keyboard handling must be done through other means.
270
271 ```rust
272 use native_windows_gui as nwg;
273
274 fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
275 nwg::Menu::builder()
276 .text("&Exit\tAlt+F4")
277 .disabled(false)
278 .parent(window)
279 .build(menu)
280 }
281 ```
282*/
283#[derive(Default, Debug, PartialEq, Eq)]
284pub struct MenuItem {
285 pub handle: ControlHandle
286}
287
288impl MenuItem {
289
290 pub fn builder<'a>() -> MenuItemBuilder<'a> {
291 MenuItemBuilder {
292 text: "Menu Item",
293 disabled: false,
294 check: false,
295 parent: None
296 }
297 }
298
299 /// Return true if the control user can interact with the control, return false otherwise
300 pub fn enabled(&self) -> bool {
301 if self.handle.blank() { panic!("{}", NOT_BOUND); }
302 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
303
304 unsafe { mh::is_menuitem_enabled(parent_handle, None, Some(id)) }
305 }
306
307 /// Enable or disable the control
308 pub fn set_enabled(&self, v: bool) {
309 if self.handle.blank() { panic!("{}", NOT_BOUND); }
310 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
311
312 unsafe { mh::enable_menuitem(parent_handle, None, Some(id), v); }
313 }
314
315 /// Sets the check state of a menu item
316 pub fn set_checked(&self, check: bool) {
317 if self.handle.blank() { panic!("{}", NOT_BOUND); }
318 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
319
320 unsafe { mh::check_menu_item(parent_handle, id, check); }
321 }
322
323 /// Returns the check state of a menu item
324 pub fn checked(&self) -> bool {
325 if self.handle.blank() { panic!("{}", NOT_BOUND); }
326 let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);
327
328 unsafe { mh::menu_item_checked(parent_handle, id) }
329 }
330
331}
332
333impl Drop for MenuItem {
334 fn drop(&mut self) {
335 self.handle.destroy();
336 }
337}
338
339pub struct MenuItemBuilder<'a> {
340 text: &'a str,
341 disabled: bool,
342 check: bool,
343 parent: Option<ControlHandle>
344}
345
346impl<'a> MenuItemBuilder<'a> {
347
348 pub fn text(mut self, text: &'a str) -> MenuItemBuilder<'a> {
349 self.text = text;
350 self
351 }
352
353 pub fn disabled(mut self, disabled: bool) -> MenuItemBuilder<'a> {
354 self.disabled = disabled;
355 self
356 }
357
358 pub fn check(mut self, check: bool) -> MenuItemBuilder<'a> {
359 self.check = check;
360 self
361 }
362
363 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuItemBuilder<'a> {
364 self.parent = Some(p.into());
365 self
366 }
367
368 pub fn build(self, item: &mut MenuItem) -> Result<(), NwgError> {
369 if self.parent.is_none() {
370 return Err(NwgError::no_parent_menu());
371 }
372
373 item.handle = ControlBase::build_hmenu()
374 .text(self.text)
375 .item(true)
376 .parent(self.parent.unwrap())
377 .build()?;
378
379 if self.disabled {
380 item.set_enabled(false);
381 }
382
383 if self.check {
384 item.set_checked(true);
385 }
386
387 Ok(())
388 }
389}
390
391/**
392 A menu separator. Can be added between two menu item to separte them. Cannot be added to a menubar.
393
394 Requires the `menu` feature.
395
396 **Builder parameters:**
397 - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.
398
399 **Control events:**
400 - OnMenuHover: When the user hovers the menu
401
402 ```rust
403 use native_windows_gui as nwg;
404
405 fn separator(sep: &mut nwg::MenuSeparator, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
406 nwg::MenuSeparator::builder()
407 .parent(menu)
408 .build(sep)
409 }
410 ```
411*/
412#[derive(Default, Debug, PartialEq, Eq)]
413pub struct MenuSeparator {
414 pub handle: ControlHandle
415}
416
417impl MenuSeparator {
418
419 pub fn builder() -> MenuSeparatorBuilder {
420 MenuSeparatorBuilder {
421 parent: None
422 }
423 }
424
425}
426
427pub struct MenuSeparatorBuilder {
428 parent: Option<ControlHandle>
429}
430
431impl MenuSeparatorBuilder {
432
433 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuSeparatorBuilder {
434 self.parent = Some(p.into());
435 self
436 }
437
438 pub fn build(self, sep: &mut MenuSeparator) -> Result<(), NwgError> {
439 if self.parent.is_none() {
440 return Err(NwgError::no_parent_menu());
441 }
442
443 sep.handle = ControlBase::build_hmenu()
444 .separator(true)
445 .parent(self.parent.unwrap())
446 .build()?;
447
448 Ok(())
449 }
450}
451
452impl Drop for MenuSeparator {
453 fn drop(&mut self) {
454 self.handle.destroy();
455 }
456}