native_windows_gui/
events.rs

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