Skip to main content

appcui/ui/datepicker/
datepicker.rs

1use chrono::{Datelike, Days, Months, NaiveDate};
2use datepicker::events::EventData;
3use appcui_proc_macro::CustomControl;
4
5const MINSPACE_FOR_SHORT_DATE: u32 = 15;
6const MINSPACE_FOR_LONG_DATE: u32 = 18;
7const MINSPACE_FOR_DROPBUTTON_DRAWING: u32 = 3;
8const MINSPACE_FOR_DATE_DRAWING: u32 = 5;
9const MIN_WIDTH_FOR_DATE_NAME: u32 = 6;
10const CALENDAR_WIDTH: u32 = 30;
11const CALENDAR_HEIGHT: u32 = 12;
12enum DateSize {
13    Large,
14    Small,
15    VerySmall,
16}
17#[derive(PartialEq, Eq)]
18enum HoveredDate {
19    DoubleLeftArrow,
20    LeftArrowYear,
21    RightArrowYear,
22    DoubleRightArrow,
23    LeftArrowMonth,
24    RightArrowMonth,
25    Day(u32),
26    None,
27}
28enum CharOrSpecialChar {
29    Regular(char),
30    Special(SpecialChar),
31}
32
33#[CustomControl(overwrite=OnPaint+OnDefaultAction+OnExpand+OnMouseEvent+OnKeyPressed, internal=true)]
34pub struct DatePicker {
35    header_y_ofs: i32,
36    expanded_panel_y: i32,
37    selected_date: NaiveDate,
38    date_string: String,
39    hover_date: HoveredDate,
40    virtual_date: NaiveDate,
41}
42
43impl DatePicker {
44    const DAYS: [&'static str; 7] = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
45    const MONTHS: [&'static str; 12] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
46
47    /// Creates a new date picker with a NaiveDate and a layout.
48    ///
49    /// # Example
50    /// ```rust,no_run
51    /// use appcui::prelude::*;
52    /// use chrono::NaiveDate;
53    ///
54    /// let datepicker = DatePicker::with_date(NaiveDate::from_ymd(2024, 6, 13), layout!("x:1,y:1,w:19"));
55    /// ```
56    pub fn with_date(date: NaiveDate, layout: Layout) -> Self {
57        let mut dp = DatePicker {
58            base: ControlBase::with_status_flags(layout, StatusFlags::Enabled | StatusFlags::Visible | StatusFlags::AcceptInput),
59            header_y_ofs: 0,
60            expanded_panel_y: 1,
61            selected_date: date,
62            date_string: Self::format_long_date(date),
63            hover_date: HoveredDate::None,
64            virtual_date: date,
65        };
66        dp.set_size_bounds(6, 1, u16::MAX, 1);
67        let date_len = dp.get_date_size();
68        match date_len {
69            DateSize::Large => {}
70            DateSize::Small => {
71                dp.date_string = Self::format_short_date(date);
72            }
73            DateSize::VerySmall => {
74                dp.date_string = Self::format_very_short_date(date);
75            }
76        }
77        dp
78    }
79
80    /// Creates a new date picker with a date string and a layout.
81    /// The date string must be in the format "YYYY-MM-DD".
82    ///
83    /// # Example                                       
84    /// ```rust,no_run
85    /// use appcui::prelude::*;
86    ///
87    /// let datepicker = DatePicker::new("2024-06-13", layout!("x:1,y:1,w:19"));
88    /// ```
89    pub fn new(date_str: &str, layout: Layout) -> Self {
90        let date = date_str.parse::<NaiveDate>().unwrap();
91        Self::with_date(date, layout)
92    }
93
94    /// Sets the date of the date picker as a string representation.
95    pub fn set_date_str(&mut self, date_str: &str) {
96        self.selected_date = date_str.parse::<NaiveDate>().unwrap();
97        self.date_string = Self::format_long_date(self.selected_date);
98    }
99
100    /// Sets the date of the date picker from a NaiveDate.
101    pub fn set_date(&mut self, date: NaiveDate) {
102        self.selected_date = date;
103        self.date_string = Self::format_long_date(date);
104    }
105
106    /// Returns the date of the date picker as a NaiveDate.
107    #[inline(always)]
108    pub fn date(&self) -> NaiveDate {
109        self.selected_date
110    }
111
112    fn update_date(&mut self, date: NaiveDate) {
113        if date != self.selected_date {
114            self.selected_date = date;
115            self.date_string = Self::format_long_date(date);
116
117            self.raise_event(ControlEvent {
118                emitter: self.handle,
119                receiver: self.event_processor,
120                data: ControlEventData::DatePicker(EventData { date: self.selected_date }),
121            });
122        }
123    }
124
125    fn jump_to_month(date: NaiveDate, target_month: u32) -> NaiveDate {
126        let year = date.year();
127        let day = date.day();
128
129        let mut new_date = NaiveDate::from_ymd_opt(year, target_month, 1).unwrap();
130
131        // Get the last day of the target month
132        let last_day_of_month = (1..=31)
133            .rev()
134            .find(|&d| NaiveDate::from_ymd_opt(year, target_month, d).is_some())
135            .unwrap();
136
137        // Adjust the day to be the minimum of the original day and the last day of the month
138        new_date = new_date.with_day(day.min(last_day_of_month)).unwrap();
139
140        new_date
141    }
142    fn mouse_over_calendar(&mut self, x: i32, y: i32) -> HoveredDate {
143        if !self.is_expanded() {
144            return HoveredDate::None;
145        }
146        if y == 1 + self.expanded_panel_y {
147            if x == 5 {
148                return HoveredDate::LeftArrowYear;
149            }
150            if x == 12 {
151                return HoveredDate::RightArrowYear;
152            }
153            if x == 2 || x == 3 {
154                return HoveredDate::DoubleLeftArrow;
155            }
156            if x == 14 || x == 15 {
157                return HoveredDate::DoubleRightArrow;
158            }
159
160            if x == 20 {
161                return HoveredDate::LeftArrowMonth;
162            }
163            if x == 26 {
164                return HoveredDate::RightArrowMonth;
165            }
166        }
167        let mut col = self.get_first_day_index() * 4 + 3;
168        let mut row = 4 + self.expanded_panel_y;
169        let last_day = self.days_in_month() as i32;
170
171        for i in 0..last_day {
172            let day = i + 1;
173
174            if (y == row) && (x == col || x == (col - 1)) {
175                self.virtual_date = self.virtual_date.with_day(day as u32).unwrap();
176                return HoveredDate::Day(day as u32);
177            }
178
179            col += 4;
180            if col >= 30 {
181                col = 3;
182                row += 1;
183            }
184        }
185        HoveredDate::None
186    }
187
188    fn format_very_short_date(selected_date: NaiveDate) -> String {
189        selected_date.format("%d.%m.%y").to_string()
190    }
191    fn format_short_date(selected_date: NaiveDate) -> String {
192        selected_date.format("%d.%m.%Y").to_string()
193    }
194
195    fn format_long_date(selected_date: NaiveDate) -> String {
196        selected_date.format("%Y, %b, %d").to_string()
197    }
198
199    fn get_date_size(&self) -> DateSize {
200        let width: u32 = self.size().width;
201        if width > MINSPACE_FOR_LONG_DATE {
202            DateSize::Large
203        } else if width > MINSPACE_FOR_SHORT_DATE {
204            DateSize::Small
205        } else {
206            DateSize::VerySmall
207        }
208    }
209    fn get_printed_chars(&self) -> u32 {
210        let wdth = self.size().width - MIN_WIDTH_FOR_DATE_NAME;
211        if (self.date_string.len() as u32) > wdth {
212            wdth
213        } else {
214            self.date_string.len() as u32
215        }
216    }
217
218    fn days_in_month(&self) -> u32 {
219        let month = self.virtual_date.month();
220        let year = self.virtual_date.year();
221        let next_month = if month == 12 { 1 } else { month + 1 };
222        let next_month_year = if month == 12 { year + 1 } else { year };
223
224        let first_of_next_month = NaiveDate::from_ymd_opt(next_month_year, next_month, 1).unwrap();
225        let first_of_current_month = NaiveDate::from_ymd_opt(year, month, 1).unwrap();
226
227        first_of_next_month.signed_duration_since(first_of_current_month).num_days() as u32
228    }
229
230    fn get_first_day_index(&self) -> i32 {
231        let first_day = self.virtual_date.with_day(1).unwrap().format("%a").to_string();
232        for i in 0..DatePicker::DAYS.len() {
233            if first_day.starts_with(DatePicker::DAYS[i]) {
234                return i as i32;
235            }
236        }
237        0
238    }
239}
240
241impl OnPaint for DatePicker {
242    fn on_paint(&self, surface: &mut Surface, theme: &Theme) {
243        let size = self.size();
244        let col_text = match () {
245            _ if !self.is_enabled() => theme.button.regular.text.inactive,
246            _ if self.has_focus() => theme.button.regular.text.focused,
247            _ if self.is_mouse_over() => theme.button.regular.text.hovered,
248            _ => theme.button.regular.text.normal,
249        };
250
251        let space_char = Character::with_attributes(' ', col_text);
252
253        // header
254        let date_size = self.get_date_size();
255        let mut format = TextFormatBuilder::new()
256            .position(1, self.header_y_ofs)
257            .attribute(col_text)
258            .align(TextAlignment::Left)
259            .wrap_type(WrapType::SingleLineWrap((size.width - MIN_WIDTH_FOR_DATE_NAME) as u16))
260            .build();
261        surface.fill_horizontal_line(0, self.header_y_ofs, (size.width - MINSPACE_FOR_DATE_DRAWING) as i32, space_char);
262        let mut buf: [u8; 16] = [0; 16];
263        match date_size {
264            DateSize::Large => {
265                if let Some(txt) = crate::utils::FormatDate::normal(&self.selected_date, &mut buf) {
266                    format.set_chars_count(txt.len() as u16);
267                    surface.write_text(txt, &format);
268                }
269            }
270            DateSize::Small => {
271                if let Some(txt) = crate::utils::FormatDate::dmy(&self.selected_date, &mut buf, b'.') {
272                    format.set_chars_count(txt.len() as u16);
273                    surface.write_text(txt, &format);
274                }
275            }
276            DateSize::VerySmall => {
277                if let Some(txt) = crate::utils::FormatDate::short(&self.selected_date, &mut buf, b'.') {
278                    format.set_chars_count(txt.len() as u16);
279                    surface.write_text(txt, &format);
280                }
281            }
282        }
283
284        if size.width >= MINSPACE_FOR_DROPBUTTON_DRAWING {
285            let px = (size.width - MINSPACE_FOR_DROPBUTTON_DRAWING) as i32;
286            surface.fill_horizontal_line_with_size(px, self.header_y_ofs, 3, space_char);
287            surface.write_char(px + 1, self.header_y_ofs, Character::with_attributes(SpecialChar::TriangleDown, col_text));
288        }
289
290        // expanded calendar
291        if self.is_expanded() {
292            let size = self.expanded_size();
293            let col = theme.menu.text.normal;
294            let space_char = Character::with_attributes(' ', col);
295            surface.fill_rect(
296                Rect::with_size(0, self.expanded_panel_y, size.width as u16, (size.height - 1) as u16),
297                space_char,
298            );
299            surface.draw_rect(
300                Rect::with_size(0, self.expanded_panel_y, size.width as u16, (size.height - 1) as u16),
301                LineType::Single,
302                col,
303            );
304            surface.draw_horizontal_line(1, 2 + self.expanded_panel_y, (CALENDAR_WIDTH - 1) as i32, LineType::SingleRound, col);
305            surface.write_char(0, 2 + self.expanded_panel_y, Character::with_attributes(SpecialChar::BoxMidleLeft, col));
306            surface.write_char(
307                (CALENDAR_WIDTH - 1) as i32,
308                2 + self.expanded_panel_y,
309                Character::with_attributes(SpecialChar::BoxMidleRight, col),
310            );
311
312            let year = self.virtual_date.year();
313            let p_y = 1 + self.expanded_panel_y;
314            surface.write_char(7, p_y, Character::with_attributes(((year / 1000) + 48) as u8, col));
315            surface.write_char(8, p_y, Character::with_attributes(((year / 100) % 10 + 48) as u8, col));
316            surface.write_char(9, p_y, Character::with_attributes(((year / 10) % 10 + 48) as u8, col));
317            surface.write_char(10, p_y, Character::with_attributes((year % 10 + 48) as u8, col));
318
319            fn set_char(surface: &mut Surface, x: i32, y: i32, char_or_special: CharOrSpecialChar, condition: bool, theme: &Theme) {
320                let attr = if condition { theme.menu.text.hovered } else { theme.menu.text.normal };
321                let character = match char_or_special {
322                    CharOrSpecialChar::Regular(c) => Character::with_attributes(c, attr),
323                    CharOrSpecialChar::Special(sc) => Character::with_attributes(sc, attr),
324                };
325                surface.write_char(x, y, character);
326            }
327            let y_pos = 1 + self.expanded_panel_y;
328
329            set_char(
330                surface,
331                5,
332                y_pos,
333                CharOrSpecialChar::Special(SpecialChar::TriangleLeft),
334                self.hover_date == HoveredDate::LeftArrowYear,
335                theme,
336            );
337            set_char(
338                surface,
339                12,
340                y_pos,
341                CharOrSpecialChar::Special(SpecialChar::TriangleRight),
342                self.hover_date == HoveredDate::RightArrowYear,
343                theme,
344            );
345            set_char(
346                surface,
347                2,
348                y_pos,
349                CharOrSpecialChar::Regular('<'),
350                self.hover_date == HoveredDate::DoubleLeftArrow,
351                theme,
352            );
353            set_char(
354                surface,
355                3,
356                y_pos,
357                CharOrSpecialChar::Regular('<'),
358                self.hover_date == HoveredDate::DoubleLeftArrow,
359                theme,
360            );
361            set_char(
362                surface,
363                14,
364                y_pos,
365                CharOrSpecialChar::Regular('>'),
366                self.hover_date == HoveredDate::DoubleRightArrow,
367                theme,
368            );
369            set_char(
370                surface,
371                15,
372                y_pos,
373                CharOrSpecialChar::Regular('>'),
374                self.hover_date == HoveredDate::DoubleRightArrow,
375                theme,
376            );
377            set_char(
378                surface,
379                20,
380                y_pos,
381                CharOrSpecialChar::Special(SpecialChar::TriangleLeft),
382                self.hover_date == HoveredDate::LeftArrowMonth,
383                theme,
384            );
385            set_char(
386                surface,
387                26,
388                y_pos,
389                CharOrSpecialChar::Special(SpecialChar::TriangleRight),
390                self.hover_date == HoveredDate::RightArrowMonth,
391                theme,
392            );
393
394            let month = Self::MONTHS[self.virtual_date.month0() as usize];
395            surface.write_ascii(22, 1 + self.expanded_panel_y, month.as_bytes(), col, false);
396
397            let mut x = 2;
398            for i in 0..7 {
399                surface.write_ascii(
400                    x,
401                    3 + self.expanded_panel_y,
402                    DatePicker::DAYS[i].as_bytes(),
403                    theme.menu.text.inactive,
404                    false,
405                );
406                x += 4;
407            }
408
409            let mut day_row = 4 + self.expanded_panel_y;
410            let mut day_col = self.get_first_day_index() * 4 + 3;
411
412            let last_day = self.days_in_month();
413
414            for i in 0..last_day {
415                let day = i + 1;
416                surface.write_char(day_col, day_row, Character::with_attributes((day % 10 + 48) as u8 as char, col));
417                if day > 9 {
418                    surface.write_char(day_col - 1, day_row, Character::with_attributes((day / 10 + 48) as u8 as char, col));
419                }
420                if day == self.selected_date.day()
421                    && self.selected_date.month() == self.virtual_date.month()
422                    && self.selected_date.year() == self.virtual_date.year()
423                {
424                    surface.fill_horizontal_line_with_size(
425                        day_col - 2,
426                        day_row,
427                        4,
428                        Character::with_attributes(0, theme.menu.text.pressed_or_selected),
429                    );
430                } else if self.virtual_date.day() == day {
431                    surface.fill_horizontal_line_with_size(day_col - 2, day_row, 4, Character::with_attributes(0, theme.menu.text.hovered));
432                }
433                day_col += 4;
434                if day_col >= 30 {
435                    day_col = 3;
436                    day_row += 1;
437                }
438            }
439        }
440    }
441}
442
443impl OnDefaultAction for DatePicker {
444    fn on_default_action(&mut self) {
445        if self.is_expanded() {
446            self.pack();
447        } else {
448            self.expand(Size::new(CALENDAR_WIDTH, CALENDAR_HEIGHT), Size::new(CALENDAR_WIDTH, CALENDAR_HEIGHT));
449        }
450    }
451}
452impl OnExpand for DatePicker {
453    fn on_expand(&mut self, direction: ExpandedDirection) {
454        self.virtual_date = self.selected_date;
455        match direction {
456            ExpandedDirection::OnTop => {
457                self.expanded_panel_y = 0;
458                self.header_y_ofs = (self.expanded_size().height as i32) - 1;
459            }
460            ExpandedDirection::OnBottom => {
461                self.expanded_panel_y = 1;
462                self.header_y_ofs = 0;
463            }
464        }
465        self.hover_date = HoveredDate::None;
466    }
467    fn on_pack(&mut self) {
468        self.expanded_panel_y = 1;
469        self.header_y_ofs = 0;
470    }
471}
472
473impl OnMouseEvent for DatePicker {
474    fn on_mouse_event(&mut self, event: &MouseEvent) -> EventProcessStatus {
475        match event {
476            MouseEvent::Enter => {
477                if !self.is_expanded() && (self.date_string.len() as i32) > (self.get_printed_chars() as i32) {
478                    self.show_tooltip(self.date_string.as_str())
479                }
480                EventProcessStatus::Processed
481            }
482
483            MouseEvent::Leave => {
484                self.hide_tooltip();
485                EventProcessStatus::Processed
486            }
487            MouseEvent::Over(p) => {
488                let hd = self.mouse_over_calendar(p.x, p.y);
489                if hd != self.hover_date {
490                    self.hover_date = hd;
491                }
492                EventProcessStatus::Processed
493            }
494            MouseEvent::Pressed(data) => {
495                let hd = self.mouse_over_calendar(data.x, data.y);
496                if hd != HoveredDate::None {
497                    match hd {
498                        HoveredDate::DoubleLeftArrow => {
499                            self.virtual_date = self.virtual_date - Months::new(120);
500                        }
501                        HoveredDate::LeftArrowYear => {
502                            self.virtual_date = self.virtual_date - Months::new(12);
503                        }
504                        HoveredDate::RightArrowYear => {
505                            self.virtual_date = self.virtual_date + Months::new(12);
506                        }
507                        HoveredDate::DoubleRightArrow => {
508                            self.virtual_date = self.virtual_date + Months::new(120);
509                        }
510                        HoveredDate::LeftArrowMonth => {
511                            self.virtual_date = self.virtual_date - Months::new(1);
512                        }
513                        HoveredDate::RightArrowMonth => {
514                            self.virtual_date = self.virtual_date + Months::new(1);
515                        }
516                        HoveredDate::Day(day) => {
517                            self.update_date(self.virtual_date.with_day(day).unwrap());
518                            self.on_default_action();
519                        }
520
521                        _ => {}
522                    }
523
524                    return EventProcessStatus::Processed;
525                }
526                self.on_default_action();
527                EventProcessStatus::Processed
528            }
529            _ => EventProcessStatus::Ignored,
530        }
531    }
532}
533
534impl OnKeyPressed for DatePicker {
535    fn on_key_pressed(&mut self, key: Key, _character: char) -> EventProcessStatus {
536        let expanded = self.is_expanded();
537
538        match key.value() {
539            key!("F") | key!("S") | key!("O") | key!("N") | key!("D") => {
540                let month = match key.value() {
541                    key!("F") => 2,
542                    key!("S") => 9,
543                    key!("O") => 10,
544                    key!("N") => 11,
545                    key!("D") => 12,
546                    _ => unreachable!(),
547                };
548                if expanded {
549                    self.virtual_date = Self::jump_to_month(self.virtual_date, month);
550                } else {
551                    self.update_date(Self::jump_to_month(self.selected_date, month));
552                }
553                return EventProcessStatus::Processed;
554            }
555
556            key!("J") | key!("A") | key!("M") | key!("Shift+J") | key!("Shift+A") | key!("Shift+M") => {
557                let mut val = 1i32;
558                let month_char = match key.value() {
559                    key!("J") => "J",
560                    key!("A") => "A",
561                    key!("M") => "M",
562                    key!("Shift+J") => {
563                        val = -1;
564                        "J"
565                    }
566                    key!("Shift+A") => {
567                        val = -1;
568                        "A"
569                    }
570                    key!("Shift+M") => {
571                        val = -1;
572                        "M"
573                    }
574                    _ => unreachable!(),
575                };
576                let target_month: &mut NaiveDate = if expanded { &mut self.virtual_date } else { &mut self.selected_date };
577
578                let month = {
579                    let mut current_month = target_month.month() as i32 + val;
580                    if current_month > 12 {
581                        current_month = 1;
582                    }
583                    if current_month < 1 {
584                        current_month = 12;
585                    }
586                    for _ in 0..Self::MONTHS.len() {
587                        if Self::MONTHS[(current_month - 1) as usize].starts_with(month_char) {
588                            break;
589                        } else {
590                            current_month += val;
591                            if current_month > 12 {
592                                current_month = 1;
593                            }
594                            if current_month < 1 {
595                                current_month = 12;
596                            }
597                        }
598                    }
599                    current_month
600                };
601                if expanded {
602                    self.virtual_date = Self::jump_to_month(self.virtual_date, month as u32);
603                } else {
604                    self.update_date(Self::jump_to_month(self.selected_date, month as u32));
605                }
606                return EventProcessStatus::Processed;
607            }
608
609            _ => {}
610        }
611
612        if !expanded {
613            match key.value() {
614                key!("Escape") => {
615                    return EventProcessStatus::Ignored;
616                }
617                key!("Up") => {
618                    self.update_date(self.selected_date + Days::new(1));
619                    return EventProcessStatus::Processed;
620                }
621
622                key!("Down") => {
623                    self.update_date(self.selected_date - Days::new(1));
624                    return EventProcessStatus::Processed;
625                }
626
627                key!("Shift+Up") => {
628                    self.update_date(self.selected_date + Months::new(1));
629                    return EventProcessStatus::Processed;
630                }
631
632                key!("Shift+Down") => {
633                    self.update_date(self.selected_date - Months::new(1));
634                    return EventProcessStatus::Processed;
635                }
636
637                key!("Ctrl+Up") => {
638                    self.update_date(self.selected_date + Months::new(12));
639                    return EventProcessStatus::Processed;
640                }
641
642                key!("Ctrl+Down") => {
643                    self.update_date(self.selected_date - Months::new(12));
644                    return EventProcessStatus::Processed;
645                }
646
647                key!("Ctrl+Shift+Up") => {
648                    self.update_date(self.selected_date + Months::new(120));
649                    return EventProcessStatus::Processed;
650                }
651
652                key!("Ctrl+Shift+Down") => {
653                    self.update_date(self.selected_date - Months::new(120));
654                    return EventProcessStatus::Processed;
655                }
656
657                key!("Enter") | key!("Space") => {
658                    self.on_default_action();
659                    return EventProcessStatus::Processed;
660                }
661                _ => {}
662            }
663            EventProcessStatus::Ignored
664        } else {
665            match key.value() {
666                key!("Escape") => {
667                    self.pack();
668                    return EventProcessStatus::Processed;
669                }
670
671                key!("Up") => {
672                    self.virtual_date = self.virtual_date - Days::new(7);
673                    return EventProcessStatus::Processed;
674                }
675
676                key!("Down") => {
677                    self.virtual_date = self.virtual_date + Days::new(7);
678                    return EventProcessStatus::Processed;
679                }
680
681                key!("Left") => {
682                    self.virtual_date = self.virtual_date - Days::new(1);
683                    return EventProcessStatus::Processed;
684                }
685
686                key!("Right") => {
687                    self.virtual_date = self.virtual_date + Days::new(1);
688                    return EventProcessStatus::Processed;
689                }
690
691                key!("Shift+Left") => {
692                    self.virtual_date = self.virtual_date - Months::new(1);
693                    return EventProcessStatus::Processed;
694                }
695
696                key!("Shift+Right") => {
697                    self.virtual_date = self.virtual_date + Months::new(1);
698                    return EventProcessStatus::Processed;
699                }
700
701                key!("Ctrl+Left") => {
702                    self.virtual_date = self.virtual_date - Months::new(12);
703                    return EventProcessStatus::Processed;
704                }
705
706                key!("Ctrl+Right") => {
707                    self.virtual_date = self.virtual_date + Months::new(12);
708                    return EventProcessStatus::Processed;
709                }
710
711                key!("Ctrl+Shift+Left") => {
712                    self.virtual_date = self.virtual_date - Months::new(120);
713                    return EventProcessStatus::Processed;
714                }
715
716                key!("Ctrl+Shift+Right") => {
717                    self.virtual_date = self.virtual_date + Months::new(120);
718                    return EventProcessStatus::Processed;
719                }
720
721                key!("Enter") => {
722                    self.update_date(self.virtual_date);
723                    self.on_default_action();
724                    return EventProcessStatus::Processed;
725                }
726                _ => {}
727            }
728
729            EventProcessStatus::Ignored
730        }
731    }
732}