1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
//! All the events that can be dispatched by the built-in controls of native-windows-gui


#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum MousePressEvent {
    MousePressLeftUp,
    MousePressLeftDown,
    MousePressRightUp,
    MousePressRightDown
}

/// Events are identifiers that are sent by controls on user interaction
/// Some events also have data that can be further processed by the event loop. See `EventData`
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(usize)]
pub enum Event {
    /// Undefined / not implemented event. This can be dispatched by the bigger controls such as ListView and TreeView
    Unknown,

    /// Generic mouse press events that can be generated by most window controls
    OnMousePress(MousePressEvent),

    /// Generic mouse move event that can be generated by most window controls
    OnMouseMove,

    /// Generic mouse wheel event that can be generated by most window controls
    /// Read the delta value with `EventData::OnMouseWheel` to check which key.
    OnMouseWheel,

    /// Generic window event when the user right clicks a window
    OnContextMenu,

    /// When a top level window control is created.
    OnInit,

    /// When a control needs to be redrawn
    OnPaint,

    /// When a key is pressed on a keyboard. Unlike OnKeyDown, this returns a char (ex: 'c') in a EventData::OnChar.
    OnChar,

    /// When a key is pressed on a keyboard. Use `EventData::OnKey` to check which key.
    OnKeyPress,

    /// When a key is released on a keyboard. Use EventData::OnKey to check which key.
    OnKeyRelease,

    /// When a system key is pressed on a keyboard. Use `EventData::OnKey` to check which key.
    OnSysKeyPress,

    /// When a system key is released on a keyboard. Use EventData::OnKey to check which key.
    OnSysKeyRelease,
    
    /// When Enter is pressed.
    OnKeyEnter,

    /// When Esc key is pressed.
    OnKeyEsc,

    /// Sent to a window when the size or position of the window is about to change. 
    /// An application can use the event data `EventData::OnMinMaxInfo` to override the minimum or maximum size.
    OnMinMaxInfo,
    
    /// When a control is resized by the user. 
    /// This is typically applied to top level windows but it also applies to children when layouts are used.
    OnResize,

    /// When a control is about to be resized by the user. 
    /// This does not trigger on maximize
    OnResizeBegin,

    /// When a control stops being resized
    /// This does not trigger on maximize
    OnResizeEnd,

    // When a window control is maximized
    OnWindowMaximize,

    // When a window control is minimized
    OnWindowMinimize,

    /// When a control is moved by the user. This is typically applied to top level windows.
    /// This is typically applied to top level windows but it also applies to children when layouts are used.
    OnMove,

    /// When a bar-like control value is changed.
    OnVerticalScroll,

    /// When a bar-like control value is changed.
    OnHorizontalScroll,

    /// When a file is dropped into a control
    OnFileDrop,

    /// When a button is clicked. Similar to a MouseUp event, but only for button control
    OnButtonClick,

    /// When a button is clicked twice rapidly
    OnButtonDoubleClick,

    /// When a label is clicked
    OnLabelClick,

    /// When a label is clicked twice rapidly
    OnLabelDoubleClick,

    /// When an ImageFrame is clicked
    OnImageFrameClick,

    /// When an ImageFrame is clicked twice rapidly
    OnImageFrameDoubleClick,

    /// When TextInput value is changed
    OnTextInput,

    /// When the list of a combobox is closed
    OnComboBoxClosed,

    /// When the list of a combobox is about to be visible
    OnComboBoxDropdown,

    /// When the current selection of the combobox was changed
    OnComboxBoxSelection,

    /// When the date select dropdown is expanded
    OnDatePickerDropdown,

    /// When the date select dropdown is closed
    OnDatePickerClosed,

    /// When the value of the date select is changed
    OnDatePickerChanged,

    /// When an item on a list box is clicked twice
    OnListBoxDoubleClick,

    /// When an item on a list box is selected
    OnListBoxSelect,

    /// The selected tab of a TabsContainer changed
    TabsContainerChanged,

    /// The selected tab of a TabsContainer is about to be changed
    TabsContainerChanging,

    /// When the trackbar thumb is released by the user
    TrackBarUpdated,

    /// When a menu control is opened
    OnMenuOpen,

    /// When the user enters the menu modal loop
    OnMenuEnter,

    /// When the user exits the menu modal loop
    OnMenuExit,

