Skip to main content

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}