Skip to main content

ferroui_controls/calendar/
calendar.rs

1// (c) Copyright Microsoft Corporation.
2// This source is subject to the Microsoft Public License (Ms-PL).
3// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
4// All other rights reserved.
5//
6// Ported from the Silverlight Toolkit sources as adapted by the upstream
7// project; the license text is in the `NOTICE.md` of this crate.
8
9use super::calendar_item::date_of;
10use super::{
11    CalendarBlackoutDatesCollection, CalendarButton, CalendarDateRange, CalendarDayButton, CalendarExtensions,
12    CalendarItem, DateTimeHelper, SelectedDatesCollection,
13};
14use crate::primitives::{SelectingItemsControl, TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl};
15use crate::{ControlImpl, Panel, SelectionChangedEventArgs};
16use ferroui_base::data::BindingMode;
17use ferroui_base::input::{
18    FocusChangedEventArgs, InputElement, InputElementImpl, InputElementImplExt, Key, KeyEventArgs, MouseButton,
19    PointerReleasedEventArgs, PointerWheelEventArgs,
20};
21use ferroui_base::interactivity::{InteractiveImpl, RoutedEventArgs};
22use ferroui_base::layout::LayoutableImpl;
23use ferroui_base::media::IBrush;
24use ferroui_base::reactive::{Disposable, IDisposable};
25use ferroui_base::utilities::{CalendarWeekRule, DateTime, DayOfWeek, HandlerList};
26use ferroui_base::{
27    ferro_class, ferro_class_info, ferro_impl_classes, ferro_properties, ferro_routed_event_args, instantiate,
28    BoxedValue, FerroObjectImpl, FerroObjectImplExt, FerroProperty, Ref, StyledElementImpl, StyledProperty,
29    StyledPropertyOptions, VisualImpl,
30};
31use std::cell::{Cell, OnceCell, RefCell};
32use std::rc::Rc;
33
34/// Specifies values for the different modes of operation of a
35/// [`Calendar`].
36#[repr(i32)]
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
38pub enum CalendarMode {
39    /// The [`Calendar`] displays a month at a time.
40    #[default]
41    Month = 0,
42
43    /// The [`Calendar`] displays a year at a time.
44    Year = 1,
45
46    /// The [`Calendar`] displays a decade at a time.
47    Decade = 2,
48}
49
50/// Specifies values that describe the available selection modes for a
51/// [`Calendar`].
52///
53/// This enumeration provides the values that are used by the SelectionMode
54/// property.
55#[repr(i32)]
56#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
57pub enum CalendarSelectionMode {
58    /// Only a single date can be selected. Use the
59    /// [`Calendar::selected_date`] property to retrieve the selected date.
60    #[default]
61    SingleDate = 0,
62
63    /// A single range of dates can be selected. Use the
64    /// [`Calendar::selected_dates`] property to retrieve the selected
65    /// dates.
66    SingleRange = 1,
67
68    /// Multiple non-contiguous ranges of dates can be selected. Use the
69    /// [`Calendar::selected_dates`] property to retrieve the selected
70    /// dates.
71    MultipleRange = 2,
72
73    /// No selections are allowed.
74    None = 3,
75}
76
77/// Provides data for the `DisplayDateChanged` event of the [`Calendar`].
78#[derive(Clone)]
79pub struct CalendarDateChangedEventArgs {
80    base: RoutedEventArgs,
81    removed_date: Option<DateTime>,
82    added_date: Option<DateTime>,
83}
84
85ferro_routed_event_args!(CalendarDateChangedEventArgs: RoutedEventArgs);
86
87impl CalendarDateChangedEventArgs {
88    /// Initializes a new instance of the [`CalendarDateChangedEventArgs`]
89    /// class.
90    ///
91    /// `removed_date` is the date that was previously displayed and
92    /// `added_date` the date to be newly displayed.
93    pub(crate) fn new(removed_date: Option<DateTime>, added_date: Option<DateTime>) -> Self {
94        Self { base: RoutedEventArgs::new(), removed_date, added_date }
95    }
96
97    /// Gets the date that was previously displayed.
98    pub fn removed_date(&self) -> Option<DateTime> {
99        self.removed_date
100    }
101
102    /// Gets the date to be newly displayed.
103    pub fn added_date(&self) -> Option<DateTime> {
104        self.added_date
105    }
106}
107
108/// Provides data for the `DisplayModeChanged` event of the [`Calendar`].
109#[derive(Clone)]
110pub struct CalendarModeChangedEventArgs {
111    base: RoutedEventArgs,
112    old_mode: CalendarMode,
113    new_mode: CalendarMode,
114}
115
116ferro_routed_event_args!(CalendarModeChangedEventArgs: RoutedEventArgs);
117
118impl CalendarModeChangedEventArgs {
119    /// Initializes a new instance of the [`CalendarModeChangedEventArgs`]
120    /// class with the previous and the new mode.
121    pub fn new(old_mode: CalendarMode, new_mode: CalendarMode) -> Self {
122        Self { base: RoutedEventArgs::new(), old_mode, new_mode }
123    }
124
125    /// Gets the previous mode of the [`Calendar`].
126    pub fn old_mode(&self) -> CalendarMode {
127        self.old_mode
128    }
129
130    /// Gets the new mode of the [`Calendar`].
131    pub fn new_mode(&self) -> CalendarMode {
132        self.new_mode
133    }
134}
135
136/// The text of the selected date in the general format of the current
137/// culture, or an empty text when no date is selected.
138impl std::fmt::Display for Calendar {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        match self.selected_date() {
141            Some(selected_date) => {
142                f.write_str(&selected_date.to_string_provider(&DateTimeHelper::get_current_date_format()))
143            }
144            None => Ok(()),
145        }
146    }
147}
148
149const PART_ELEMENT_ROOT: &str = "PART_Root";
150const PART_ELEMENT_MONTH: &str = "PART_CalendarItem";
151
152/// The date of an item of the arguments of a selection change.
153fn date_of_item(item: &Option<BoxedValue>) -> Option<DateTime> {
154    item.as_ref().and_then(|item| item.downcast_ref::<DateTime>().copied())
155}
156
157/// Represents a control that enables a user to select a date by using a
158/// visual calendar display.
159///
160/// A Calendar control can be used on its own, or as a drop-down part of a
161/// date picker control. A Calendar displays either the days of a month, the
162/// months of a year, or the years of a decade, depending on the value of
163/// the DisplayMode property.  When displaying the days of a month, the user
164/// can select a date, a range of dates, or multiple ranges of dates.  The
165/// kinds of selections that are allowed are controlled by the SelectionMode
166/// property.
167///
168/// The range of dates displayed is governed by the DisplayDateStart and
169/// DisplayDateEnd properties.  If DisplayMode is Year or Decade, only
170/// months or years that contain displayable dates will be displayed.
171///
172/// The BlackoutDates property can be used to specify dates that cannot be
173/// selected. These dates will be displayed as dimmed and disabled.
174///
175/// By default, Today is highlighted.  This can be disabled by setting
176/// IsTodayHighlighted to false.
177///
178/// The Calendar control provides basic navigation using either the mouse or
179/// keyboard. The following table summarizes keyboard navigation.
180///
181/// ```text
182///     Key Combination     DisplayMode     Action
183///     ARROW               Any             Change focused date, unselect
184///                                         all selected dates, and select
185///                                         new focused date.
186///
187///     SHIFT+ARROW         Any             If SelectionMode is not set to
188///                                         SingleDate or None begin
189///                                         selecting a range of dates.
190///
191///     CTRL+UP ARROW       Any             Switch to the next larger
192///                                         DisplayMode.  If DisplayMode is
193///                                         already Decade, no action.
194///
195///     CTRL+DOWN ARROW     Any             Switch to the next smaller
196///                                         DisplayMode.  If DisplayMode is
197///                                         already Month, no action.
198///
199///     SPACEBAR            Month           Select focused date.
200///
201///     SPACEBAR            Year or Decade  Switch DisplayMode to the Month
202///                                         or Year represented by focused
203///                                         item.
204/// ```
205#[repr(C)]
206pub struct Calendar {
207    base: TemplatedControl,
208
209    selected_month: Cell<DateTime>,
210    selected_year: Cell<DateTime>,
211
212    is_shift_pressed: Cell<bool>,
213    display_date_is_changing: Cell<bool>,
214    is_tap_range_selection_active: Cell<bool>,
215    tap_range_start: Cell<Option<DateTime>>,
216
217    focus_button: RefCell<Option<Ref<CalendarDayButton>>>,
218    focus_calendar_button: RefCell<Option<Ref<CalendarButton>>>,
219
220    root: RefCell<Option<Ref<Panel>>>,
221
222    selected_dates: OnceCell<SelectedDatesCollection>,
223    blackout_dates: OnceCell<CalendarBlackoutDatesCollection>,
224    removed_items: RefCell<Vec<DateTime>>,
225    last_selected_date_internal: Cell<Option<DateTime>>,
226    display_date_internal: Cell<DateTime>,
227
228    hover_start: Cell<Option<DateTime>>,
229    hover_start_index: Cell<Option<i32>>,
230    hover_end_internal: Cell<Option<DateTime>>,
231    hover_end_index: Cell<Option<i32>>,
232    has_focus_internal: Cell<bool>,
233    is_mouse_selection: Cell<bool>,
234
235    /// A value indicating whether the date picker should change its
236    /// DisplayDate because of a SelectedDate change on its Calendar.
237    calendar_date_picker_display_date_flag: Cell<bool>,
238
239    selected_dates_changed: HandlerList<dyn Fn(&SelectionChangedEventArgs)>,
240    display_date_changed: HandlerList<dyn Fn(&CalendarDateChangedEventArgs)>,
241    display_mode_changed: HandlerList<dyn Fn(&CalendarModeChangedEventArgs)>,
242    day_button_mouse_up: HandlerList<dyn Fn(&PointerReleasedEventArgs)>,
243}
244
245ferro_class!(Calendar: TemplatedControl);
246
247ferro_class_info!(Calendar {
248    new: Calendar::new,
249    markup: {
250        properties: [
251            SelectedDates: SelectedDatesCollection { get: Calendar::selected_dates },
252            BlackoutDates: CalendarBlackoutDatesCollection { get: Calendar::blackout_dates },
253        ],
254        attributes: [
255            TemplatePart("PART_CalendarItem", type(Ref<CalendarItem>)),
256            TemplatePart("PART_Root", type(Ref<Panel>)),
257        ],
258    },
259});
260
261ferro_impl_classes!(Calendar: StyledElementImpl, VisualImpl, LayoutableImpl, InteractiveImpl);
262
263impl FerroObjectImpl for Calendar {
264    /// Initializes a new instance of the [`Calendar`] class.
265    fn constructed(this: &Self) {
266        Self::parent_constructed(this);
267
268        this.set_current_value(Self::display_date_property(), DateTime::today());
269        Self::update_display_date(this, this.display_date(), DateTime::MIN_VALUE);
270        let this_ref = this.to_ref();
271        let _ = this.blackout_dates.set(CalendarBlackoutDatesCollection::new(&this_ref));
272        let _ = this.selected_dates.set(SelectedDatesCollection::new(&this_ref));
273    }
274}
275
276impl InputElementImpl for Calendar {
277    fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
278        Self::parent_on_pointer_released(this, e);
279        if !this.has_focus_internal() && e.initial_press_mouse_button() == MouseButton::Left {
280            this.focus();
281        }
282    }
283
284    /// Default mouse wheel handler for the calendar control.
285    fn on_pointer_wheel_changed(this: &Self, e: &PointerWheelEventArgs) {
286        Self::parent_on_pointer_wheel_changed(this, e);
287        if !e.handled() {
288            let (ctrl, shift) = CalendarExtensions::get_meta_key_state(e.key_modifiers());
289
290            if !ctrl {
291                if e.delta().y > 0.0 {
292                    this.process_page_up_key(false);
293                } else {
294                    this.process_page_down_key(false);
295                }
296            } else if e.delta().y > 0.0 {
297                this.process_down_key(ctrl, shift);
298            } else {
299                this.process_up_key(ctrl, shift);
300            }
301            e.set_handled(true);
302        }
303    }
304
305    fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
306        Self::parent_on_got_focus(this, e);
307        this.has_focus_internal.set(true);
308
309        match this.display_mode() {
310            CalendarMode::Month => {
311                let focus_date;
312                match this.last_selected_date() {
313                    Some(last_selected_date)
314                        if DateTimeHelper::compare_year_month(this.display_date_internal(), last_selected_date) == 0 =>
315                    {
316                        focus_date = last_selected_date;
317                    }
318                    _ => {
319                        focus_date = this.display_date();
320                        this.set_last_selected_date(Some(this.display_date()));
321                    }
322                }
323                let focus_button = this.find_day_button_from_day(focus_date);
324                this.set_focus_button(focus_button.clone());
325
326                if let Some(focus_button) = focus_button {
327                    focus_button.set_is_current(true);
328                }
329            }
330            CalendarMode::Year | CalendarMode::Decade => {
331                if let Some(focus_calendar_button) = this.focus_calendar_button() {
332                    focus_calendar_button.set_is_calendar_button_focused(true);
333                }
334            }
335        }
336    }
337
338    fn on_lost_focus(this: &Self, e: &FocusChangedEventArgs) {
339        Self::parent_on_lost_focus(this, e);
340        this.has_focus_internal.set(false);
341
342        match this.display_mode() {
343            CalendarMode::Month => {
344                if let Some(focus_button) = this.focus_button() {
345                    focus_button.set_is_current(false);
346                }
347            }
348            CalendarMode::Year | CalendarMode::Decade => {
349                if let Some(focus_calendar_button) = this.focus_calendar_button() {
350                    focus_calendar_button.set_is_calendar_button_focused(false);
351                }
352            }
353        }
354    }
355}
356
357impl TemplatedControlImpl for Calendar {
358    /// Builds the visual tree for the [`Calendar`] when a new template is
359    /// applied.
360    fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
361        let root = e.name_scope().find_as::<Panel>(PART_ELEMENT_ROOT);
362        *this.root.borrow_mut() = root.clone();
363
364        this.set_selected_month(this.display_date());
365        this.set_selected_year(this.display_date());
366
367        if root.is_some() {
368            let month = e.name_scope().find_as::<CalendarItem>(PART_ELEMENT_MONTH);
369
370            if let Some(month) = month {
371                month.set_owner(Some(&this.to_ref()));
372            }
373        }
374    }
375}
376
377impl ControlImpl for Calendar {
378    fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
379        crate::automation::peers::CalendarAutomationPeer::new(this).upcast()
380    }
381}
382
383ferro_properties! {
384    impl Calendar {
385        /// Defines the `FirstDayOfWeek` property.
386        pub fn first_day_of_week_property() -> StyledProperty<DayOfWeek> {
387            FerroProperty::register::<Calendar, _>(
388                "FirstDayOfWeek",
389                DateTimeHelper::get_current_date_format().first_day_of_week(),
390            )
391        }
392
393        /// Defines the `IsTodayHighlighted` property.
394        pub fn is_today_highlighted_property() -> StyledProperty<bool> {
395            FerroProperty::register::<Calendar, _>("IsTodayHighlighted", true)
396        }
397
398        /// Defines the `HeaderBackground` property.
399        pub fn header_background_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
400            FerroProperty::register::<Calendar, _>("HeaderBackground", None)
401        }
402
403        /// Defines the `IsWeekNumberVisible` property.
404        pub fn is_week_number_visible_property() -> StyledProperty<bool> {
405            FerroProperty::register::<Calendar, _>("IsWeekNumberVisible", false)
406        }
407
408        /// Defines the `WeekNumberRule` property.
409        pub fn week_number_rule_property() -> StyledProperty<CalendarWeekRule> {
410            FerroProperty::register::<Calendar, _>(
411                "WeekNumberRule",
412                DateTimeHelper::get_current_date_format().calendar_week_rule(),
413            )
414        }
415
416        /// Defines the `DisplayMode` property.
417        pub fn display_mode_property() -> StyledProperty<CalendarMode> {
418            FerroProperty::register_with::<Calendar, _>(
419                "DisplayMode",
420                StyledPropertyOptions::new(CalendarMode::Month).validate(Calendar::is_valid_display_mode),
421            )
422        }
423
424        /// Defines the `SelectionMode` property.
425        pub fn selection_mode_property() -> StyledProperty<CalendarSelectionMode> {
426            FerroProperty::register::<Calendar, _>("SelectionMode", CalendarSelectionMode::SingleDate)
427        }
428
429        /// Defines the `AllowTapRangeSelection` property.
430        pub fn allow_tap_range_selection_property() -> StyledProperty<bool> {
431            FerroProperty::register::<Calendar, _>("AllowTapRangeSelection", true)
432        }
433
434        /// Defines the `SelectedDate` property.
435        pub fn selected_date_property() -> StyledProperty<Option<DateTime>> {
436            FerroProperty::register_with::<Calendar, _>(
437                "SelectedDate",
438                StyledPropertyOptions::new(None).default_binding_mode(BindingMode::TwoWay),
439            )
440        }
441
442        /// Defines the `DisplayDate` property.
443        pub fn display_date_property() -> StyledProperty<DateTime> {
444            FerroProperty::register_with::<Calendar, _>(
445                "DisplayDate",
446                StyledPropertyOptions::new(DateTime::MIN_VALUE).default_binding_mode(BindingMode::TwoWay),
447            )
448        }
449
450        /// Defines the `DisplayDateStart` property.
451        pub fn display_date_start_property() -> StyledProperty<Option<DateTime>> {
452            FerroProperty::register_with::<Calendar, _>(
453                "DisplayDateStart",
454                StyledPropertyOptions::new(None).default_binding_mode(BindingMode::TwoWay),
455            )
456        }
457
458        /// Defines the `DisplayDateEnd` property.
459        pub fn display_date_end_property() -> StyledProperty<Option<DateTime>> {
460            FerroProperty::register_with::<Calendar, _>(
461                "DisplayDateEnd",
462                StyledPropertyOptions::new(None).default_binding_mode(BindingMode::TwoWay),
463            )
464        }
465    }
466}
467
468impl Calendar {
469    pub(crate) const ROWS_PER_MONTH: i32 = 7;
470    pub(crate) const COLUMNS_PER_MONTH: i32 = 7;
471    pub(crate) const ROWS_PER_YEAR: i32 = 3;
472    pub(crate) const COLUMNS_PER_YEAR: i32 = 4;
473
474    fn static_constructor() {
475        InputElement::is_enabled_property()
476            .changed()
477            .add_class_handler::<Calendar>(|x, e| x.on_is_enabled_changed(e.get_new_value::<bool>()));
478        Self::first_day_of_week_property()
479            .changed()
480            .add_class_handler::<Calendar>(|x, e| x.on_first_day_of_week_changed(e.get_new_value::<DayOfWeek>()));
481        Self::is_today_highlighted_property()
482            .changed()
483            .add_class_handler::<Calendar>(|x, _| x.on_is_today_highlighted_changed());
484        Self::display_mode_property().changed().add_class_handler::<Calendar>(|x, e| {
485            let (old_mode, mode) = e.get_old_and_new_value::<CalendarMode>();
486            x.on_display_mode_property_changed(old_mode, mode);
487        });
488        Self::selection_mode_property().changed().add_class_handler::<Calendar>(|x, e| {
489            x.on_selection_mode_changed(e.get_new_value::<CalendarSelectionMode>())
490        });
491        Self::allow_tap_range_selection_property()
492            .changed()
493            .add_class_handler::<Calendar>(|x, _| x.on_allow_tap_range_selection_changed());
494        Self::selected_date_property()
495            .changed()
496            .add_class_handler::<Calendar>(|x, e| x.on_selected_date_changed(e.get_new_value::<Option<DateTime>>()));
497        Self::display_date_property().changed().add_class_handler::<Calendar>(|x, e| {
498            let (old_value, new_value) = e.get_old_and_new_value::<DateTime>();
499            x.on_display_date_changed(new_value, old_value);
500        });
501        Self::display_date_start_property().changed().add_class_handler::<Calendar>(|x, e| {
502            x.on_display_date_start_changed(e.get_new_value::<Option<DateTime>>())
503        });
504        Self::display_date_end_property()
505            .changed()
506            .add_class_handler::<Calendar>(|x, e| x.on_display_date_end_changed(e.get_new_value::<Option<DateTime>>()));
507        Self::is_week_number_visible_property().changed().add_class_handler::<Calendar>(|x, _| x.update_months());
508        Self::week_number_rule_property().changed().add_class_handler::<Calendar>(|x, _| x.update_months());
509        InputElement::key_down_event().add_class_handler::<Calendar>(|x, e| x.calendar_key_down(e));
510        InputElement::key_up_event().add_class_handler::<Calendar>(|x, e| x.calendar_key_up(e));
511    }
512
513    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
514    pub fn construct() -> Self {
515        Self {
516            base: TemplatedControl::construct(),
517            selected_month: Cell::new(DateTime::MIN_VALUE),
518            selected_year: Cell::new(DateTime::MIN_VALUE),
519            is_shift_pressed: Cell::new(false),
520            display_date_is_changing: Cell::new(false),
521            is_tap_range_selection_active: Cell::new(false),
522            tap_range_start: Cell::new(None),
523            focus_button: RefCell::new(None),
524            focus_calendar_button: RefCell::new(None),
525            root: RefCell::new(None),
526            selected_dates: OnceCell::new(),
527            blackout_dates: OnceCell::new(),
528            removed_items: RefCell::new(Vec::new()),
529            last_selected_date_internal: Cell::new(None),
530            display_date_internal: Cell::new(DateTime::MIN_VALUE),
531            hover_start: Cell::new(None),
532            hover_start_index: Cell::new(None),
533            hover_end_internal: Cell::new(None),
534            hover_end_index: Cell::new(None),
535            has_focus_internal: Cell::new(false),
536            is_mouse_selection: Cell::new(false),
537            calendar_date_picker_display_date_flag: Cell::new(false),
538            selected_dates_changed: HandlerList::new(),
539            display_date_changed: HandlerList::new(),
540            display_mode_changed: HandlerList::new(),
541            day_button_mouse_up: HandlerList::new(),
542        }
543    }
544
545    /// Initializes a new instance of the [`Calendar`] class.
546    pub fn new() -> Ref<Self> {
547        instantiate(Self::construct())
548    }
549
550    pub(crate) fn focus_button(&self) -> Option<Ref<CalendarDayButton>> {
551        self.focus_button.borrow().clone()
552    }
553
554    pub(crate) fn set_focus_button(&self, value: Option<Ref<CalendarDayButton>>) {
555        *self.focus_button.borrow_mut() = value;
556    }
557
558    pub(crate) fn focus_calendar_button(&self) -> Option<Ref<CalendarButton>> {
559        self.focus_calendar_button.borrow().clone()
560    }
561
562    pub(crate) fn set_focus_calendar_button(&self, value: Option<Ref<CalendarButton>>) {
563        *self.focus_calendar_button.borrow_mut() = value;
564    }
565
566    pub(crate) fn root(&self) -> Option<Ref<Panel>> {
567        self.root.borrow().clone()
568    }
569
570    pub(crate) fn set_root(&self, value: Option<Ref<Panel>>) {
571        *self.root.borrow_mut() = value;
572    }
573
574    pub(crate) fn month_control(&self) -> Option<Ref<CalendarItem>> {
575        let root = self.root()?;
576        if root.children().count() > 0 {
577            return root.children().get(0).cast::<CalendarItem>();
578        }
579        None
580    }
581
582    /// Gets or sets the day that is considered the beginning of the week.
583    /// The default is the first day of the week of the culture that was
584    /// current when the class was initialised.
585    pub fn first_day_of_week(&self) -> DayOfWeek {
586        self.get_value(Self::first_day_of_week_property())
587    }
588
589    pub fn set_first_day_of_week(&self, value: DayOfWeek) {
590        self.set_value(Self::first_day_of_week_property(), value)
591    }
592
593    /// FirstDayOfWeekProperty property changed handler.
594    fn on_first_day_of_week_changed(&self, new_value: DayOfWeek) {
595        if Self::is_valid_first_day_of_week(new_value) {
596            self.update_months();
597        } else {
598            panic!("Invalid DayOfWeek (Parameter 'e')");
599        }
600    }
601
602    /// Whether a value is a day of the week. Every value of the
603    /// enumeration is one.
604    fn is_valid_first_day_of_week(day: DayOfWeek) -> bool {
605        matches!(
606            day,
607            DayOfWeek::Sunday
608                | DayOfWeek::Monday
609                | DayOfWeek::Tuesday
610                | DayOfWeek::Wednesday
611                | DayOfWeek::Thursday
612                | DayOfWeek::Friday
613                | DayOfWeek::Saturday
614        )
615    }
616
617    /// Gets or sets a value indicating whether the current date is
618    /// highlighted. The default is true.
619    pub fn is_today_highlighted(&self) -> bool {
620        self.get_value(Self::is_today_highlighted_property())
621    }
622
623    pub fn set_is_today_highlighted(&self, value: bool) {
624        self.set_value(Self::is_today_highlighted_property(), value)
625    }
626
627    /// IsTodayHighlightedProperty property changed handler.
628    fn on_is_today_highlighted_changed(&self) {
629        let i = DateTimeHelper::compare_year_month(self.display_date_internal(), DateTime::today());
630
631        if i > -2 && i < 2 {
632            self.update_months();
633        }
634    }
635
636    /// The brush of the background of the header.
637    pub fn header_background(&self) -> Option<Rc<dyn IBrush>> {
638        self.get_value(Self::header_background_property())
639    }
640
641    pub fn set_header_background(&self, value: Option<Rc<dyn IBrush>>) {
642        self.set_value(Self::header_background_property(), value)
643    }
644
645    /// Gets or sets a value indicating whether week numbers are shown in
646    /// the month view.
647    pub fn is_week_number_visible(&self) -> bool {
648        self.get_value(Self::is_week_number_visible_property())
649    }
650
651    pub fn set_is_week_number_visible(&self, value: bool) {
652        self.set_value(Self::is_week_number_visible_property(), value)
653    }
654
655    /// Gets or sets the rule used to determine the first week of the year
656    /// for week number display. The default is taken from the culture that
657    /// was current when the class was initialised.
658    ///
659    /// Use [`CalendarWeekRule::FirstFourDayWeek`] in combination with a
660    /// first day of the week of [`DayOfWeek::Monday`] for ISO 8601 week
661    /// numbering.
662    pub fn week_number_rule(&self) -> CalendarWeekRule {
663        self.get_value(Self::week_number_rule_property())
664    }
665
666    pub fn set_week_number_rule(&self, value: CalendarWeekRule) {
667        self.set_value(Self::week_number_rule_property(), value)
668    }
669
670    /// Gets or sets a value indicating whether the calendar is displayed in
671    /// months, years, or decades.
672    pub fn display_mode(&self) -> CalendarMode {
673        self.get_value(Self::display_mode_property())
674    }
675
676    pub fn set_display_mode(&self, value: CalendarMode) {
677        self.set_value(Self::display_mode_property(), value)
678    }
679
680    /// DisplayModeProperty property changed handler.
681    fn on_display_mode_property_changed(&self, old_mode: CalendarMode, mode: CalendarMode) {
682        let month_control = self.month_control();
683
684        if month_control.is_some() {
685            match old_mode {
686                CalendarMode::Month => {
687                    self.set_selected_year(self.display_date_internal());
688                    self.set_selected_month(self.display_date_internal());
689                }
690                CalendarMode::Year => {
691                    self.set_current_value(Self::display_date_property(), self.selected_month());
692                    self.set_selected_year(self.selected_month());
693                }
694                CalendarMode::Decade => {
695                    self.set_current_value(Self::display_date_property(), self.selected_year());
696                    self.set_selected_month(self.selected_year());
697                }
698            }
699
700            match mode {
701                CalendarMode::Month => self.on_month_click(),
702                CalendarMode::Year | CalendarMode::Decade => self.on_header_click(),
703            }
704        }
705        if let Some(month_control) = &month_control {
706            month_control.update_week_number_labels_visibility();
707        }
708        self.on_display_mode_changed(&CalendarModeChangedEventArgs::new(old_mode, mode));
709    }
710
711    fn is_valid_display_mode(mode: &CalendarMode) -> bool {
712        matches!(mode, CalendarMode::Month | CalendarMode::Year | CalendarMode::Decade)
713    }
714
715    fn on_display_mode_changed(&self, args: &CalendarModeChangedEventArgs) {
716        for (_, handler) in self.display_mode_changed.snapshot().iter() {
717            handler(args);
718        }
719    }
720
721    /// Gets or sets a value that indicates what kind of selections are
722    /// allowed. The default is [`CalendarSelectionMode::SingleDate`].
723    ///
724    /// This property determines whether the Calendar allows no selection,
725    /// selection of a single date, or selection of multiple dates.
726    ///
727    /// When this property is changed, all selected dates will be cleared.
728    pub fn selection_mode(&self) -> CalendarSelectionMode {
729        self.get_value(Self::selection_mode_property())
730    }
731
732    pub fn set_selection_mode(&self, value: CalendarSelectionMode) {
733        self.set_value(Self::selection_mode_property(), value)
734    }
735
736    /// Gets or sets a value indicating whether tap-to-select range mode is
737    /// enabled. When enabled, users can tap a start date and then tap an
738    /// end date to select a range. The default is true.
739    ///
740    /// This feature only works when SelectionMode is set to a range mode.
741    /// When enabled, the first tap selects the start date, and the second
742    /// tap selects the end date to complete the range. Tapping a third date
743    /// starts a new range.
744    pub fn allow_tap_range_selection(&self) -> bool {
745        self.get_value(Self::allow_tap_range_selection_property())
746    }
747
748    pub fn set_allow_tap_range_selection(&self, value: bool) {
749        self.set_value(Self::allow_tap_range_selection_property(), value)
750    }
751
752    fn on_selection_mode_changed(&self, new_value: CalendarSelectionMode) {
753        if Self::is_valid_selection_mode(new_value) {
754            self.display_date_is_changing.set(true);
755            self.set_current_value(Self::selected_date_property(), None);
756            self.display_date_is_changing.set(false);
757            self.selected_dates().clear();
758
759            // Reset tap range selection state when mode changes
760            self.is_tap_range_selection_active.set(false);
761            self.tap_range_start.set(None);
762        } else {
763            panic!("Invalid SelectionMode (Parameter 'e')");
764        }
765    }
766
767    fn on_allow_tap_range_selection_changed(&self) {
768        self.is_tap_range_selection_active.set(false);
769        self.tap_range_start.set(None);
770    }
771
772    /// Whether a value is a selection mode. Every value of the enumeration
773    /// is one.
774    fn is_valid_selection_mode(mode: CalendarSelectionMode) -> bool {
775        matches!(
776            mode,
777            CalendarSelectionMode::SingleDate
778                | CalendarSelectionMode::SingleRange
779                | CalendarSelectionMode::MultipleRange
780                | CalendarSelectionMode::None
781        )
782    }
783
784    /// Gets or sets the currently selected date. The default is `None`.
785    ///
786    /// Setting it panics when the given date is in the blackout dates, or
787    /// when it is set to anything other than `None` while the selection
788    /// mode is [`CalendarSelectionMode::None`].
789    ///
790    /// Use this property when SelectionMode is set to SingleDate.  In other
791    /// modes, this property will always be the first date in SelectedDates.
792    pub fn selected_date(&self) -> Option<DateTime> {
793        self.get_value(Self::selected_date_property())
794    }
795
796    pub fn set_selected_date(&self, value: Option<DateTime>) {
797        self.set_value(Self::selected_date_property(), value)
798    }
799
800    fn on_selected_date_changed(&self, added_date: Option<DateTime>) {
801        if !self.display_date_is_changing.get() {
802            if self.selection_mode() != CalendarSelectionMode::None {
803                if Self::is_valid_date_selection(self, added_date) {
804                    let selected_dates = self.selected_dates();
805                    match added_date {
806                        None => selected_dates.clear(),
807                        Some(added_date) => {
808                            if !(selected_dates.count() > 0 && selected_dates.get(0) == added_date) {
809                                self.add_removed_items(&selected_dates.to_vec());
810                                selected_dates.clear_internal();
811                                // the value is added as a range so that the
812                                // SelectedDatesChanged event can be thrown with
813                                // all the removed items
814                                selected_dates.add_range(added_date, added_date);
815                            }
816                        }
817                    }
818
819                    // We update the LastSelectedDate for only the Single
820                    // mode.  For the other modes it automatically gets
821                    // updated when the HoverEnd is updated.
822                    if self.selection_mode() == CalendarSelectionMode::SingleDate {
823                        self.set_last_selected_date(added_date);
824                    }
825                } else {
826                    panic!("SelectedDate value is not valid. (Parameter 'e')");
827                }
828            } else {
829                panic!("The SelectedDate property cannot be set when the selection mode is None.");
830            }
831        }
832    }
833
834    /// Gets a collection of selected dates. The default is an empty
835    /// collection.
836    ///
837    /// Dates can be added to the collection either individually or in a
838    /// range using the AddRange method.  Depending on the value of the
839    /// SelectionMode property, adding a date or range to the collection may
840    /// cause it to be cleared.  The following table lists how the selection
841    /// mode affects the SelectedDates property.
842    ///
843    /// ```text
844    ///     CalendarSelectionMode   Description
845    ///     None                    No selections are allowed.  SelectedDate
846    ///                             cannot be set and no values can be added
847    ///                             to SelectedDates.
848    ///
849    ///     SingleDate              Only a single date can be selected,
850    ///                             either by setting SelectedDate or the
851    ///                             first value in SelectedDates.  AddRange
852    ///                             cannot be used.
853    ///
854    ///     SingleRange             A single range of dates can be selected.
855    ///                             Setting SelectedDate, adding a date
856    ///                             individually to SelectedDates, or using
857    ///                             AddRange will clear all previous values
858    ///                             from SelectedDates.
859    ///     MultipleRange           Multiple non-contiguous ranges of dates
860    ///                             can be selected. Adding a date
861    ///                             individually to SelectedDates or using
862    ///                             AddRange will not clear SelectedDates.
863    ///                             Setting SelectedDate will still clear
864    ///                             SelectedDates, but additional dates or
865    ///                             range can then be added.  Adding a range
866    ///                             that includes some dates that are
867    ///                             already selected or overlaps with
868    ///                             another range results in the union of
869    ///                             the ranges and does not cause an
870    ///                             exception.
871    /// ```
872    pub fn selected_dates(&self) -> SelectedDatesCollection {
873        self.selected_dates.get().expect("the calendar is constructed").clone()
874    }
875
876    fn is_selection_changed(e: &SelectionChangedEventArgs) -> bool {
877        if e.added_items().len() != e.removed_items().len() {
878            return true;
879        }
880        for added_date in e.added_items() {
881            let added_date = date_of_item(added_date);
882            if !e.removed_items().iter().any(|removed_date| date_of_item(removed_date) == added_date) {
883                return true;
884            }
885        }
886        false
887    }
888
889    pub(crate) fn on_selected_dates_collection_changed(&self, e: &SelectionChangedEventArgs) {
890        if Self::is_selection_changed(e) {
891            e.set_routed_event(Some(SelectingItemsControl::selection_changed_event()));
892            e.set_source(self.to_ref());
893            for (_, handler) in self.selected_dates_changed.snapshot().iter() {
894                handler(e);
895            }
896        }
897    }
898
899    /// The dates removed from the selection that the next notification of
900    /// a selection change reports.
901    pub(crate) fn removed_items(&self) -> Vec<DateTime> {
902        self.removed_items.borrow().clone()
903    }
904
905    pub(crate) fn add_removed_items(&self, items: &[DateTime]) {
906        self.removed_items.borrow_mut().extend_from_slice(items);
907    }
908
909    pub(crate) fn clear_removed_items(&self) {
910        self.removed_items.borrow_mut().clear();
911    }
912
913    pub(crate) fn last_selected_date_internal(&self) -> Option<DateTime> {
914        self.last_selected_date_internal.get()
915    }
916
917    pub(crate) fn last_selected_date(&self) -> Option<DateTime> {
918        self.last_selected_date_internal()
919    }
920
921    pub(crate) fn set_last_selected_date(&self, value: Option<DateTime>) {
922        self.last_selected_date_internal.set(value);
923
924        if self.selection_mode() == CalendarSelectionMode::None {
925            if let Some(focus_button) = self.focus_button() {
926                focus_button.set_is_current(false);
927            }
928            let focus_button = self
929                .find_day_button_from_day(self.last_selected_date().expect("Nullable object must have a value."));
930            self.set_focus_button(focus_button.clone());
931            if let Some(focus_button) = focus_button {
932                focus_button.set_is_current(self.has_focus_internal());
933            }
934        }
935    }
936
937    pub(crate) fn selected_month(&self) -> DateTime {
938        self.selected_month.get()
939    }
940
941    pub(crate) fn set_selected_month(&self, value: DateTime) {
942        let month_difference_start = DateTimeHelper::compare_year_month(value, self.display_date_range_start());
943        let month_difference_end = DateTimeHelper::compare_year_month(value, self.display_date_range_end());
944
945        if month_difference_start >= 0 && month_difference_end <= 0 {
946            self.selected_month.set(DateTimeHelper::discard_day_time(value));
947        } else if month_difference_start < 0 {
948            self.selected_month.set(DateTimeHelper::discard_day_time(self.display_date_range_start()));
949        } else {
950            debug_assert!(month_difference_end > 0, "monthDifferenceEnd should be greater than 0!");
951            self.selected_month.set(DateTimeHelper::discard_day_time(self.display_date_range_end()));
952        }
953    }
954
955    pub(crate) fn selected_year(&self) -> DateTime {
956        self.selected_year.get()
957    }
958
959    pub(crate) fn set_selected_year(&self, value: DateTime) {
960        if value.year() < self.display_date_range_start().year() {
961            self.selected_year.set(self.display_date_range_start());
962        } else if value.year() > self.display_date_range_end().year() {
963            self.selected_year.set(self.display_date_range_end());
964        } else {
965            self.selected_year.set(value);
966        }
967    }
968
969    /// Gets or sets the date to display. The default is today.
970    ///
971    /// A date that is not in the range specified by the DisplayDateStart
972    /// and DisplayDateEnd properties is replaced by the nearest end of
973    /// that range.
974    pub fn display_date(&self) -> DateTime {
975        self.get_value(Self::display_date_property())
976    }
977
978    pub fn set_display_date(&self, value: DateTime) {
979        self.set_value(Self::display_date_property(), value)
980    }
981
982    pub(crate) fn display_date_internal(&self) -> DateTime {
983        self.display_date_internal.get()
984    }
985
986    fn on_display_date_changed(&self, new_value: DateTime, old_value: DateTime) {
987        Self::update_display_date(self, new_value, old_value);
988    }
989
990    fn update_display_date(c: &Calendar, added_date: DateTime, removed_date: DateTime) {
991        // If DisplayDate < DisplayDateStart, DisplayDate = DisplayDateStart
992        if DateTime::compare(added_date, c.display_date_range_start()) < 0 {
993            c.set_display_date(c.display_date_range_start());
994            return;
995        }
996
997        // If DisplayDate > DisplayDateEnd, DisplayDate = DisplayDateEnd
998        if DateTime::compare(added_date, c.display_date_range_end()) > 0 {
999            c.set_display_date(c.display_date_range_end());
1000            return;
1001        }
1002
1003        c.display_date_internal.set(DateTimeHelper::discard_day_time(added_date));
1004        c.update_months();
1005        c.on_display_date(&CalendarDateChangedEventArgs::new(Some(removed_date), Some(added_date)));
1006    }
1007
1008    fn on_display_date(&self, e: &CalendarDateChangedEventArgs) {
1009        for (_, handler) in self.display_date_changed.snapshot().iter() {
1010            handler(e);
1011        }
1012    }
1013
1014    /// Gets or sets the first date to be displayed.
1015    pub fn display_date_start(&self) -> Option<DateTime> {
1016        self.get_value(Self::display_date_start_property())
1017    }
1018
1019    pub fn set_display_date_start(&self, value: Option<DateTime>) {
1020        self.set_value(Self::display_date_start_property(), value)
1021    }
1022
1023    fn on_display_date_start_changed(&self, new_value: Option<DateTime>) {
1024        if !self.display_date_is_changing.get() {
1025            if let Some(new_value) = new_value {
1026                // DisplayDateStart coerces to the value of the
1027                // SelectedDateMin if SelectedDateMin < DisplayDateStart
1028                let selected_date_min = Self::selected_date_min(self);
1029
1030                if let Some(selected_date_min) = selected_date_min {
1031                    if DateTime::compare(selected_date_min, new_value) < 0 {
1032                        self.set_current_value(Self::display_date_start_property(), Some(selected_date_min));
1033                        return;
1034                    }
1035                }
1036
1037                // if DisplayDateStart > DisplayDateEnd,
1038                // DisplayDateEnd = DisplayDateStart
1039                if DateTime::compare(new_value, self.display_date_range_end()) > 0 {
1040                    self.set_current_value(Self::display_date_end_property(), self.display_date_start());
1041                }
1042
1043                // If DisplayDate < DisplayDateStart,
1044                // DisplayDate = DisplayDateStart
1045                if DateTimeHelper::compare_year_month(new_value, self.display_date_internal()) > 0 {
1046                    self.set_current_value(Self::display_date_property(), new_value);
1047                }
1048            }
1049            self.update_months();
1050        }
1051    }
1052
1053    /// Gets a collection of dates that are marked as not selectable. The
1054    /// default value is an empty collection.
1055    ///
1056    /// Adding a date to this collection when it is already selected
1057    /// panics.
1058    ///
1059    /// Dates in this collection will appear as disabled on the calendar.
1060    ///
1061    /// To make all past dates not selectable, you can use the
1062    /// `add_dates_in_past` method provided by the collection returned by
1063    /// this property.
1064    pub fn blackout_dates(&self) -> CalendarBlackoutDatesCollection {
1065        self.blackout_dates.get().expect("the calendar is constructed").clone()
1066    }
1067
1068    fn selected_date_min(cal: &Calendar) -> Option<DateTime> {
1069        let selected_dates = cal.selected_dates().to_vec();
1070        let mut selected_date_min = *selected_dates.first()?;
1071        debug_assert!(
1072            cal.selected_date().is_some_and(|selected| DateTime::compare(selected, selected_date_min) == 0),
1073            "The SelectedDate should be the minimum selected date!"
1074        );
1075
1076        for selected_date in selected_dates {
1077            if DateTime::compare(selected_date, selected_date_min) < 0 {
1078                selected_date_min = selected_date;
1079            }
1080        }
1081        Some(selected_date_min)
1082    }
1083
1084    pub(crate) fn display_date_range_start(&self) -> DateTime {
1085        self.display_date_start().unwrap_or(DateTime::MIN_VALUE)
1086    }
1087
1088    /// Gets or sets the last date to be displayed.
1089    pub fn display_date_end(&self) -> Option<DateTime> {
1090        self.get_value(Self::display_date_end_property())
1091    }
1092
1093    pub fn set_display_date_end(&self, value: Option<DateTime>) {
1094        self.set_value(Self::display_date_end_property(), value)
1095    }
1096
1097    fn on_display_date_end_changed(&self, new_value: Option<DateTime>) {
1098        if !self.display_date_is_changing.get() {
1099            if let Some(new_value) = new_value {
1100                // DisplayDateEnd coerces to the value of the
1101                // SelectedDateMax if SelectedDateMax > DisplayDateEnd
1102                let selected_date_max = Self::selected_date_max(self);
1103
1104                if let Some(selected_date_max) = selected_date_max {
1105                    if DateTime::compare(selected_date_max, new_value) > 0 {
1106                        self.set_current_value(Self::display_date_end_property(), Some(selected_date_max));
1107                        return;
1108                    }
1109                }
1110
1111                // if DisplayDateEnd < DisplayDateStart,
1112                // DisplayDateEnd = DisplayDateStart
1113                if DateTime::compare(new_value, self.display_date_range_start()) < 0 {
1114                    self.set_current_value(Self::display_date_end_property(), self.display_date_start());
1115                    return;
1116                }
1117
1118                // If DisplayDate > DisplayDateEnd,
1119                // DisplayDate = DisplayDateEnd
1120                if DateTimeHelper::compare_year_month(new_value, self.display_date_internal()) < 0 {
1121                    self.set_current_value(Self::display_date_property(), new_value);
1122                }
1123            }
1124            self.update_months();
1125        }
1126    }
1127
1128    fn selected_date_max(cal: &Calendar) -> Option<DateTime> {
1129        let selected_dates = cal.selected_dates().to_vec();
1130        let mut selected_date_max = *selected_dates.first()?;
1131        debug_assert!(
1132            cal.selected_date().is_some_and(|selected| DateTime::compare(selected, selected_date_max) == 0),
1133            "The SelectedDate should be the maximum SelectedDate!"
1134        );
1135
1136        for selected_date in selected_dates {
1137            if DateTime::compare(selected_date, selected_date_max) > 0 {
1138                selected_date_max = selected_date;
1139            }
1140        }
1141        Some(selected_date_max)
1142    }
1143
1144    pub(crate) fn display_date_range_end(&self) -> DateTime {
1145        self.display_date_end().unwrap_or(DateTime::MAX_VALUE)
1146    }
1147
1148    pub(crate) fn hover_start(&self) -> Option<DateTime> {
1149        self.hover_start.get()
1150    }
1151
1152    pub(crate) fn set_hover_start(&self, value: Option<DateTime>) {
1153        self.hover_start.set(value);
1154    }
1155
1156    pub(crate) fn hover_start_index(&self) -> Option<i32> {
1157        self.hover_start_index.get()
1158    }
1159
1160    pub(crate) fn set_hover_start_index(&self, value: Option<i32>) {
1161        self.hover_start_index.set(value);
1162    }
1163
1164    pub(crate) fn hover_end_internal(&self) -> Option<DateTime> {
1165        self.hover_end_internal.get()
1166    }
1167
1168    pub(crate) fn hover_end(&self) -> Option<DateTime> {
1169        self.hover_end_internal()
1170    }
1171
1172    pub(crate) fn set_hover_end(&self, value: Option<DateTime>) {
1173        self.hover_end_internal.set(value);
1174        self.set_last_selected_date(value);
1175    }
1176
1177    pub(crate) fn hover_end_index(&self) -> Option<i32> {
1178        self.hover_end_index.get()
1179    }
1180
1181    pub(crate) fn set_hover_end_index(&self, value: Option<i32>) {
1182        self.hover_end_index.set(value);
1183    }
1184
1185    pub(crate) fn has_focus_internal(&self) -> bool {
1186        self.has_focus_internal.get()
1187    }
1188
1189    pub(crate) fn is_mouse_selection(&self) -> bool {
1190        self.is_mouse_selection.get()
1191    }
1192
1193    pub(crate) fn set_is_mouse_selection(&self, value: bool) {
1194        self.is_mouse_selection.set(value);
1195    }
1196
1197    /// Gets a value indicating whether the date picker should change its
1198    /// DisplayDate because of a SelectedDate change on its Calendar.
1199    pub(crate) fn calendar_date_picker_display_date_flag(&self) -> bool {
1200        self.calendar_date_picker_display_date_flag.get()
1201    }
1202
1203    pub(crate) fn set_calendar_date_picker_display_date_flag(&self, value: bool) {
1204        self.calendar_date_picker_display_date_flag.set(value);
1205    }
1206
1207    pub(crate) fn find_day_button_from_day(&self, day: DateTime) -> Option<Ref<CalendarDayButton>> {
1208        let month_control = self.month_control();
1209
1210        // REMOVE_RTM: should be updated if we support MultiCalendar
1211        let count = Self::ROWS_PER_MONTH * Self::COLUMNS_PER_MONTH;
1212        if let Some(month_view) = month_control.and_then(|month_control| month_control.month_view()) {
1213            for child_index in Self::COLUMNS_PER_MONTH..count {
1214                if let Some(b) = month_view.children().get(child_index as usize).cast::<CalendarDayButton>() {
1215                    if let Some(d) = date_of(&b) {
1216                        if DateTimeHelper::compare_days(d, day) == 0 {
1217                            return Some(b);
1218                        }
1219                    }
1220                }
1221            }
1222        }
1223        None
1224    }
1225
1226    fn on_selected_month_changed(&self, selected_month: Option<DateTime>) {
1227        if let Some(selected_month) = selected_month {
1228            debug_assert!(self.display_mode() == CalendarMode::Year, "DisplayMode should be Year!");
1229            self.set_selected_month(selected_month);
1230            self.update_months();
1231        }
1232    }
1233
1234    fn on_selected_year_changed(&self, selected_year: Option<DateTime>) {
1235        if let Some(selected_year) = selected_year {
1236            debug_assert!(self.display_mode() == CalendarMode::Decade, "DisplayMode should be Decade!");
1237            self.set_selected_year(selected_year);
1238            self.update_months();
1239        }
1240    }
1241
1242    pub(crate) fn on_header_click(&self) {
1243        debug_assert!(
1244            self.display_mode() == CalendarMode::Year || self.display_mode() == CalendarMode::Decade,
1245            "The DisplayMode should be Year or Decade"
1246        );
1247        if let Some(month_control) = self.month_control() {
1248            if let (Some(month_view), Some(year_view)) = (month_control.month_view(), month_control.year_view()) {
1249                month_view.set_is_visible(false);
1250                year_view.set_is_visible(true);
1251                self.update_months();
1252            }
1253        }
1254    }
1255
1256    pub(crate) fn reset_states(&self) {
1257        let month_control = self.month_control();
1258        let count = Self::ROWS_PER_MONTH * Self::COLUMNS_PER_MONTH;
1259        if let Some(month_view) = month_control.and_then(|month_control| month_control.month_view()) {
1260            for child_index in Self::COLUMNS_PER_MONTH..count {
1261                let d = month_view
1262                    .children()
1263                    .get(child_index as usize)
1264                    .cast::<CalendarDayButton>()
1265                    .expect("the child of the month view is a CalendarDayButton");
1266                d.ignore_mouse_over_state();
1267            }
1268        }
1269    }
1270
1271    pub(crate) fn update_months(&self) {
1272        if let Some(month_control) = self.month_control() {
1273            match self.display_mode() {
1274                CalendarMode::Month => month_control.update_month_mode(),
1275                CalendarMode::Year => month_control.update_year_mode(),
1276                CalendarMode::Decade => month_control.update_decade_mode(),
1277            }
1278        }
1279    }
1280
1281    pub(crate) fn is_valid_date_selection(cal: &Calendar, value: Option<DateTime>) -> bool {
1282        let Some(value) = value else {
1283            return true;
1284        };
1285
1286        if cal.blackout_dates().contains_date(value) {
1287            false
1288        } else {
1289            cal.display_date_is_changing.set(true);
1290            if DateTime::compare(value, cal.display_date_range_start()) < 0 {
1291                cal.set_display_date_start(Some(value));
1292            } else if DateTime::compare(value, cal.display_date_range_end()) > 0 {
1293                cal.set_display_date_end(Some(value));
1294            }
1295            cal.display_date_is_changing.set(false);
1296
1297            true
1298        }
1299    }
1300
1301    fn is_valid_keyboard_selection(cal: &Calendar, value: Option<DateTime>) -> bool {
1302        let Some(value) = value else {
1303            return true;
1304        };
1305
1306        if cal.blackout_dates().contains_date(value) {
1307            false
1308        } else {
1309            DateTime::compare(value, cal.display_date_range_start()) >= 0
1310                && DateTime::compare(value, cal.display_date_range_end()) <= 0
1311        }
1312    }
1313
1314    /// This method highlights the days in MultiSelection mode without
1315    /// adding them to the SelectedDates collection.
1316    pub(crate) fn highlight_days(&self) {
1317        if let (Some(hover_end), Some(_)) = (self.hover_end(), self.hover_start()) {
1318            let month_control = self.month_control();
1319            debug_assert!(month_control.is_some());
1320
1321            // This assumes a contiguous set of dates:
1322            if self.hover_end_index().is_some() && self.hover_start_index().is_some() {
1323                let month_view = month_control
1324                    .and_then(|month_control| month_control.month_view())
1325                    .expect("the calendar has a month view");
1326                let (start_index, end_index) = self.sort_hover_indexes();
1327
1328                for i in start_index..=end_index {
1329                    if let Some(b) = month_view.children().get(i as usize).cast::<CalendarDayButton>() {
1330                        b.set_is_selected(true);
1331
1332                        if date_of(&b).is_some_and(|d| DateTimeHelper::compare_days(hover_end, d) == 0) {
1333                            if let Some(focus_button) = self.focus_button() {
1334                                focus_button.set_is_current(false);
1335                            }
1336                            b.set_is_current(self.has_focus_internal());
1337                            self.set_focus_button(Some(b));
1338                        }
1339                    }
1340                }
1341            }
1342        }
1343    }
1344
1345    /// This method un-highlights the days that were hovered over but not
1346    /// added to the SelectedDates collection or un-highlighted the
1347    /// previously selected days in SingleRange Mode.
1348    pub(crate) fn un_highlight_days(&self) {
1349        if self.hover_end().is_some() && self.hover_start().is_some() {
1350            let month_control = self.month_control();
1351            debug_assert!(month_control.is_some());
1352
1353            if self.hover_end_index().is_some() && self.hover_start_index().is_some() {
1354                let month_view = month_control
1355                    .and_then(|month_control| month_control.month_view())
1356                    .expect("the calendar has a month view");
1357                let (start_index, end_index) = self.sort_hover_indexes();
1358
1359                if self.selection_mode() == CalendarSelectionMode::MultipleRange {
1360                    for i in start_index..=end_index {
1361                        if let Some(b) = month_view.children().get(i as usize).cast::<CalendarDayButton>() {
1362                            if let Some(d) = date_of(&b) {
1363                                if !self.selected_dates().contains(d) {
1364                                    b.set_is_selected(false);
1365                                }
1366                            }
1367                        }
1368                    }
1369                } else {
1370                    // It is SingleRange
1371                    for i in start_index..=end_index {
1372                        month_view
1373                            .children()
1374                            .get(i as usize)
1375                            .cast::<CalendarDayButton>()
1376                            .expect("the child of the month view is a CalendarDayButton")
1377                            .set_is_selected(false);
1378                    }
1379                }
1380            }
1381        }
1382    }
1383
1384    /// The indexes of the start and of the end of the hovered range, in
1385    /// that order.
1386    pub(crate) fn sort_hover_indexes(&self) -> (i32, i32) {
1387        let hover_start = self.hover_start().expect("the hover range has a start");
1388        let hover_end = self.hover_end().expect("the hover range has an end");
1389        let hover_start_index = self.hover_start_index().expect("the hover range has a start index");
1390        let hover_end_index = self.hover_end_index().expect("the hover range has an end index");
1391
1392        if DateTimeHelper::compare_days(hover_end, hover_start) > 0 {
1393            (hover_start_index, hover_end_index)
1394        } else {
1395            (hover_end_index, hover_start_index)
1396        }
1397    }
1398
1399    pub(crate) fn on_previous_click(&self) {
1400        if self.display_mode() == CalendarMode::Month {
1401            let d = DateTimeHelper::add_months(DateTimeHelper::discard_day_time(self.display_date()), -1);
1402            if let Some(d) = d {
1403                if !self
1404                    .last_selected_date()
1405                    .is_some_and(|last_selected_date| DateTimeHelper::compare_year_month(last_selected_date, d) == 0)
1406                {
1407                    self.set_last_selected_date(Some(d));
1408                }
1409                self.set_current_value(Self::display_date_property(), d);
1410            }
1411        } else {
1412            if self.display_mode() == CalendarMode::Year {
1413                let d = DateTimeHelper::add_years(DateTime::new(self.selected_month().year(), 1, 1), -1);
1414
1415                match d {
1416                    Some(d) => self.set_selected_month(d),
1417                    None => self.set_selected_month(DateTimeHelper::discard_day_time(self.display_date_range_start())),
1418                }
1419            } else {
1420                debug_assert!(self.display_mode() == CalendarMode::Decade, "DisplayMode should be Decade!");
1421
1422                let d = DateTimeHelper::add_years(DateTime::new(self.selected_year().year(), 1, 1), -10);
1423
1424                match d {
1425                    Some(d) => {
1426                        let decade = DateTimeHelper::decade_of_date(d).max(1);
1427                        self.set_selected_year(DateTime::new(decade, 1, 1));
1428                    }
1429                    None => self.set_selected_year(DateTimeHelper::discard_day_time(self.display_date_range_start())),
1430                }
1431            }
1432            self.update_months();
1433        }
1434    }
1435
1436    pub(crate) fn on_next_click(&self) {
1437        if self.display_mode() == CalendarMode::Month {
1438            let d = DateTimeHelper::add_months(DateTimeHelper::discard_day_time(self.display_date()), 1);
1439            if let Some(d) = d {
1440                if !self
1441                    .last_selected_date()
1442                    .is_some_and(|last_selected_date| DateTimeHelper::compare_year_month(last_selected_date, d) == 0)
1443                {
1444                    self.set_last_selected_date(Some(d));
1445                }
1446                self.set_current_value(Self::display_date_property(), d);
1447            }
1448        } else {
1449            if self.display_mode() == CalendarMode::Year {
1450                let d = DateTimeHelper::add_years(DateTime::new(self.selected_month().year(), 1, 1), 1);
1451
1452                match d {
1453                    Some(d) => self.set_selected_month(d),
1454                    None => self.set_selected_month(DateTimeHelper::discard_day_time(self.display_date_range_end())),
1455                }
1456            } else {
1457                debug_assert!(self.display_mode() == CalendarMode::Decade, "DisplayMode should be Decade");
1458
1459                let d = DateTimeHelper::add_years(DateTime::new(self.selected_year().year(), 1, 1), 10);
1460
1461                match d {
1462                    Some(d) => {
1463                        let decade = DateTimeHelper::decade_of_date(d).max(1);
1464                        self.set_selected_year(DateTime::new(decade, 1, 1));
1465                    }
1466                    None => self.set_selected_year(DateTimeHelper::discard_day_time(self.display_date_range_end())),
1467                }
1468            }
1469            self.update_months();
1470        }
1471    }
1472
1473    /// If the day is a trailing day, Update the DisplayDate.
1474    pub(crate) fn on_day_click(&self, selected_date: DateTime) {
1475        debug_assert!(self.display_mode() == CalendarMode::Month, "DisplayMode should be Month!");
1476        let i = DateTimeHelper::compare_year_month(selected_date, self.display_date_internal());
1477
1478        if self.selection_mode() == CalendarSelectionMode::None {
1479            self.set_last_selected_date(Some(selected_date));
1480        }
1481
1482        if i > 0 {
1483            self.on_next_click();
1484        } else if i < 0 {
1485            self.on_previous_click();
1486        }
1487    }
1488
1489    fn on_month_click(&self) {
1490        let month_control = self.month_control();
1491        debug_assert!(month_control.is_some());
1492
1493        if let Some(month_control) = month_control {
1494            if let (Some(year_view), Some(month_view)) = (month_control.year_view(), month_control.month_view()) {
1495                year_view.set_is_visible(false);
1496                month_view.set_is_visible(true);
1497
1498                if !self.last_selected_date().is_some_and(|last_selected_date| {
1499                    DateTimeHelper::compare_year_month(last_selected_date, self.display_date()) == 0
1500                }) {
1501                    self.set_last_selected_date(Some(self.display_date()));
1502                }
1503
1504                self.update_months();
1505            }
1506        }
1507    }
1508
1509    /// Occurs when the collection returned by the SelectedDates property is
1510    /// changed.
1511    pub fn selected_dates_changed(
1512        &self,
1513        handler: impl Fn(&SelectionChangedEventArgs) + 'static,
1514    ) -> Rc<dyn IDisposable> {
1515        let token = self.selected_dates_changed.add(Rc::new(handler));
1516        let weak = self.to_ref().downgrade();
1517        Disposable::create(move || {
1518            if let Some(this) = weak.upgrade() {
1519                this.selected_dates_changed.remove(token);
1520            }
1521        })
1522    }
1523
1524    /// Occurs when the DisplayDate property is changed.
1525    ///
1526    /// This event occurs after DisplayDate is assigned its new value.
1527    pub fn display_date_changed(
1528        &self,
1529        handler: impl Fn(&CalendarDateChangedEventArgs) + 'static,
1530    ) -> Rc<dyn IDisposable> {
1531        let token = self.display_date_changed.add(Rc::new(handler));
1532        let weak = self.to_ref().downgrade();
1533        Disposable::create(move || {
1534            if let Some(this) = weak.upgrade() {
1535                this.display_date_changed.remove(token);
1536            }
1537        })
1538    }
1539
1540    /// Occurs when the DisplayMode property is changed.
1541    pub fn display_mode_changed(
1542        &self,
1543        handler: impl Fn(&CalendarModeChangedEventArgs) + 'static,
1544    ) -> Rc<dyn IDisposable> {
1545        let token = self.display_mode_changed.add(Rc::new(handler));
1546        let weak = self.to_ref().downgrade();
1547        Disposable::create(move || {
1548            if let Some(this) = weak.upgrade() {
1549                this.display_mode_changed.remove(token);
1550            }
1551        })
1552    }
1553
1554    /// Occurs when the left mouse button is released over a day button
1555    /// that is not a blackout date.
1556    pub(crate) fn day_button_mouse_up(
1557        &self,
1558        handler: impl Fn(&PointerReleasedEventArgs) + 'static,
1559    ) -> Rc<dyn IDisposable> {
1560        let token = self.day_button_mouse_up.add(Rc::new(handler));
1561        let weak = self.to_ref().downgrade();
1562        Disposable::create(move || {
1563            if let Some(this) = weak.upgrade() {
1564                this.day_button_mouse_up.remove(token);
1565            }
1566        })
1567    }
1568
1569    /// This method adds the days that were selected by Keyboard to the
1570    /// SelectedDays Collection.
1571    fn add_selection(&self) {
1572        if let (Some(hover_end), Some(hover_start)) = (self.hover_end(), self.hover_start()) {
1573            let selected_dates = self.selected_dates();
1574            self.add_removed_items(&selected_dates.to_vec());
1575
1576            selected_dates.clear_internal();
1577            // In keyboard selection, we are sure that the collection does
1578            // not include any blackout days
1579            selected_dates.add_range(hover_start, hover_end);
1580        }
1581    }
1582
1583    /// Moves the selected dates to the removed items and empties the
1584    /// collection without notifications.
1585    fn move_selection_to_removed_items(&self) {
1586        let selected_dates = self.selected_dates();
1587        self.add_removed_items(&selected_dates.to_vec());
1588        selected_dates.clear_internal();
1589    }
1590
1591    /// Handles tap range selection logic for date range selection.
1592    ///
1593    /// `selected_date` is the date that was tapped. Returns true if the tap
1594    /// was handled as part of range selection; otherwise, false.
1595    pub(crate) fn process_tap_range_selection(&self, selected_date: DateTime) -> bool {
1596        if !self.allow_tap_range_selection()
1597            || (self.selection_mode() != CalendarSelectionMode::SingleRange
1598                && self.selection_mode() != CalendarSelectionMode::MultipleRange)
1599        {
1600            return false;
1601        }
1602
1603        if !Self::is_valid_date_selection(self, Some(selected_date)) {
1604            return false;
1605        }
1606
1607        let selected_dates = self.selected_dates();
1608
1609        match self.tap_range_start.get().filter(|_| self.is_tap_range_selection_active.get()) {
1610            None => {
1611                self.is_tap_range_selection_active.set(true);
1612                self.tap_range_start.set(Some(selected_date));
1613
1614                if self.selection_mode() == CalendarSelectionMode::SingleRange {
1615                    self.move_selection_to_removed_items();
1616                }
1617
1618                if !selected_dates.contains(selected_date) {
1619                    selected_dates.add(selected_date);
1620                }
1621
1622                true
1623            }
1624            Some(tap_range_start) => {
1625                let mut start_date = tap_range_start;
1626                let mut end_date = selected_date;
1627
1628                if DateTime::compare(start_date, end_date) > 0 {
1629                    std::mem::swap(&mut start_date, &mut end_date);
1630                }
1631
1632                let range = CalendarDateRange::new_range(start_date, end_date);
1633                if self.blackout_dates().contains_any(&range) {
1634                    self.tap_range_start.set(Some(selected_date));
1635
1636                    if self.selection_mode() == CalendarSelectionMode::SingleRange {
1637                        self.move_selection_to_removed_items();
1638                    }
1639
1640                    if !selected_dates.contains(selected_date) {
1641                        selected_dates.add(selected_date);
1642                    }
1643                    return true;
1644                }
1645
1646                if self.selection_mode() == CalendarSelectionMode::SingleRange {
1647                    self.move_selection_to_removed_items();
1648                }
1649
1650                selected_dates.add_range(start_date, end_date);
1651
1652                self.is_tap_range_selection_active.set(false);
1653                self.tap_range_start.set(None);
1654
1655                true
1656            }
1657        }
1658    }
1659
1660    fn process_selection(&self, shift: bool, last_selected_date: Option<DateTime>, index: Option<i32>) {
1661        if let (CalendarSelectionMode::None, Some(last_selected_date)) = (self.selection_mode(), last_selected_date) {
1662            self.on_day_click(last_selected_date);
1663            return;
1664        }
1665
1666        // Handle tap range selection.
1667        if let Some(last_selected_date) = last_selected_date {
1668            if index.is_none() && !shift && self.process_tap_range_selection(last_selected_date) {
1669                self.on_day_click(last_selected_date);
1670                return;
1671            }
1672        }
1673
1674        let Some(last_selected_date) = last_selected_date else {
1675            return;
1676        };
1677
1678        if Self::is_valid_keyboard_selection(self, Some(last_selected_date)) {
1679            if self.selection_mode() == CalendarSelectionMode::SingleRange
1680                || self.selection_mode() == CalendarSelectionMode::MultipleRange
1681            {
1682                self.move_selection_to_removed_items();
1683                if shift {
1684                    self.is_shift_pressed.set(true);
1685                    let hover_start = match self.hover_start() {
1686                        Some(hover_start) => hover_start,
1687                        None => {
1688                            let hover_start = match self.last_selected_date() {
1689                                Some(last_selected_date) => last_selected_date,
1690                                None => {
1691                                    if DateTimeHelper::compare_year_month(self.display_date_internal(), DateTime::today())
1692                                        == 0
1693                                    {
1694                                        DateTime::today()
1695                                    } else {
1696                                        self.display_date_internal()
1697                                    }
1698                                }
1699                            };
1700                            self.set_hover_start(Some(hover_start));
1701
1702                            if let Some(b) = self.find_day_button_from_day(hover_start) {
1703                                self.set_hover_start_index(Some(b.index()));
1704                            }
1705                            hover_start
1706                        }
1707                    };
1708                    // the index of the SelectedDate is always the last
1709                    // selectedDate's index
1710                    self.un_highlight_days();
1711                    // If we hit a BlackOutDay with keyboard we do not
1712                    // update the HoverEnd
1713                    let range = if DateTime::compare(hover_start, last_selected_date) < 0 {
1714                        CalendarDateRange::new_range(hover_start, last_selected_date)
1715                    } else {
1716                        CalendarDateRange::new_range(last_selected_date, hover_start)
1717                    };
1718
1719                    if !self.blackout_dates().contains_any(&range) {
1720                        self.set_hover_end(Some(last_selected_date));
1721
1722                        match index {
1723                            Some(index) => {
1724                                self.set_hover_end_index(self.hover_end_index().map(|current| current + index));
1725                            }
1726                            None => {
1727                                // For Home, End, PageUp and PageDown Keys there
1728                                // is no easy way to predict the index value
1729                                let hover_end_internal =
1730                                    self.hover_end_internal().expect("the hover range has an end");
1731                                if let Some(b) = self.find_day_button_from_day(hover_end_internal) {
1732                                    self.set_hover_end_index(Some(b.index()));
1733                                }
1734                            }
1735                        }
1736                    }
1737
1738                    self.on_day_click(self.hover_end().expect("the hover range has an end"));
1739                    self.highlight_days();
1740                } else {
1741                    self.set_hover_start(Some(last_selected_date));
1742                    self.set_hover_end(Some(last_selected_date));
1743                    self.add_selection();
1744                    self.on_day_click(last_selected_date);
1745                }
1746            } else {
1747                // ON CLEAR
1748                self.set_last_selected_date(Some(last_selected_date));
1749                let selected_dates = self.selected_dates();
1750                if selected_dates.count() > 0 {
1751                    selected_dates.set(0, last_selected_date);
1752                } else {
1753                    selected_dates.add(last_selected_date);
1754                }
1755                self.on_day_click(last_selected_date);
1756            }
1757        }
1758    }
1759
1760    pub(crate) fn on_day_button_mouse_up(&self, e: &PointerReleasedEventArgs) {
1761        for (_, handler) in self.day_button_mouse_up.snapshot().iter() {
1762            handler(e);
1763        }
1764    }
1765
1766    pub(crate) fn calendar_key_down(&self, e: &KeyEventArgs) {
1767        if !e.handled() && self.is_enabled() {
1768            e.set_handled(self.process_calendar_key(e));
1769        }
1770    }
1771
1772    pub(crate) fn process_calendar_key(&self, e: &KeyEventArgs) -> bool {
1773        if self.display_mode() == CalendarMode::Month {
1774            if let Some(last_selected_date) = self.last_selected_date() {
1775                // If a blackout day is inactive, when clicked on it, the
1776                // previous inactive day which is not a blackout day can get
1777                // the focus.  In this case we should allow keyboard
1778                // functions on that inactive day
1779                if DateTimeHelper::compare_year_month(last_selected_date, self.display_date_internal()) != 0
1780                    && self.focus_button().is_some_and(|focus_button| !focus_button.is_inactive())
1781                {
1782                    return true;
1783                }
1784            }
1785        }
1786
1787        // Some keys (e.g. Left/Right) need to be translated in RightToLeft mode
1788        let invariant_key = e.key;
1789
1790        let (ctrl, shift) = CalendarExtensions::get_meta_key_state(e.key_modifiers);
1791
1792        match invariant_key {
1793            Key::Up => {
1794                self.process_up_key(ctrl, shift);
1795                true
1796            }
1797            Key::Down => {
1798                self.process_down_key(ctrl, shift);
1799                true
1800            }
1801            Key::Left => {
1802                self.process_left_key(shift);
1803                true
1804            }
1805            Key::Right => {
1806                self.process_right_key(shift);
1807                true
1808            }
1809            Key::PageDown => {
1810                self.process_page_down_key(shift);
1811                true
1812            }
1813            Key::PageUp => {
1814                self.process_page_up_key(shift);
1815                true
1816            }
1817            Key::Home => {
1818                self.process_home_key(shift);
1819                true
1820            }
1821            Key::End => {
1822                self.process_end_key(shift);
1823                true
1824            }
1825            Key::Enter | Key::Space => self.process_enter_key(),
1826            _ => false,
1827        }
1828    }
1829
1830    /// The last selected date, or today when there is none.
1831    fn last_selected_date_or_today(&self) -> DateTime {
1832        self.last_selected_date().unwrap_or_else(DateTime::today)
1833    }
1834
1835    pub(crate) fn process_up_key(&self, ctrl: bool, shift: bool) {
1836        match self.display_mode() {
1837            CalendarMode::Month => {
1838                if ctrl {
1839                    self.set_selected_month(self.display_date_internal());
1840                    self.set_current_value(Self::display_mode_property(), CalendarMode::Year);
1841                } else {
1842                    let selected_date =
1843                        DateTimeHelper::add_days(self.last_selected_date_or_today(), -Self::COLUMNS_PER_MONTH);
1844                    self.process_selection(shift, selected_date, Some(-Self::COLUMNS_PER_MONTH));
1845                }
1846            }
1847            CalendarMode::Year => {
1848                if ctrl {
1849                    self.set_selected_year(self.selected_month());
1850                    self.set_current_value(Self::display_mode_property(), CalendarMode::Decade);
1851                } else {
1852                    let selected_month = DateTimeHelper::add_months(self.selected_month.get(), -Self::COLUMNS_PER_YEAR);
1853                    self.on_selected_month_changed(selected_month);
1854                }
1855            }
1856            CalendarMode::Decade => {
1857                if !ctrl {
1858                    let selected_year = DateTimeHelper::add_years(self.selected_year(), -Self::COLUMNS_PER_YEAR);
1859                    self.on_selected_year_changed(selected_year);
1860                }
1861            }
1862        }
1863    }
1864
1865    pub(crate) fn process_down_key(&self, ctrl: bool, shift: bool) {
1866        match self.display_mode() {
1867            CalendarMode::Month => {
1868                if !ctrl || shift {
1869                    let selected_date =
1870                        DateTimeHelper::add_days(self.last_selected_date_or_today(), Self::COLUMNS_PER_MONTH);
1871                    self.process_selection(shift, selected_date, Some(Self::COLUMNS_PER_MONTH));
1872                }
1873            }
1874            CalendarMode::Year => {
1875                if ctrl {
1876                    self.set_current_value(Self::display_date_property(), self.selected_month());
1877                    self.set_current_value(Self::display_mode_property(), CalendarMode::Month);
1878                } else {
1879                    let selected_month = DateTimeHelper::add_months(self.selected_month.get(), Self::COLUMNS_PER_YEAR);
1880                    self.on_selected_month_changed(selected_month);
1881                }
1882            }
1883            CalendarMode::Decade => {
1884                if ctrl {
1885                    self.set_selected_month(self.selected_year());
1886                    self.set_current_value(Self::display_mode_property(), CalendarMode::Year);
1887                } else {
1888                    let selected_year = DateTimeHelper::add_years(self.selected_year(), Self::COLUMNS_PER_YEAR);
1889                    self.on_selected_year_changed(selected_year);
1890                }
1891            }
1892        }
1893    }
1894
1895    pub(crate) fn process_left_key(&self, shift: bool) {
1896        match self.display_mode() {
1897            CalendarMode::Month => {
1898                let selected_date = DateTimeHelper::add_days(self.last_selected_date_or_today(), -1);
1899                self.process_selection(shift, selected_date, Some(-1));
1900            }
1901            CalendarMode::Year => {
1902                let selected_month = DateTimeHelper::add_months(self.selected_month.get(), -1);
1903                self.on_selected_month_changed(selected_month);
1904            }
1905            CalendarMode::Decade => {
1906                let selected_year = DateTimeHelper::add_years(self.selected_year(), -1);
1907                self.on_selected_year_changed(selected_year);
1908            }
1909        }
1910    }
1911
1912    pub(crate) fn process_right_key(&self, shift: bool) {
1913        match self.display_mode() {
1914            CalendarMode::Month => {
1915                let selected_date = DateTimeHelper::add_days(self.last_selected_date_or_today(), 1);
1916                self.process_selection(shift, selected_date, Some(1));
1917            }
1918            CalendarMode::Year => {
1919                let selected_month = DateTimeHelper::add_months(self.selected_month.get(), 1);
1920                self.on_selected_month_changed(selected_month);
1921            }
1922            CalendarMode::Decade => {
1923                let selected_year = DateTimeHelper::add_years(self.selected_year(), 1);
1924                self.on_selected_year_changed(selected_year);
1925            }
1926        }
1927    }
1928
1929    fn process_enter_key(&self) -> bool {
1930        match self.display_mode() {
1931            CalendarMode::Year => {
1932                self.set_current_value(Self::display_date_property(), self.selected_month());
1933                self.set_current_value(Self::display_mode_property(), CalendarMode::Month);
1934                true
1935            }
1936            CalendarMode::Decade => {
1937                self.set_selected_month(self.selected_year());
1938                self.set_current_value(Self::display_mode_property(), CalendarMode::Year);
1939                true
1940            }
1941            CalendarMode::Month => false,
1942        }
1943    }
1944
1945    pub(crate) fn process_home_key(&self, shift: bool) {
1946        match self.display_mode() {
1947            CalendarMode::Month => {
1948                // REMOVE_RTM: Not all types of calendars start with Day1. If Non-Gregorian is supported check this:
1949                let display_date_internal = self.display_date_internal();
1950                let selected_date = DateTime::new(display_date_internal.year(), display_date_internal.month(), 1);
1951                self.process_selection(shift, Some(selected_date), None);
1952            }
1953            CalendarMode::Year => {
1954                let selected_month = DateTime::new(self.selected_month.get().year(), 1, 1);
1955                self.on_selected_month_changed(Some(selected_month));
1956            }
1957            CalendarMode::Decade => {
1958                let selected_year = DateTime::new(DateTimeHelper::decade_of_date(self.selected_year()), 1, 1);
1959                self.on_selected_year_changed(Some(selected_year));
1960            }
1961        }
1962    }
1963
1964    pub(crate) fn process_end_key(&self, shift: bool) {
1965        match self.display_mode() {
1966            CalendarMode::Month => {
1967                let display_date_internal = self.display_date_internal();
1968                let mut selected_date = DateTime::new(display_date_internal.year(), display_date_internal.month(), 1);
1969
1970                if DateTimeHelper::compare_year_month(DateTime::MAX_VALUE, selected_date) > 0 {
1971                    // since DisplayDate is not equal to
1972                    // DateTime.MaxValue we are sure selectedDate is
1973                    // not null
1974                    selected_date =
1975                        DateTimeHelper::add_months(selected_date, 1).expect("the next month is a representable date");
1976                    selected_date =
1977                        DateTimeHelper::add_days(selected_date, -1).expect("the previous day is a representable date");
1978                } else {
1979                    selected_date = DateTime::MAX_VALUE;
1980                }
1981                self.process_selection(shift, Some(selected_date), None);
1982            }
1983            CalendarMode::Year => {
1984                let selected_month = DateTime::new(self.selected_month.get().year(), 12, 1);
1985                self.on_selected_month_changed(Some(selected_month));
1986            }
1987            CalendarMode::Decade => {
1988                let selected_year = DateTime::new(DateTimeHelper::end_of_decade(self.selected_year()), 1, 1);
1989                self.on_selected_year_changed(Some(selected_year));
1990            }
1991        }
1992    }
1993
1994    pub(crate) fn process_page_down_key(&self, shift: bool) {
1995        if !shift {
1996            self.on_next_click();
1997            return;
1998        }
1999        match self.display_mode() {
2000            CalendarMode::Month => {
2001                let selected_date = DateTimeHelper::add_months(self.last_selected_date_or_today(), 1);
2002                self.process_selection(shift, selected_date, None);
2003            }
2004            CalendarMode::Year => {
2005                let selected_month = DateTimeHelper::add_years(self.selected_month.get(), 1);
2006                self.on_selected_month_changed(selected_month);
2007            }
2008            CalendarMode::Decade => {
2009                let selected_year = DateTimeHelper::add_years(self.selected_year(), 10);
2010                self.on_selected_year_changed(selected_year);
2011            }
2012        }
2013    }
2014
2015    pub(crate) fn process_page_up_key(&self, shift: bool) {
2016        if !shift {
2017            self.on_previous_click();
2018            return;
2019        }
2020        match self.display_mode() {
2021            CalendarMode::Month => {
2022                let selected_date = DateTimeHelper::add_months(self.last_selected_date_or_today(), -1);
2023                self.process_selection(shift, selected_date, None);
2024            }
2025            CalendarMode::Year => {
2026                let selected_month = DateTimeHelper::add_years(self.selected_month.get(), -1);
2027                self.on_selected_month_changed(selected_month);
2028            }
2029            CalendarMode::Decade => {
2030                let selected_year = DateTimeHelper::add_years(self.selected_year(), -10);
2031                self.on_selected_year_changed(selected_year);
2032            }
2033        }
2034    }
2035
2036    fn calendar_key_up(&self, e: &KeyEventArgs) {
2037        if !e.handled() && (e.key == Key::LeftShift || e.key == Key::RightShift) {
2038            self.process_shift_key_up();
2039        }
2040    }
2041
2042    pub(crate) fn process_shift_key_up(&self) {
2043        if self.is_shift_pressed.get()
2044            && (self.selection_mode() == CalendarSelectionMode::SingleRange
2045                || self.selection_mode() == CalendarSelectionMode::MultipleRange)
2046        {
2047            self.add_selection();
2048            self.is_shift_pressed.set(false);
2049        }
2050    }
2051
2052    /// Called when the IsEnabled property changes.
2053    fn on_is_enabled_changed(&self, is_enabled: bool) {
2054        if let Some(month_control) = self.month_control() {
2055            month_control.update_disabled(is_enabled);
2056        }
2057    }
2058}