    /// When a menu is hovered (either through mouse or keyboard)
    OnMenuHover,

    /// When the user selects on a menu item
    OnMenuItemSelected,

    /// When the user hovers over a callback tooltip
    /// The callback will also receive a `EventData::OnTooltipText`
    OnTooltipText,

    /// When the user has clicked the left mouse button within the control.
    OnTreeViewClick,

    /// When the user has clicked the left mouse button within the control twice rapidly.
    OnTreeViewDoubleClick,

    /// When the user has clicked the right mouse button within the control.
    OnTreeViewRightClick,

    /// When begins in-place editing of the specified item's text.
    OnTreeViewBeginItemEdit,

    /// When ends the editing of a treeview item's label.
    OnTreeViewEndItemEdit,

    /// When the control has lost the input focus
    OnTreeFocusLost,

    /// When the control has acquired the input focus
    OnTreeFocus,

    /// When an item is removed from the treeview. The item being deleted is passed in `EventData::OnTreeItemDelete`
    OnTreeItemDelete,

    /// When an item is expanded. Generates a `EventData::OnTreeItemDelete`
    OnTreeItemExpanded,

    /// When the state of a tree item is changed.
    OnTreeItemChanged,

    /// When the selected tree item is changed.
    OnTreeItemSelectionChanged,

    /// When all the items in a list view are destroyed
    /// Do not add, delete, or rearrange items in the list view while processing this notification code.
    OnListViewClear,

    /// When an item is about to be removed from the list view
    /// Do not add, delete, or rearrange items in the list view while processing this notification code.
    /// Generates an `EventData::ListViewItemIndex`
    OnListViewItemRemoved,

    /// When a new item is inserted in the list view
    /// This is only triggered when an ietm is added to a new ROW
    OnListViewItemInsert,

    /// When an item in the list view is activated by the user
    /// An item is activated when the user clicks it twice
    /// Generates an `EventData::ListViewItemIndex`
    OnListViewItemActivated,

    /// When the user has clicked the left mouse button within the control
    /// Generates an `EventData::ListViewItemIndex`
    OnListViewClick,

    /// When the user has clicked the right mouse button within the control
    /// Generates an `EventData::ListViewItemIndex`
    OnListViewRightClick,

    /// When the user has clicked the left mouse button within the control twice rapidly
    /// Generates an `EventData::ListViewItemIndex`
    OnListViewDoubleClick,

    /// When the user has clicked the left mouse button on ListView header column
    /// Generates an `EventData::ListViewItemIndex`
    OnListViewColumnClick,

    /// When an item is selected/unselected in the listview
    /// See `EventData::OnListViewItemChanged` to differentiate the two
    OnListViewItemChanged,

    /// When the control has acquired the input focus
    OnListViewFocus,

    /// When the control has lost the input focus
    OnListViewFocusLost,

    /// When a TrayNotification info popup (not the tooltip) is shown 
    OnTrayNotificationShow,

    /// When a TrayNotification info popup (not the tooltip) is hidden 
    OnTrayNotificationHide,

    /// When a TrayNotification is closed due to a timeout
    OnTrayNotificationTimeout,

    /// When a TrayNotification is closed due to a user click
    OnTrayNotificationUserClose,

    /// When a timer delay is elapsed
    OnTimerTick,

    /// When a timer end condition is reached
    OnTimerStop,

    /// When a notice is... noticed
    OnNotice,

    /// When a user clicks on the X button of a window
    OnWindowClose,
}


/// Events data sent by the controls. 
#[derive(Debug)]
pub enum EventData {
    /// The event has no data
    NoData,

    /// Sets if the window should be closed after the event
    OnWindowClose(WindowCloseData),

    /// Contains the default maximized position and dimensions, and the default minimum and maximum tracking sizes. 
    /// An application can override the defaults by setting the members of this event.
    OnMinMaxInfo(MinMaxInfo),

    /// Sets the text of a tooltip.
    /// The method `on_tooltip_text` should be used to access the inner data
    OnTooltipText(ToolTipTextData),

    /// The character entered by a user by an `OnChar` event
    OnChar(char),

    /// The windows key code entered by a user. See the `nwg::keys` module
    OnKey(u32),

    /// Hold resources that will most likely be used during painting. 
    OnPaint(PaintData),

