native_windows_gui2/events.rs
1//! All the events that can be dispatched by the built-in controls of native-windows-gui
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4#[repr(u8)]
5pub enum MousePressEvent {
6 MousePressLeftUp,
7 MousePressLeftDown,
8 MousePressRightUp,
9 MousePressRightDown,
10}
11
12/// Events are identifiers that are sent by controls on user interaction
13/// Some events also have data that can be further processed by the event loop. See `EventData`
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15#[repr(usize)]
16pub enum Event {
17 /// Undefined / not implemented event. This can be dispatched by the bigger controls such as ListView and TreeView
18 Unknown,
19
20 /// Generic mouse press events that can be generated by most window controls
21 OnMousePress(MousePressEvent),
22
23 /// Generic mouse move event that can be generated by most window controls
24 OnMouseMove,
25
26 /// Generic mouse wheel event that can be generated by most window controls
27 /// Read the delta value with `EventData::OnMouseWheel` to check which key.
28 OnMouseWheel,
29
30 /// Generic window event when the user right clicks a window
31 OnContextMenu,
32
33 /// When a top level window control is created.
34 OnInit,
35
36 /// When a control needs to be redrawn
37 OnPaint,
38
39 /// When a key is pressed on a keyboard. Unlike OnKeyDown, this returns a char (ex: 'c') in a EventData::OnChar.
40 OnChar,
41
42 /// When a key is pressed on a keyboard. Use `EventData::OnKey` to check which key.
43 OnKeyPress,
44
45 /// When a key is released on a keyboard. Use EventData::OnKey to check which key.
46 OnKeyRelease,
47
48 /// When a system key is pressed on a keyboard. Use `EventData::OnKey` to check which key.
49 OnSysKeyPress,
50
51 /// When a system key is released on a keyboard. Use EventData::OnKey to check which key.
52 OnSysKeyRelease,
53
54 /// When Enter is pressed.
55 OnKeyEnter,
56
57 /// When Esc key is pressed.
58 OnKeyEsc,
59
60 /// Sent to a window when the size or position of the window is about to change.
61 /// An application can use the event data `EventData::OnMinMaxInfo` to override the minimum or maximum size.
62 OnMinMaxInfo,
63
64 /// When a control is resized by the user.
65 /// This is typically applied to top level windows but it also applies to children when layouts are used.
66 OnResize,
67
68 /// When a control is about to be resized by the user.
69 /// This does not trigger on maximize
70 OnResizeBegin,
71
72 /// When a control stops being resized
73 /// This does not trigger on maximize
74 OnResizeEnd,
75
76 // When a window control is maximized
77 OnWindowMaximize,
78
79 // When a window control is minimized
80 OnWindowMinimize,
81
82 /// When a control is moved by the user. This is typically applied to top level windows.
83 /// This is typically applied to top level windows but it also applies to children when layouts are used.
84 OnMove,
85
86 /// When a bar-like control value is changed.
87 OnVerticalScroll,
88
89 /// When a bar-like control value is changed.
90 OnHorizontalScroll,
91
92 /// When a file is dropped into a control
93 OnFileDrop,
94
95 /// When a button is clicked. Similar to a MouseUp event, but only for button control
96 OnButtonClick,
97
98 /// When a button is clicked twice rapidly
99 OnButtonDoubleClick,
100
101 /// When a label is clicked
102 OnLabelClick,
103
104 /// When a label is clicked twice rapidly
105 OnLabelDoubleClick,
106
107 /// When an ImageFrame is clicked
108 OnImageFrameClick,
109
110 /// When an ImageFrame is clicked twice rapidly
111 OnImageFrameDoubleClick,
112
113 /// When TextInput value is changed
114 OnTextInput,
115
116 /// When the list of a combobox is closed
117 OnComboBoxClosed,
118
119 /// When the list of a combobox is about to be visible
120 OnComboBoxDropdown,
121
122 /// When the current selection of the combobox was changed
123 OnComboxBoxSelection,
124
125 /// When the date select dropdown is expanded
126 OnDatePickerDropdown,
127
128 /// When the date select dropdown is closed
129 OnDatePickerClosed,
130
131 /// When the value of the date select is changed
132 OnDatePickerChanged,
133
134 /// When an item on a list box is clicked twice
135 OnListBoxDoubleClick,
136
137 /// When an item on a list box is selected
138 OnListBoxSelect,
139
140 /// The selected tab of a TabsContainer changed
141 TabsContainerChanged,
142
143 /// The selected tab of a TabsContainer is about to be changed
144 TabsContainerChanging,
145
146 /// When the trackbar thumb is released by the user
147 TrackBarUpdated,
148
149 /// When a menu control is opened
150 OnMenuOpen,
151
152 /// When the user enters the menu modal loop
153 OnMenuEnter,
154
155 /// When the user exits the menu modal loop
156 OnMenuExit,
157
158 /// When a menu is hovered (either through mouse or keyboard)
159 OnMenuHover,
160
161 /// When the user selects on a menu item
162 OnMenuItemSelected,
163
164 /// When the user hovers over a callback tooltip
165 /// The callback will also receive a `EventData::OnTooltipText`
166 OnTooltipText,
167
168 /// When the user has clicked the left mouse button within the control.
169 OnTreeViewClick,
170
171 /// When the user has clicked the left mouse button within the control twice rapidly.
172 OnTreeViewDoubleClick,
173
174 /// When the user has clicked the right mouse button within the control.
175 OnTreeViewRightClick,
176
177 /// When begins in-place editing of the specified item's text.
178 OnTreeViewBeginItemEdit,
179
180 /// When ends the editing of a treeview item's label.
181 OnTreeViewEndItemEdit,
182
183 /// When the control has lost the input focus
184 OnTreeFocusLost,
185
186 /// When the control has acquired the input focus
187 OnTreeFocus,
188
189 /// When an item is removed from the treeview. The item being deleted is passed in `EventData::OnTreeItemDelete`
190 OnTreeItemDelete,
191
192 /// When an item is expanded. Generates a `EventData::OnTreeItemDelete`
193 OnTreeItemExpanded,
194
195 /// When the state of a tree item is changed.
196 OnTreeItemChanged,
197
198 /// When the selected tree item is changed.
199 OnTreeItemSelectionChanged,
200
201 /// When all the items in a list view are destroyed
202 /// Do not add, delete, or rearrange items in the list view while processing this notification code.
203 OnListViewClear,
204
205 /// When an item is about to be removed from the list view
206 /// Do not add, delete, or rearrange items in the list view while processing this notification code.
207 /// Generates an `EventData::ListViewItemIndex`
208 OnListViewItemRemoved,
209
210 /// When a new item is inserted in the list view
211 /// This is only triggered when an ietm is added to a new ROW
212 OnListViewItemInsert,
213
214 /// When an item in the list view is activated by the user
215 /// An item is activated when the user clicks it twice
216 /// Generates an `EventData::ListViewItemIndex`
217 OnListViewItemActivated,
218
219 /// When the user has clicked the left mouse button within the control
220 /// Generates an `EventData::ListViewItemIndex`
221 OnListViewClick,
222
223 /// When the user has clicked the right mouse button within the control
224 /// Generates an `EventData::ListViewItemIndex`
225 OnListViewRightClick,
226
227 /// When the user has clicked the left mouse button within the control twice rapidly
228 /// Generates an `EventData::ListViewItemIndex`
229 OnListViewDoubleClick,
230
231 /// When the user has clicked the left mouse button on ListView header column
232 /// Generates an `EventData::ListViewItemIndex`
233 OnListViewColumnClick,
234
235 /// When an item is selected/unselected in the listview
236 /// See `EventData::OnListViewItemChanged` to differentiate the two
237 OnListViewItemChanged,
238
239 /// When the control has acquired the input focus
240 OnListViewFocus,
241
242 /// When the control has lost the input focus
243 OnListViewFocusLost,
244
245 /// When a TrayNotification info popup (not the tooltip) is shown
246 OnTrayNotificationShow,
247
248 /// When a TrayNotification info popup (not the tooltip) is hidden
249 OnTrayNotificationHide,
250
251 /// When a TrayNotification is closed due to a timeout
252 OnTrayNotificationTimeout,
253
254 /// When a TrayNotification is closed due to a user click
255 OnTrayNotificationUserClose,
256
257 /// When a timer delay is elapsed
258 OnTimerTick,
259
260 /// When a timer end condition is reached
261 OnTimerStop,
262
263 /// When a notice is... noticed
264 OnNotice,
265
266 /// When a user clicks on the X button of a window
267 OnWindowClose,
268}
269
270/// Events data sent by the controls.
271#[derive(Debug)]
272pub enum EventData {
273 /// The event has no data
274 NoData,
275
276 /// Sets if the window should be closed after the event
277 OnWindowClose(WindowCloseData),
278
279 /// Contains the default maximized position and dimensions, and the default minimum and maximum tracking sizes.
280 /// An application can override the defaults by setting the members of this event.
281 OnMinMaxInfo(MinMaxInfo),
282
283 /// Sets the text of a tooltip.
284 /// The method `on_tooltip_text` should be used to access the inner data
285 OnTooltipText(ToolTipTextData),
286
287 /// The character entered by a user by an `OnChar` event
288 OnChar(char),
289
290 /// The windows key code entered by a user. See the `nwg::keys` module
291 OnKey(u32),
292
293 /// Hold resources that will most likely be used during painting.
294 OnPaint(PaintData),
295
296 /// The delta value of a mouse wheel event. A positive value indicates that the wheel was rotated to the right;
297 /// a negative value indicates that the wheel was rotated to the left.
298 OnMouseWheel(i32),
299
300 /// The path to one or more files that were dropped in the application
301 OnFileDrop(DropFiles),
302
303 /// The handle to the item being deleted. The item is still valid.
304 #[cfg(feature = "tree-view")]
305 OnTreeItemDelete(crate::TreeItem),
306
307 /// The handle to the item being changed.
308 #[cfg(feature = "tree-view")]
309 OnTreeItemUpdate {
310 item: crate::TreeItem,
311 action: crate::TreeItemAction,
312 },
313
314 /// When ends the editing of a treeview item's label.
315 #[cfg(feature = "tree-view")]
316 OnTreeViewEndItemEdit { f_cancel: bool, new_text: String },
317
318 /// The handles the the old item and the new item.
319 #[cfg(feature = "tree-view")]
320 OnTreeItemSelectionChanged {
321 old: crate::TreeItem,
322 new: crate::TreeItem,
323 },
324
325 /// Row index and column index of the list view item that raised the event
326 /// `row_index` `0xFFF...` means the absence of an item
327 #[cfg(feature = "list-view")]
328 OnListViewItemIndex {
329 row_index: usize,
330 column_index: usize,
331 },
332
333 /// Row index, column index, and selected state of the list view item that raised the event
334 #[cfg(feature = "list-view")]
335 OnListViewItemChanged {
336 row_index: usize,
337 column_index: usize,
338 selected: bool,
339 },
340}
341
342impl EventData {
343 /// Unwraps event data into a `&PaintData`. Panics if it's not the right type.
344 pub fn on_paint(&self) -> &PaintData {
345 match self {
346 EventData::OnPaint(p) => p,
347 d => panic!("Wrong data type: {:?}", d),
348 }
349 }
350
351 /// Unwraps event data into a `&MinMaxInfo`. Panics if it's not the right type.
352 pub fn on_min_max(&self) -> &MinMaxInfo {
353 match self {
354 EventData::OnMinMaxInfo(i) => i,
355 d => panic!("Wrong data type: {:?}", d),
356 }
357 }
358
359 /// Unwraps event data into a `char`. Panics if it's not the right type.
360 pub fn on_char(&self) -> char {
361 match self {
362 EventData::OnChar(c) => *c,
363 d => panic!("Wrong data type: {:?}", d),
364 }
365 }
366
367 /// Unwraps event data into a `&ToolTipTextData`. Panics if it's not the right type.
368 pub fn on_tooltip_text(&self) -> &ToolTipTextData {
369 match self {
370 EventData::OnTooltipText(d) => d,
371 d => panic!("Wrong data type: {:?}", d),
372 }
373 }
374
375 /// Unwraps event data into a `&DragData`. Panics if it's not the right type.
376 pub fn on_file_drop(&self) -> &DropFiles {
377 match self {
378 EventData::OnFileDrop(d) => d,
379 d => panic!("Wrong data type: {:?}", d),
380 }
381 }
382
383 /// Unwraps event data into the virtual key code for `OnKeyPress` and `OnKeyRelease`
384 pub fn on_key(&self) -> u32 {
385 match self {
386 EventData::OnKey(key) => *key,
387 d => panic!("Wrong data type: {:?}", d),
388 }
389 }
390
391 /// unwraps event data into the removed tree item
392 #[cfg(feature = "tree-view")]
393 pub fn on_tree_item_delete(&self) -> &crate::TreeItem {
394 match self {
395 EventData::OnTreeItemDelete(item) => item,
396 d => panic!("Wrong data type: {:?}", d),
397 }
398 }
399
400 /// unwraps event data into the update tree view item and the action
401 #[cfg(feature = "tree-view")]
402 pub fn on_tree_item_update(&self) -> (&crate::TreeItem, crate::TreeItemAction) {
403 match self {
404 EventData::OnTreeItemUpdate { item, action } => (item, *action),
405 d => panic!("Wrong data type: {:?}", d),
406 }
407 }
408
409 /// unwraps event data into the removed tree item
410 #[cfg(feature = "tree-view")]
411 pub fn on_tree_item_selection_changed(&self) -> (&crate::TreeItem, &crate::TreeItem) {
412 match self {
413 EventData::OnTreeItemSelectionChanged { old, new } => (old, new),
414 d => panic!("Wrong data type: {:?}", d),
415 }
416 }
417
418 /// unwraps event data into f_cancel, new_text.
419 /// f_cancel indicates the editing is cancel or not.
420 /// new_text is the new input text when editing is not cancel.
421 #[cfg(feature = "tree-view")]
422 pub fn on_tree_view_end_item_edit(&self) -> (bool, String) {
423 match self {
424 EventData::OnTreeViewEndItemEdit { f_cancel, new_text } => {
425 (*f_cancel, new_text.to_string())
426 }
427 d => panic!("Wrong data type: {:?}", d),
428 }
429 }
430
431 /// unwraps event data into the indices of a list view index (row_index, column_index)
432 #[cfg(feature = "list-view")]
433 pub fn on_list_view_item_index(&self) -> (usize, usize) {
434 match self {
435 &EventData::OnListViewItemIndex {
436 row_index,
437 column_index,
438 } => (row_index, column_index),
439 d => panic!("Wrong data type: {:?}", d),
440 }
441 }
442
443 /// unwraps event data into the indices of a list view index (row_index, column_index, selected)
444 #[cfg(feature = "list-view")]
445 pub fn on_list_view_item_changed(&self) -> (usize, usize, bool) {
446 match self {
447 &EventData::OnListViewItemChanged {
448 row_index,
449 column_index,
450 selected,
451 } => (row_index, column_index, selected),
452 d => panic!("Wrong data type: {:?}", d),
453 }
454 }
455}
456
457//
458// Events data structures
459//
460
461use std::fmt;
462use winapi::shared::windef::{HWND, POINT};
463use winapi::um::commctrl::NMTTDISPINFOW;
464use winapi::um::shellapi::{DragFinish, HDROP};
465use winapi::um::winuser::{BeginPaint, EndPaint, MINMAXINFO, PAINTSTRUCT};
466
467/// A wrapper structure that sets the tooltip text on an `OnTooltipText` callback
468pub struct ToolTipTextData {
469 pub(crate) data: *mut NMTTDISPINFOW,
470}
471
472impl ToolTipTextData {
473 /// Tells the application to save the text value of the callback
474 /// The `OnTooltipText` will not be called a second time for the associated control
475 pub fn keep(&self, keep: bool) {
476 use ::winapi::um::commctrl::TTF_DI_SETITEM;
477
478 let data = unsafe { &mut *self.data };
479 match keep {
480 true => {
481 data.uFlags |= TTF_DI_SETITEM;
482 }
483 false => {
484 data.uFlags &= !TTF_DI_SETITEM;
485 }
486 }
487 }
488
489 /// Sets the text of the callback. This function will copy the text.
490 /// WINAPI does not easily allow tooltips with more than 79 characters (80 with NULL)
491 /// With a text > 79 characters, this method will do nothing.
492 pub fn set_text<'b>(&self, text: &'b str) {
493 use crate::win32::base_helper::to_utf16;
494 use std::ptr;
495
496 let text_len = text.len();
497 if text_len > 79 {
498 return;
499 }
500
501 self.clear();
502 unsafe {
503 let data = &mut *self.data;
504 let local_text = to_utf16(text);
505 ptr::copy_nonoverlapping(local_text.as_ptr(), data.szText.as_mut_ptr(), text_len);
506 }
507 }
508
509 fn clear(&self) {
510 use std::{mem, ptr};
511 use winapi::um::winnt::WCHAR;
512
513 unsafe {
514 let data = &mut *self.data;
515 ptr::write(&mut data.szText as *mut [WCHAR; 80], mem::zeroed());
516 }
517 }
518}
519
520impl fmt::Debug for ToolTipTextData {
521 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522 write!(f, "ToolTipTextData")
523 }
524}
525
526/// Opaque type that manages if a window should be closed after an OnClose event
527pub struct WindowCloseData {
528 pub(crate) data: *mut bool,
529}
530
531impl WindowCloseData {
532 /// Sets if the window should close after the event
533 pub fn close(&self, value: bool) {
534 unsafe {
535 *self.data = value;
536 }
537 }
538
539 /// Returns true if the window will close after the event or false otherwise
540 pub fn closing(&self) -> bool {
541 unsafe { *self.data }
542 }
543}
544
545impl fmt::Debug for WindowCloseData {
546 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
547 write!(f, "WindowCloseData({})", self.closing())
548 }
549}
550
551/// Opaque type over a paint event's data
552#[derive(Debug)]
553pub struct PaintData {
554 pub(crate) hwnd: HWND,
555}
556
557impl PaintData {
558 /// Wrapper over BeginPaint
559 pub fn begin_paint(&self) -> PAINTSTRUCT {
560 unsafe {
561 let mut paint: PAINTSTRUCT = ::std::mem::zeroed();
562 BeginPaint(self.hwnd, &mut paint);
563 paint
564 }
565 }
566
567 /// Wrapper over EndPaint
568 pub fn end_paint(&self, p: &PAINTSTRUCT) {
569 unsafe {
570 EndPaint(self.hwnd, p);
571 }
572 }
573}
574
575/// Opaque type over one or more dragged files.
576pub struct DropFiles {
577 pub(crate) drop: HDROP,
578}
579
580impl DropFiles {
581 /// Retrieves the position of the mouse pointer at the time a file was dropped during a drag-and-drop operation.
582 /// The coordinates are local to the control. Ex: (0, 0) is the top left corner of the control.
583 pub fn point(&self) -> [i32; 2] {
584 use winapi::um::shellapi::DragQueryPoint;
585
586 unsafe {
587 let mut pt = POINT { x: 0, y: 0 };
588 DragQueryPoint(self.drop, &mut pt);
589 [pt.x, pt.y]
590 }
591 }
592
593 /// Return the number of files dropped
594 pub fn len(&self) -> usize {
595 use std::ptr;
596 use winapi::um::shellapi::DragQueryFileW;
597
598 unsafe { DragQueryFileW(self.drop, 0xFFFFFFFF, ptr::null_mut(), 0) as usize }
599 }
600
601 /// Return the files path dropped into the app
602 pub fn files(&self) -> Vec<String> {
603 use crate::win32::base_helper::from_utf16;
604 use std::ptr;
605 use winapi::um::shellapi::DragQueryFileW;
606
607 let len = self.len();
608 let mut files = Vec::with_capacity(len);
609 unsafe {
610 for i in 0..len {
611 // Need to add a +1 here for some reason
612 let buffer_size =
613 (DragQueryFileW(self.drop, i as _, ptr::null_mut(), 0) + 1) as usize;
614
615 let mut buffer: Vec<u16> = Vec::with_capacity(buffer_size);
616 buffer.set_len(buffer_size);
617
618 DragQueryFileW(self.drop, i as _, buffer.as_mut_ptr(), buffer_size as _);
619
620 files.push(from_utf16(&buffer));
621 }
622 }
623
624 files
625 }
626}
627
628impl fmt::Debug for DropFiles {
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 write!(
631 f,
632 "DragData {{ point: {:?}, files: {:?} }}",
633 self.point(),
634 self.files()
635 )
636 }
637}
638
639impl Drop for DropFiles {
640 fn drop(&mut self) {
641 if !self.drop.is_null() {
642 unsafe { DragFinish(self.drop) }
643 }
644 }
645}
646
647pub struct MinMaxInfo {
648 pub(crate) inner: *mut MINMAXINFO,
649}
650
651impl MinMaxInfo {
652 /// The maximized width and the maximized height of the window. For top-level windows, this value is based on the width of the primary monitor.
653 pub fn set_maximized_size(&self, width: i32, height: i32) {
654 let info = unsafe { &mut *self.inner };
655 let (x, y) = crate::win32::high_dpi::logical_to_physical(width as i32, height as i32);
656 info.ptMaxSize = POINT { x, y };
657 }
658
659 /// Returns the maximized width and the maximized height of the window. For top-level windows, this value is based on the width of the primary monitor.
660 pub fn maximized_size(&self) -> [i32; 2] {
661 let info = unsafe { &mut *self.inner };
662 let (w, h) =
663 crate::win32::high_dpi::physical_to_logical(info.ptMaxSize.x, info.ptMaxSize.y);
664 [w, h]
665 }
666
667 /// Sets the position of the left side of the maximized window and the position of the top of the maximized window. For top-level windows, this value is based on the position of the primary monitor.
668 pub fn set_maximized_pos(&self, x: i32, y: i32) {
669 let info = unsafe { &mut *self.inner };
670 let (x, y) = crate::win32::high_dpi::logical_to_physical(x, y);
671 info.ptMaxPosition = POINT { x, y };
672 }
673
674 /// Returns the position of the left side of the maximized window and the position of the top of the maximized window. For top-level windows, this value is based on the position of the primary monitor.
675 pub fn maximized_pos(&self) -> [i32; 2] {
676 let info = unsafe { &mut *self.inner };
677 let (x, y) =
678 crate::win32::high_dpi::physical_to_logical(info.ptMaxPosition.x, info.ptMaxPosition.y);
679
680 [x, y]
681 }
682
683 /// Sets the maximum size of the window
684 pub fn set_max_size(&self, width: i32, height: i32) {
685 let info = unsafe { &mut *self.inner };
686 let (x, y) = crate::win32::high_dpi::logical_to_physical(width, height);
687 info.ptMaxTrackSize = POINT { x, y };
688 }
689
690 /// Returns the maximum size of the window
691 pub fn max_size(&self) -> [i32; 2] {
692 let info = unsafe { &mut *self.inner };
693 let (w, h) = crate::win32::high_dpi::physical_to_logical(
694 info.ptMaxTrackSize.x,
695 info.ptMaxTrackSize.y,
696 );
697 [w, h]
698 }
699
700 /// Sets the maximum size of the window
701 pub fn set_min_size(&self, width: i32, height: i32) {
702 let info = unsafe { &mut *self.inner };
703 let (x, y) = crate::win32::high_dpi::logical_to_physical(width as i32, height as i32);
704 info.ptMinTrackSize = POINT { x, y };
705 }
706
707 /// Returns the minimum size of the window
708 pub fn min_size(&self) -> [i32; 2] {
709 let info = unsafe { &mut *self.inner };
710 let (w, h) = crate::win32::high_dpi::physical_to_logical(
711 info.ptMinTrackSize.x,
712 info.ptMinTrackSize.y,
713 );
714 [w, h]
715 }
716}
717
718impl fmt::Debug for MinMaxInfo {
719 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
720 write!(
721 f,
722 "MinMaxInfo {{ maximized_size: {:?}, maximized_pos: {:?}, max_size: {:?}, min_size: {:?} }}",
723 self.maximized_size(),
724 self.maximized_pos(),
725 self.max_size(),
726 self.min_size()
727 )
728 }
729}