native_windows_gui2/controls/
date_picker.rs

1use super::{ControlBase, ControlHandle};
2use crate::win32::base_helper::{check_hwnd, to_utf16};
3use crate::win32::window_helper as wh;
4use crate::{Font, NwgError};
5use winapi::um::winuser::{WS_DISABLED, WS_TABSTOP, WS_VISIBLE};
6
7const NOT_BOUND: &'static str = "DatePicker is not yet bound to a winapi object";
8const BAD_HANDLE: &'static str = "INTERNAL ERROR: DatePicker handle is not HWND!";
9
10bitflags! {
11
12    /**
13        The DatePickerFlags flags
14
15        * NONE:     No flags. Equivalent to a invisible date picker.
16        * VISIBLE:  The date picker is immediatly visible after creation
17        * DISABLED: The date picker cannot be interacted with by the user. It also has a grayed out look.
18        * TAB_STOP: The control can be selected using tab navigation
19    */
20    pub struct DatePickerFlags: u32 {
21        const VISIBLE = WS_VISIBLE;
22        const DISABLED = WS_DISABLED;
23        const TAB_STOP = WS_TABSTOP;
24    }
25}
26
27/**
28    A date struct that can be passed to a date time picker control.
29    Fields are self explanatory.
30*/
31#[derive(Clone, Copy, PartialEq, Debug)]
32pub struct DatePickerValue {
33    pub year: u16,
34    pub month: u16,
35    pub day: u16,
36}
37
38/**
39A date and time picker (DTP) control provides a simple and intuitive interface through which to exchange date and time information with a user.
40For example, with a DTP control you can ask the user to enter a date and then easily retrieve the selection.
41
42Requires the `datetime-picker` feature.
43
44**Builder parameters:**
45  * `parent`:   **Required.** The dtp parent container.
46  * `size`:     The dtp size.
47  * `position`: The dtp position.
48  * `enabled`:  If the dtp can be used by the user. It also has a grayed out look if disabled.
49  * `flags`:    A combination of the DatePickerFlags values.
50  * `ex_flags`: A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
51  * `font`:     The font used for the dtp text
52  * `date`:     The default date as a `DatePickerValue` value
53  * `format`:   The format of the date. See the `set_format` method.
54  * `range`:    The accepted range of dates. The value is inclusive.
55  * `focus`:    The control receive focus after being created
56
57**Control events:**
58  * `OnDatePickerClosed`: When the datepicker dropdown is closed
59  * `OnDatePickerDropdown`: When the datepicker dropdown is opened
60  * `OnDatePickerChanged`: When a new value in a datepicker is choosen
61  * `MousePress(_)`: Generic mouse press events on the checkbox
62  * `OnMouseMove`: Generic mouse mouse event
63  * `OnMouseWheel`: Generic mouse wheel event
64
65```rust
66use native_windows_gui2 as nwg;
67fn build_dtp(date: &mut nwg::DatePicker, window: &nwg::Window) {
68    let v = nwg::DatePickerValue { year: 2000, month: 10, day: 5 };
69    let v1 = nwg::DatePickerValue { year: 2000, month: 10, day: 5 };
70    let v2 = nwg::DatePickerValue { year: 2012, month: 10, day: 5 };
71
72    nwg::DatePicker::builder()
73        .size((200, 300))
74        .position((0, 0))
75        .date(Some(v))
76        .format(Some("'YEAR: 'yyyy"))
77        .range(Some([v1, v2]))
78        .parent(window)
79        .build(date);
80}
81```
82*/
83#[derive(Default, PartialEq, Eq)]
84pub struct DatePicker {
85    pub handle: ControlHandle,
86}
87
88impl DatePicker {
89    pub fn builder<'a>() -> DatePickerBuilder<'a> {
90        DatePickerBuilder {
91            size: (100, 25),
92            position: (0, 0),
93            focus: false,
94            flags: None,
95            ex_flags: 0,
96            font: None,
97            parent: None,
98            date: None,
99            format: None,
100            range: None,
101        }
102    }
103
104    /**
105        Sets the date format of the control
106
107        About the format string:
108        - `d` 	    The one- or two-digit day.
109        - `dd` 	    The two-digit day. Single-digit day values are preceded by a zero.
110        - `ddd` 	The three-character weekday abbreviation.
111        - `dddd` 	The full weekday name.
112        - `M` 	    The one- or two-digit month number.
113        - `MM` 	    The two-digit month number. Single-digit values are preceded by a zero.
114        - `MMM` 	The three-character month abbreviation.
115        - `MMMM` 	The full month name.
116        - `t` 	    The one-letter AM/PM abbreviation (that is, AM is displayed as "A").
117        - `tt` 	    The two-letter AM/PM abbreviation (that is, AM is displayed as "AM").
118        - `yy` 	    The last two digits of the year (that is, 1996 would be displayed as "96").
119        - `yyyy` 	The full year (that is, 1996 would be displayed as "1996").
120
121        - `h` 	The one- or two-digit hour in 12-hour format.
122        - `hh` 	The two-digit hour in 12-hour format. Single-digit values are preceded by a zero.
123        - `H` 	The one- or two-digit hour in 24-hour format.
124        - `HH` 	The two-digit hour in 24-hour format. Single-digit values are preceded by a zero.
125        - `m` 	The one- or two-digit minute.
126        - `mm` 	The two-digit minute. Single-digit values are preceded by a zero.
127
128        Furthermore, any string enclosed in `'` can be used in the format to display text.
129        For example, to display the current date with the format `'Today is: Tuesday Mar 23, 1996`, the format string is `'Today is: 'dddd MMM dd', 'yyyy`.
130
131        If `format` is set to `None`, use the default system format.
132    */
133    pub fn set_format<'a>(&self, format: Option<&'a str>) {
134        use winapi::shared::minwindef::LPARAM;
135        use winapi::um::commctrl::DTM_SETFORMATW;
136
137        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
138
139        let (_format, format_ptr) = if format.is_some() {
140            let f = to_utf16(format.unwrap());
141            let fptr = f.as_ptr() as LPARAM;
142            (f, fptr)
143        } else {
144            (Vec::new(), 0)
145        };
146
147        wh::send_message(handle, DTM_SETFORMATW, 0, format_ptr);
148    }
149
150    /**
151        Return the check state of the checkbox of the control.
152        If the date time picker is not optional, return false.
153
154        To set the check state of the control, use `set_value` method
155    */
156    pub fn checked(&self) -> bool {
157        use winapi::um::winuser::STATE_SYSTEM_CHECKED;
158
159        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
160
161        let info = get_dtp_info(handle);
162
163        match info.stateCheck {
164            STATE_SYSTEM_CHECKED => true,
165            _ => false,
166        }
167    }
168
169    /// Close the calendar popup if it is open. Note that there is no way to force the calendar to drop down
170    pub fn close_calendar(&self) {
171        use winapi::um::commctrl::DTM_CLOSEMONTHCAL;
172        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
173        wh::send_message(handle, DTM_CLOSEMONTHCAL, 0, 0);
174    }
175
176    /**
177        Return the time set in the control in a `PickerDate` structure.
178        Return None if `optional` was set and the checkbox is not checked.
179        Note: use `get_text` to get the text value of the control.
180    */
181    pub fn value(&self) -> Option<DatePickerValue> {
182        use std::mem;
183        use winapi::shared::minwindef::LPARAM;
184        use winapi::um::commctrl::{DTM_GETSYSTEMTIME, GDT_VALID};
185        use winapi::um::minwinbase::SYSTEMTIME;
186
187        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
188
189        let mut syst: SYSTEMTIME = unsafe { mem::zeroed() };
190        let syst_ptr = &mut syst as *mut _;
191
192        let r = wh::send_message(handle, DTM_GETSYSTEMTIME, 0, syst_ptr as LPARAM);
193        match r {
194            GDT_VALID => Some(DatePickerValue {
195                year: syst.wYear,
196                month: syst.wMonth,
197                day: syst.wDay,
198            }),
199            _ => None,
200        }
201    }
202
203    /**
204        Set the time set in the control in a `PickerDate` structure.
205        If `None` is passed, this clears the checkbox.
206    */
207    pub fn set_value(&self, date: Option<DatePickerValue>) {
208        use winapi::shared::minwindef::{LPARAM, WPARAM};
209        use winapi::um::commctrl::{DTM_SETSYSTEMTIME, GDT_NONE, GDT_VALID};
210        use winapi::um::minwinbase::SYSTEMTIME;
211
212        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
213
214        match date {
215            Some(date) => {
216                let syst: SYSTEMTIME = SYSTEMTIME {
217                    wYear: date.year,
218                    wMonth: date.month,
219                    wDay: date.day,
220                    wDayOfWeek: 0,
221                    wHour: 0,
222                    wMinute: 0,
223                    wSecond: 0,
224                    wMilliseconds: 0,
225                };
226
227                wh::send_message(
228                    handle,
229                    DTM_SETSYSTEMTIME,
230                    GDT_VALID as WPARAM,
231                    &syst as *const SYSTEMTIME as LPARAM,
232                );
233            }
234            None => {
235                wh::send_message(handle, DTM_SETSYSTEMTIME, GDT_NONE as WPARAM, 0);
236            }
237        };
238    }
239
240    /// Gets the current minimum and maximum allowable system times for a date and time picker control.
241    pub fn range(&self) -> [DatePickerValue; 2] {
242        use std::mem;
243        use winapi::shared::minwindef::LPARAM;
244        use winapi::um::commctrl::DTM_GETRANGE;
245        use winapi::um::minwinbase::SYSTEMTIME;
246
247        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
248
249        let mut tr: [SYSTEMTIME; 2] = unsafe { mem::zeroed() };
250
251        wh::send_message(
252            handle,
253            DTM_GETRANGE,
254            0,
255            &mut tr as *mut [SYSTEMTIME; 2] as LPARAM,
256        );
257
258        [
259            DatePickerValue {
260                year: tr[0].wYear,
261                month: tr[0].wMonth,
262                day: tr[0].wDay,
263            },
264            DatePickerValue {
265                year: tr[1].wYear,
266                month: tr[1].wMonth,
267                day: tr[1].wDay,
268            },
269        ]
270    }
271
272    /// Sets the minimum and maximum allowable system times for a date and time picker control.
273    pub fn set_range(&self, r: &[DatePickerValue; 2]) {
274        use winapi::shared::minwindef::LPARAM;
275        use winapi::um::commctrl::DTM_SETRANGE;
276        use winapi::um::commctrl::{GDTR_MAX, GDTR_MIN};
277        use winapi::um::minwinbase::SYSTEMTIME;
278
279        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
280
281        let values = [
282            SYSTEMTIME {
283                wYear: r[0].year,
284                wMonth: r[0].month,
285                wDayOfWeek: 0,
286                wDay: r[0].day,
287                wHour: 0,
288                wMinute: 0,
289                wSecond: 0,
290                wMilliseconds: 0,
291            },
292            SYSTEMTIME {
293                wYear: r[1].year,
294                wMonth: r[1].month,
295                wDayOfWeek: 0,
296                wDay: r[1].day,
297                wHour: 0,
298                wMinute: 0,
299                wSecond: 0,
300                wMilliseconds: 0,
301            },
302        ];
303
304        wh::send_message(
305            handle,
306            DTM_SETRANGE,
307            GDTR_MIN | GDTR_MAX,
308            &values as *const [SYSTEMTIME; 2] as LPARAM,
309        );
310    }
311
312    /// Return the font of the control
313    pub fn font(&self) -> Option<Font> {
314        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
315        let font_handle = wh::get_window_font(handle);
316        if font_handle.is_null() {
317            None
318        } else {
319            Some(Font {
320                handle: font_handle,
321            })
322        }
323    }
324
325    /// Sets the font of the control
326    pub fn set_font(&self, font: Option<&Font>) {
327        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
328
329        wh::set_window_font(handle, font.map(|f| f.handle), true);
330    }
331
332    /// Return true if the control currently has the keyboard focus
333    pub fn focus(&self) -> bool {
334        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
335        wh::get_focus(handle)
336    }
337
338    /// Sets the keyboard focus on the button.
339    pub fn set_focus(&self) {
340        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
341
342        wh::set_focus(handle);
343    }
344
345    /// Return true if the control user can interact with the control, return false otherwise
346    pub fn enabled(&self) -> bool {
347        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
348        wh::get_window_enabled(handle)
349    }
350
351    /// Enable or disable the control
352    pub fn set_enabled(&self, v: bool) {
353        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
354        wh::set_window_enabled(handle, v)
355    }
356
357    /// Return true if the control is visible to the user. Will return true even if the
358    /// control is outside of the parent client view (ex: at the position (10000, 10000))
359    pub fn visible(&self) -> bool {
360        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
361        wh::get_window_visibility(handle)
362    }
363
364    /// Show or hide the control to the user
365    pub fn set_visible(&self, v: bool) {
366        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
367        wh::set_window_visibility(handle, v)
368    }
369
370    /// Return the size of the date picker in the parent window
371    pub fn size(&self) -> (u32, u32) {
372        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
373        wh::get_window_size(handle)
374    }
375
376    /// Set the size of the date picker in the parent window
377    pub fn set_size(&self, x: u32, y: u32) {
378        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
379        wh::set_window_size(handle, x, y, false)
380    }
381
382    /// Return the position of the date picker in the parent window
383    pub fn position(&self) -> (i32, i32) {
384        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
385        wh::get_window_position(handle)
386    }
387
388    /// Set the position of the date picker in the parent window
389    pub fn set_position(&self, x: i32, y: i32) {
390        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
391        wh::set_window_position(handle, x, y)
392    }
393
394    /// Winapi class name used during control creation
395    pub fn class_name(&self) -> &'static str {
396        "SysDateTimePick32"
397    }
398
399    /// Winapi base flags used during window creation
400    pub fn flags(&self) -> u32 {
401        WS_VISIBLE | WS_TABSTOP
402    }
403
404    /// Winapi flags required by the control
405    pub fn forced_flags(&self) -> u32 {
406        use winapi::um::commctrl::DTS_SHOWNONE;
407        use winapi::um::winuser::WS_CHILD;
408
409        WS_CHILD | DTS_SHOWNONE
410    }
411}
412
413impl Drop for DatePicker {
414    fn drop(&mut self) {
415        self.handle.destroy();
416    }
417}
418
419pub struct DatePickerBuilder<'a> {
420    size: (i32, i32),
421    position: (i32, i32),
422    flags: Option<DatePickerFlags>,
423    ex_flags: u32,
424    font: Option<&'a Font>,
425    focus: bool,
426    parent: Option<ControlHandle>,
427    date: Option<DatePickerValue>,
428    format: Option<&'a str>,
429    range: Option<[DatePickerValue; 2]>,
430}
431
432impl<'a> DatePickerBuilder<'a> {
433    pub fn flags(mut self, flags: DatePickerFlags) -> DatePickerBuilder<'a> {
434        self.flags = Some(flags);
435        self
436    }
437
438    pub fn ex_flags(mut self, flags: u32) -> DatePickerBuilder<'a> {
439        self.ex_flags = flags;
440        self
441    }
442
443    pub fn size(mut self, size: (i32, i32)) -> DatePickerBuilder<'a> {
444        self.size = size;
445        self
446    }
447
448    pub fn position(mut self, pos: (i32, i32)) -> DatePickerBuilder<'a> {
449        self.position = pos;
450        self
451    }
452
453    pub fn font(mut self, font: Option<&'a Font>) -> DatePickerBuilder<'a> {
454        self.font = font;
455        self
456    }
457
458    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> DatePickerBuilder<'a> {
459        self.parent = Some(p.into());
460        self
461    }
462
463    pub fn date(mut self, date: Option<DatePickerValue>) -> DatePickerBuilder<'a> {
464        self.date = date;
465        self
466    }
467
468    pub fn format(mut self, format: Option<&'a str>) -> DatePickerBuilder<'a> {
469        self.format = format;
470        self
471    }
472
473    pub fn range(mut self, range: Option<[DatePickerValue; 2]>) -> DatePickerBuilder<'a> {
474        self.range = range;
475        self
476    }
477
478    pub fn focus(mut self, focus: bool) -> DatePickerBuilder<'a> {
479        self.focus = focus;
480        self
481    }
482
483    pub fn build(self, out: &mut DatePicker) -> Result<(), NwgError> {
484        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
485
486        let parent = match self.parent {
487            Some(p) => Ok(p),
488            None => Err(NwgError::no_parent("DatePicker")),
489        }?;
490
491        *out = Default::default();
492
493        out.handle = ControlBase::build_hwnd()
494            .class_name(out.class_name())
495            .forced_flags(out.forced_flags())
496            .flags(flags)
497            .ex_flags(self.ex_flags)
498            .size(self.size)
499            .position(self.position)
500            .parent(Some(parent))
501            .build()?;
502
503        if self.font.is_some() {
504            out.set_font(self.font);
505        } else {
506            out.set_font(Font::global_default().as_ref());
507        }
508
509        if self.date.is_some() {
510            out.set_value(self.date)
511        }
512
513        if self.range.is_some() {
514            out.set_range(&self.range.unwrap());
515        }
516
517        if self.format.is_some() {
518            out.set_format(self.format);
519        }
520
521        if self.focus {
522            out.set_focus();
523        }
524
525        Ok(())
526    }
527}
528
529use winapi::shared::windef::HWND;
530use winapi::um::commctrl::DATETIMEPICKERINFO;
531
532fn get_dtp_info(handle: HWND) -> DATETIMEPICKERINFO {
533    use std::mem;
534    use winapi::shared::minwindef::{DWORD, LPARAM};
535    use winapi::um::commctrl::DTM_GETDATETIMEPICKERINFO;
536
537    let mut dtp_info: DATETIMEPICKERINFO = unsafe { mem::zeroed() };
538    dtp_info.cbSize = mem::size_of::<DATETIMEPICKERINFO>() as DWORD;
539    let dtp_info_ptr = &mut dtp_info as *mut _;
540    wh::send_message(handle, DTM_GETDATETIMEPICKERINFO, 0, dtp_info_ptr as LPARAM);
541
542    dtp_info
543}