    /// The delta value of a mouse wheel event. A positive value indicates that the wheel was rotated to the right; 
    /// a negative value indicates that the wheel was rotated to the left.
    OnMouseWheel(i32),

    /// The path to one or more files that were dropped in the application
    OnFileDrop(DropFiles),

    /// The handle to the item being deleted. The item is still valid.
    #[cfg(feature="tree-view")]
    OnTreeItemDelete(crate::TreeItem),

    /// The handle to the item being changed.
    #[cfg(feature="tree-view")]
    OnTreeItemUpdate{ item: crate::TreeItem, action: crate::TreeItemAction },
    
    /// When ends the editing of a treeview item's label.
    #[cfg(feature="tree-view")]
    OnTreeViewEndItemEdit{ f_cancel: bool, new_text: String },

    /// The handles the the old item and the new item.
    #[cfg(feature="tree-view")]
    OnTreeItemSelectionChanged{ old: crate::TreeItem, new: crate::TreeItem },

    /// Row index and column index of the list view item that raised the event
    /// `row_index` `0xFFF...` means the absence of an item
    #[cfg(feature="list-view")]
    OnListViewItemIndex { row_index: usize, column_index: usize },

    /// Row index, column index, and selected state of the list view item that raised the event
    #[cfg(feature="list-view")]
    OnListViewItemChanged { row_index: usize, column_index: usize, selected: bool },
}

impl EventData {

    /// Unwraps event data into a `&PaintData`. Panics if it's not the right type.
    pub fn on_paint(&self) -> &PaintData {
        match self {
            EventData::OnPaint(p) => p,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// Unwraps event data into a `&MinMaxInfo`. Panics if it's not the right type.
    pub fn on_min_max(&self) -> &MinMaxInfo {
        match self {
            EventData::OnMinMaxInfo(i) => i,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// Unwraps event data into a `char`. Panics if it's not the right type.
    pub fn on_char(&self) -> char {
        match self {
            EventData::OnChar(c) => *c,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// Unwraps event data into a `&ToolTipTextData`. Panics if it's not the right type.
    pub fn on_tooltip_text(&self) -> &ToolTipTextData {
        match self {
            EventData::OnTooltipText(d) => d,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// Unwraps event data into a `&DragData`. Panics if it's not the right type.
    pub fn on_file_drop(&self) -> &DropFiles {
        match self {
            EventData::OnFileDrop(d) => d,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// Unwraps event data into the virtual key code for `OnKeyPress` and `OnKeyRelease`
    pub fn on_key(&self) -> u32 {
        match self {
            EventData::OnKey(key) => *key,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// unwraps event data into the removed tree item
    #[cfg(feature="tree-view")]
    pub fn on_tree_item_delete(&self) -> &crate::TreeItem {
        match self {
            EventData::OnTreeItemDelete(item) => item,
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// unwraps event data into the update tree view item and the action
    #[cfg(feature="tree-view")]
    pub fn on_tree_item_update(&self) -> (&crate::TreeItem, crate::TreeItemAction) {
        match self {
            EventData::OnTreeItemUpdate { item, action } => (item, *action),
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// unwraps event data into the removed tree item
    #[cfg(feature="tree-view")]
    pub fn on_tree_item_selection_changed(&self) -> (&crate::TreeItem, &crate::TreeItem) {
        match self {
            EventData::OnTreeItemSelectionChanged { old, new } => (old, new),
            d => panic!("Wrong data type: {:?}", d)
        }
    }
    
    /// unwraps event data into f_cancel, new_text.
    /// f_cancel indicates the editing is cancel or not.
    /// new_text is the new input text when editing is not cancel.
    #[cfg(feature="tree-view")]
    pub fn on_tree_view_end_item_edit(&self) -> (bool, String) {
        match self {
            EventData::OnTreeViewEndItemEdit { f_cancel, new_text} => (*f_cancel, new_text.to_string()),
            d => panic!("Wrong data type: {:?}", d)
        }
    }
    
    /// unwraps event data into the indices of a list view index (row_index, column_index)
    #[cfg(feature="list-view")]
    pub fn on_list_view_item_index(&self) -> (usize, usize) {
        match self {
            &EventData::OnListViewItemIndex { row_index, column_index } => (row_index, column_index),
            d => panic!("Wrong data type: {:?}", d)
        }
    }

    /// unwraps event data into the indices of a list view index (row_index, column_index, selected)
    #[cfg(feature="list-view")]
    pub fn on_list_view_item_changed(&self) -> (usize, usize, bool) {
        match self {
            &EventData::OnListViewItemChanged { row_index, column_index, selected} => (row_index, column_index, selected),
            d => panic!("Wrong data type: {:?}", d)
        }
    }

}

//
// Events data structures
//

use winapi::um::commctrl::NMTTDISPINFOW;
use winapi::um::winuser::{PAINTSTRUCT, MINMAXINFO, BeginPaint, EndPaint};
use winapi::um::shellapi::{HDROP, DragFinish};
use winapi::shared::windef::{HWND, POINT};
use std::fmt;

/// A wrapper structure that sets the tooltip text on an `OnTooltipText` callback
pub struct ToolTipTextData {
    pub(crate) data: *mut NMTTDISPINFOW
}

impl ToolTipTextData {

    /// Tells the application to save the text value of the callback
    /// The `OnTooltipText` will not be called a second time for the associated control
    pub fn keep(&self, keep: bool) {
        use ::winapi::um::commctrl::TTF_DI_SETITEM;
        
        let data = unsafe { &mut *self.data };
        match keep {
            true => { data.uFlags |= TTF_DI_SETITEM; },
            false => { data.uFlags &= !TTF_DI_SETITEM; }
        }
    }

    /// Sets the text of the callback. This function will copy the text.
    /// WINAPI does not easily allow tooltips with more than 79 characters (80 with NULL)
    /// With a text > 79 characters, this method will do nothing.
    pub fn set_text<'b>(&self, text: &'b str) {
        use crate::win32::base_helper::to_utf16;
        use std::ptr;
        
        let text_len = text.len();
        if text_len > 79 {
            return;
        }

        self.clear();
        unsafe {
            let data = &mut *self.data;
            let local_text = to_utf16(text);
            ptr::copy_nonoverlapping(local_text.as_ptr(), data.szText.as_mut_ptr(), text_len);
        }
    }

    fn clear(&self) {
        use winapi::um::winnt::WCHAR;
        use std::{ptr, mem};
        
        unsafe {
            let data = &mut *self.data;
            ptr::write(&mut data.szText as *mut [WCHAR; 80], mem::zeroed());
        }
    }

}

impl fmt::Debug for ToolTipTextData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ToolTipTextData")
    }
}


/// Opaque type that manages if a window should be closed after an OnClose event
pub struct WindowCloseData {
    pub(crate) data: *mut bool
}

impl WindowCloseData {

    /// Sets if the window should close after the event
    pub fn close(&self, value: bool) {
        unsafe{ *self.data = value; }
    }

    /// Returns true if the window will close after the event or false otherwise
    pub fn closing(&self) -> bool {
        unsafe{ *self.data }
    }
}

impl fmt::Debug for WindowCloseData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "WindowCloseData({})", self.closing())
    }
}


/// Opaque type over a paint event's data
#[derive(Debug)]
pub struct PaintData {
    pub(crate) hwnd: HWND
}

impl PaintData {

    /// Wrapper over BeginPaint
    pub fn begin_paint(&self) -> PAINTSTRUCT {
        unsafe {
            let mut paint: PAINTSTRUCT = ::std::mem::zeroed();
            BeginPaint(self.hwnd, &mut paint);
            paint
        }
    }

    /// Wrapper over EndPaint
    pub fn end_paint(&self, p: &PAINTSTRUCT) {
        unsafe {
            EndPaint(self.hwnd, p);
        }
    }

}


/// Opaque type over one or more dragged files.
pub struct DropFiles {
    pub(crate) drop: HDROP,
}

impl DropFiles {

    /// Retrieves the position of the mouse pointer at the time a file was dropped during a drag-and-drop operation.
    /// The coordinates are local to the control. Ex: (0, 0) is the top left corner of the control.
    pub fn point(&self) -> [i32; 2] {
        use winapi::um::shellapi::DragQueryPoint;

        unsafe {
            let mut pt = POINT { x: 0, y: 0 };
            DragQueryPoint(self.drop, &mut pt);
            [pt.x, pt.y]
        }
    }

    /// Return the number of files dropped 
    pub fn len(&self) -> usize {
        use winapi::um::shellapi::DragQueryFileW;
        use std::ptr;

        unsafe {
            DragQueryFileW(self.drop, 0xFFFFFFFF, ptr::null_mut(), 0) as usize
        }
    }

    /// Return the files path dropped into the app
    pub fn files(&self) -> Vec<String> {
        use winapi::um::shellapi::DragQueryFileW;
        use crate::win32::base_helper::from_utf16;
        use std::ptr;

        let len = self.len();
        let mut files = Vec::with_capacity(len);
        unsafe {
            for i in 0..len {
                // Need to add a +1 here for some reason
                let buffer_size = (DragQueryFileW(self.drop, i as _, ptr::null_mut(), 0) + 1) as usize;

                let mut buffer: Vec<u16> = Vec::with_capacity(buffer_size);
                buffer.set_len(buffer_size);

                DragQueryFileW(self.drop, i as _, buffer.as_mut_ptr(), buffer_size as _);

                files.push(from_utf16(&buffer));
            }
        }

        files
    }

}

impl fmt::Debug for DropFiles {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "DragData {{ point: {:?}, files: {:?} }}", self.point(), self.files())
    }
}

impl Drop for DropFiles {

    fn drop(&mut self) {
        if !self.drop.is_null() {
            unsafe { DragFinish(self.drop) }
        }
    }

}


pub struct MinMaxInfo {
    pub(crate) inner: *mut MINMAXINFO,
}

impl MinMaxInfo {

    /// 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.
    pub fn set_maximized_size(&self, width: i32, height: i32) {
        let info = unsafe { &mut *self.inner };
        let (x, y) = unsafe { crate::win32::high_dpi::logical_to_physical(width as i32, height as i32) };
        info.ptMaxSize = POINT { x, y };
    }

    /// 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.
    pub fn maximized_size(&self) -> [i32; 2] {
        let info = unsafe { &mut *self.inner };
        let (w, h) = unsafe { crate::win32::high_dpi::physical_to_logical(info.ptMaxSize.x, info.ptMaxSize.y) };
        [w, h]
    }

    /// 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.
    pub fn set_maximized_pos(&self, x: i32, y: i32) {
        let info = unsafe { &mut *self.inner };
        let (x, y) = unsafe { crate::win32::high_dpi::logical_to_physical(x, y) };
        info.ptMaxPosition = POINT { x, y };
    }

    /// 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.
    pub fn maximized_pos(&self) -> [i32; 2] {
        let info = unsafe { &mut *self.inner };
        let (x, y) = unsafe { crate::win32::high_dpi::physical_to_logical(info.ptMaxPosition.x, info.ptMaxPosition.y) };
        
        [x, y]
    }

    /// Sets the maximum size of the window
    pub fn set_max_size(&self, width: i32, height: i32) {
        let info = unsafe { &mut *self.inner };
        let (x, y) = unsafe { crate::win32::high_dpi::logical_to_physical(width, height) };
        info.ptMaxTrackSize = POINT { x, y };
    }

    /// Returns the maximum size of the window
    pub fn max_size(&self) -> [i32; 2] {
        let info = unsafe { &mut *self.inner };
        let (w, h) = unsafe { crate::win32::high_dpi::physical_to_logical(info.ptMaxTrackSize.x, info.ptMaxTrackSize.y) };
        [w, h]
    }

    /// Sets the maximum size of the window
    pub fn set_min_size(&self, width: i32, height: i32) {
        let info = unsafe { &mut *self.inner };
        let (x, y) = unsafe { crate::win32::high_dpi::logical_to_physical(width as i32, height as i32) };
        info.ptMinTrackSize = POINT { x, y };
    }

    /// Returns the minimum size of the window
    pub fn min_size(&self) -> [i32; 2] {
        let info = unsafe { &mut *self.inner };
        let (w, h) = unsafe { crate::win32::high_dpi::physical_to_logical(info.ptMinTrackSize.x, info.ptMinTrackSize.y) };
        [w, h]
    }
}

impl fmt::Debug for MinMaxInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, 
            "MinMaxInfo {{ maximized_size: {:?}, maximized_pos: {:?}, max_size: {:?}, min_size: {:?} }}",
            self.maximized_size(), self.maximized_pos(), self.max_size(), self.min_size() 
        )
    }
}