rat_widget/calendar/
calendar.rs

1use crate::_private::NonExhaustive;
2use crate::calendar::event::CalOutcome;
3use crate::calendar::selection::{NoSelection, RangeSelection, SingleSelection};
4use crate::calendar::{CalendarSelection, MonthState};
5use crate::text::HasScreenCursor;
6use chrono::{Datelike, Days, Local, Months, NaiveDate, Weekday};
7use rat_event::ConsumedEvent;
8use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
9use rat_reloc::RelocatableState;
10use ratatui::layout::Rect;
11use std::array;
12use std::cell::RefCell;
13use std::ops::RangeInclusive;
14use std::rc::Rc;
15
16/// How should `move_to_today()` behave.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum TodayPolicy {
19    /// Set the current date at the given month index and fill
20    /// out the rest accordingly.
21    Index(usize),
22    /// Behave like a yearly calendar. Sets the calendar to the
23    /// current year and focuses on the month of the current date.
24    Year,
25}
26
27impl Default for TodayPolicy {
28    fn default() -> Self {
29        Self::Index(0)
30    }
31}
32
33///
34/// A struct that contains an array of MonthState to get a true calendar
35/// behaviour. There is no Widget for this, the exact layout is left
36/// to the user. This takes care of all the rest.
37///
38#[derive(Debug, Clone)]
39pub struct CalendarState<const N: usize, Selection> {
40    /// Complete area
41    /// __read only__. renewed for each render.
42    pub area: Rect,
43    /// Area inside the block.
44    /// __read only__. renewed for each render.
45    pub inner: Rect,
46    /// Area inside the block.
47    /// __read only__. renewed for each render.
48    #[deprecated(since = "2.3.0", note = "use inner instead")]
49    pub widget_area: Rect,
50    /// Step-size when navigating outside the displayed
51    /// calendar.
52    ///
53    /// You can do a rolling calendar which goes back 1 month
54    /// or a yearly calendar which goes back 12 months.
55    /// Or any other value you like.
56    ///
57    /// If you set step to 0 this kind of navigation is
58    /// deactivated.
59    ///
60    /// Default is 1.
61    step: usize,
62
63    /// Behavior of move_to_today.
64    home: TodayPolicy,
65
66    /// Primary month.
67    /// Only this month will take part in Tab navigation.
68    /// The other months will be mouse-reachable only.
69    /// You can use arrow-keys to navigate the months though.
70    primary_idx: usize,
71
72    /// Months.
73    pub months: [MonthState<Selection>; N],
74
75    /// Selection model.
76    pub selection: Rc<RefCell<Selection>>,
77
78    /// Calendar focus
79    pub focus: FocusFlag,
80
81    pub non_exhaustive: NonExhaustive,
82}
83
84impl<const N: usize, Selection> Default for CalendarState<N, Selection>
85where
86    Selection: Default,
87{
88    #[allow(deprecated)]
89    fn default() -> Self {
90        let selection = Rc::new(RefCell::new(Selection::default()));
91        let focus = FocusFlag::new();
92
93        Self {
94            area: Default::default(),
95            inner: Default::default(),
96            widget_area: Default::default(),
97            step: 1,
98            months: array::from_fn(|_| {
99                let mut state = MonthState::new();
100                state.selection = selection.clone();
101                state.container = Some(focus.clone());
102                state
103            }),
104            selection,
105            primary_idx: Default::default(),
106            focus,
107            home: Default::default(),
108            non_exhaustive: NonExhaustive,
109        }
110    }
111}
112
113impl<const N: usize, Selection> HasFocus for CalendarState<N, Selection> {
114    fn build(&self, builder: &mut FocusBuilder) {
115        let tag = builder.start(self);
116        for (i, v) in self.months.iter().enumerate() {
117            if i == self.primary_idx {
118                builder.widget(v); // regular widget
119            } else {
120                builder.widget_with_flags(v.focus(), v.area(), v.area_z(), Navigation::Leave)
121            }
122        }
123        builder.end(tag);
124    }
125
126    fn focus(&self) -> FocusFlag {
127        self.focus.clone()
128    }
129
130    fn area(&self) -> Rect {
131        Rect::default()
132    }
133}
134
135impl<const N: usize, Selection> HasScreenCursor for CalendarState<N, Selection> {
136    fn screen_cursor(&self) -> Option<(u16, u16)> {
137        None
138    }
139}
140
141impl<const N: usize, Selection> RelocatableState for CalendarState<N, Selection> {
142    #[allow(deprecated)]
143    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
144        self.area.relocate(shift, clip);
145        self.inner.relocate(shift, clip);
146        self.widget_area.relocate(shift, clip);
147
148        for w in &mut self.months {
149            w.relocate(shift, clip);
150        }
151    }
152}
153
154impl<const N: usize, Selection> CalendarState<N, Selection> {
155    pub fn new() -> Self
156    where
157        Selection: Default,
158    {
159        Self::default()
160    }
161
162    pub fn named(name: &str) -> Self
163    where
164        Selection: Default,
165    {
166        let mut z = Self::default();
167        z.focus = z.focus.with_name(name);
168        z
169    }
170
171    /// Step-size when navigating outside the displayed
172    /// calendar.
173    ///
174    /// You can do a rolling calendar which goes back 1 month
175    /// or a yearly calendar which goes back 12 months.
176    /// Or any other value you like.
177    ///
178    /// If you set step to 0 this kind of navigation is
179    /// deactivated.
180    ///
181    /// Default is 1.
182    pub fn set_step(&mut self, step: usize) {
183        self.step = step;
184    }
185
186    /// Step-size when navigating outside the displayed
187    /// calendar.
188    ///
189    /// You can do a rolling calendar which goes back 1 month
190    /// or a yearly calendar which goes back 12 months.
191    /// Or any other value you like.
192    ///
193    /// If you set step to 0 this kind of navigation is
194    /// deactivated.
195    ///
196    /// Default is 1.
197    pub fn step(&self) -> usize {
198        self.step
199    }
200
201    /// How should move_to_today() work.
202    pub fn set_today_policy(&mut self, home: TodayPolicy) {
203        if let TodayPolicy::Index(idx) = home {
204            assert!(idx < self.months.len());
205        }
206        self.home = home;
207    }
208
209    /// How should move_to_today() work.
210    pub fn today_policy(&mut self) -> TodayPolicy {
211        self.home
212    }
213
214    /// Set the primary month for the calendar.
215    ///
216    /// The primary month will be focused with Tab navigation.
217    /// That means you can jump over all calendar months with just
218    /// one Tab.
219    ///
220    /// Movement within the calendar is possible with the arrow-keys.
221    /// This will change the primary month for future Tab navigation.
222    pub fn set_primary_idx(&mut self, primary: usize) {
223        assert!(primary < self.months.len());
224        self.primary_idx = primary;
225    }
226
227    /// The primary month for the calendar.
228    ///
229    /// The primary month will be focused with Tab navigation.
230    /// That means you can jump over all calendar months with just
231    /// one Tab.
232    ///
233    /// Movement within the calendar is possible with the arrow-keys.
234    /// This will change the primary month for future Tab navigation.
235    pub fn primary_idx(&self) -> usize {
236        self.primary_idx
237    }
238
239    /// Set the start-date for the calendar.
240    /// This will set the start-date for each month.
241    pub fn set_start_date(&mut self, mut date: NaiveDate) {
242        for month in &mut self.months {
243            month.set_start_date(date);
244            date = date + Months::new(1);
245        }
246    }
247
248    /// Start-date of the calendar.
249    pub fn start_date(&self) -> NaiveDate {
250        self.months[0].start_date()
251    }
252
253    /// End-date of the calendar.
254    pub fn end_date(&self) -> NaiveDate {
255        self.months[self.months.len() - 1].end_date()
256    }
257
258    /// Changes the start-date for each month.
259    /// Doesn't change any selection.
260    pub fn scroll_forward(&mut self, n: usize) -> CalOutcome {
261        if n == 0 {
262            return CalOutcome::Continue;
263        }
264
265        // change start dates
266        let mut start = self.months[0].start_date() + Months::new(n as u32);
267
268        for i in 0..self.months.len() {
269            self.months[i].set_start_date(start);
270            start = start + Months::new(1);
271        }
272
273        CalOutcome::Changed
274    }
275
276    /// Changes the start-date for each month.
277    /// Doesn't change any selection.
278    pub fn scroll_back(&mut self, n: usize) -> CalOutcome {
279        if n == 0 {
280            return CalOutcome::Continue;
281        }
282
283        // change start dates
284        let mut start = self.months[0].start_date() - Months::new(n as u32);
285
286        for i in 0..self.months.len() {
287            self.months[i].set_start_date(start);
288            start = start + Months::new(1);
289        }
290
291        CalOutcome::Changed
292    }
293
294    /// Changes the start-date for each month.
295    /// Doesn't change any selection.
296    pub fn scroll_to(&mut self, date: NaiveDate) -> CalOutcome {
297        // change start dates
298        let mut start = date - Months::new(self.primary_idx() as u32);
299
300        for i in 0..self.months.len() {
301            self.months[i].set_start_date(start);
302            start = start + Months::new(1);
303        }
304
305        CalOutcome::Changed
306    }
307
308    /// Returns None
309    pub fn screen_cursor(&self) -> Option<(u16, u16)> {
310        None
311    }
312}
313
314impl<const N: usize, Selection> CalendarState<N, Selection>
315where
316    Selection: CalendarSelection,
317{
318    /// Is the date selected?
319    pub fn is_selected(&self, date: NaiveDate) -> bool {
320        self.selection.is_selected(date)
321    }
322
323    /// Lead selection.
324    pub fn lead_selection(&self) -> Option<NaiveDate> {
325        self.selection.lead_selection()
326    }
327
328    pub(super) fn focus_lead(&mut self) -> CalOutcome {
329        let Some(lead) = self.selection.lead_selection() else {
330            return CalOutcome::Continue;
331        };
332
333        let mut r = CalOutcome::Continue;
334
335        if self.is_focused() {
336            for (i, month) in self.months.iter().enumerate() {
337                if lead >= month.start_date() && lead <= month.end_date() {
338                    if self.primary_idx != i {
339                        r = CalOutcome::Changed;
340                    }
341                    self.primary_idx = i;
342                    month.focus.set(true);
343                } else {
344                    month.focus.set(false);
345                }
346            }
347        }
348
349        r
350    }
351
352    pub(super) fn focus_n(&mut self, n: usize) -> CalOutcome {
353        let mut r = CalOutcome::Continue;
354
355        if self.is_focused() {
356            for (i, month) in self.months.iter().enumerate() {
357                if i == n {
358                    if self.primary_idx != i {
359                        r = CalOutcome::Changed;
360                    }
361                    self.primary_idx = i;
362                    month.focus.set(true);
363                } else {
364                    month.focus.set(false);
365                }
366            }
367        }
368
369        r
370    }
371}
372
373impl<const N: usize> CalendarState<N, NoSelection> {
374    /// Move to the previous month. Scrolls the calendar
375    /// if necessary.
376    pub fn prev_month(&mut self, n: usize) -> CalOutcome {
377        let base_start = self.start_date();
378        let date = self.months[self.primary_idx].start_date();
379
380        let prev = date - Months::new(n as u32);
381
382        let mut r = CalOutcome::Continue;
383        if prev >= base_start {
384            self.focus_n(self.primary_idx - 1);
385            r = CalOutcome::Changed;
386        } else if self.step() > 0 {
387            r = self.scroll_back(self.step());
388        }
389
390        r
391    }
392
393    /// Move to the next month. Scrolls the calendar
394    /// if necessary.
395    pub fn next_month(&mut self, n: usize) -> CalOutcome {
396        let base_end = self.end_date();
397        let date = self.months[self.primary_idx].start_date();
398
399        let next = date + Months::new(n as u32);
400
401        let mut r = CalOutcome::Continue;
402        if next <= base_end {
403            self.focus_n(self.primary_idx + 1);
404            r = CalOutcome::Changed;
405        } else if self.step() > 0 {
406            r = self.scroll_forward(self.step());
407        }
408        r
409    }
410
411    /// Move the calendar to show the current date.
412    ///
413    /// Resets the start-dates according to TodayPolicy.
414    /// Focuses the primary index and selects the current day.
415    ///
416    pub fn move_to_today(&mut self) -> CalOutcome {
417        self.move_to(Local::now().date_naive())
418    }
419
420    pub fn move_to(&mut self, date: NaiveDate) -> CalOutcome {
421        let r = CalOutcome::Changed;
422
423        match self.home {
424            TodayPolicy::Index(primary) => {
425                self.primary_idx = primary;
426                self.set_start_date(date - Months::new(primary as u32));
427                self.focus_n(self.primary_idx);
428            }
429            TodayPolicy::Year => {
430                let month = date.month0();
431                self.primary_idx = month as usize;
432                self.set_start_date(date - Months::new(month));
433                self.focus_n(self.primary_idx);
434            }
435        }
436
437        r
438    }
439}
440
441impl<const N: usize> CalendarState<N, SingleSelection> {
442    /// Clear the selection.
443    pub fn clear_selection(&mut self) {
444        self.selection.borrow_mut().clear();
445    }
446
447    /// Select the given date.
448    /// Doesn't scroll the calendar.
449    pub fn select(&mut self, date: NaiveDate) -> bool {
450        self.selection.borrow_mut().select(date)
451    }
452
453    /// Selected date.
454    pub fn selected(&self) -> Option<NaiveDate> {
455        self.selection.borrow().selected()
456    }
457
458    /// Move the calendar to show the current date.
459    ///
460    /// Resets the start-dates according to TodayPolicy.
461    /// Focuses the primary index and selects the current day.
462    pub fn move_to_today(&mut self) -> CalOutcome {
463        self.move_to(Local::now().date_naive())
464    }
465
466    /// Move the calendar to show the given date.
467    ///
468    /// Resets the start-dates according to TodayPolicy.
469    /// Focuses the primary index and selects the current day.
470    ///
471    pub fn move_to(&mut self, date: NaiveDate) -> CalOutcome {
472        let mut r = CalOutcome::Changed;
473
474        if self.selection.borrow_mut().select(date) {
475            r = CalOutcome::Selected;
476        }
477        match self.home {
478            TodayPolicy::Index(primary) => {
479                self.primary_idx = primary;
480                self.set_start_date(date - Months::new(primary as u32));
481                self.focus_lead();
482            }
483            TodayPolicy::Year => {
484                let month = date.month0();
485                self.primary_idx = month as usize;
486                self.set_start_date(date - Months::new(month));
487                self.focus_lead();
488            }
489        }
490
491        r
492    }
493
494    /// Select previous day.
495    /// Scrolls the calendar if necessary.
496    pub fn prev_day(&mut self, n: usize) -> CalOutcome {
497        self.prev(Months::new(0), Days::new(n as u64))
498    }
499
500    /// Select previous week.
501    /// Scrolls the calendar if necessary.
502    pub fn next_day(&mut self, n: usize) -> CalOutcome {
503        self.next(Months::new(0), Days::new(n as u64))
504    }
505
506    /// Select the same day in the previous month.
507    ///
508    /// This may shift the date a bit if it's out of range of the
509    /// new month.
510    pub fn prev_month(&mut self, n: usize) -> CalOutcome {
511        self.prev(Months::new(n as u32), Days::new(0))
512    }
513
514    /// Select the same day in the next month.
515    ///
516    /// This may shift the date a bit if it's out of range of the
517    /// new month.
518    pub fn next_month(&mut self, n: usize) -> CalOutcome {
519        self.next(Months::new(n as u32), Days::new(0))
520    }
521
522    /// impl for prev_day(), prev_month()
523    fn prev(&mut self, months: Months, days: Days) -> CalOutcome {
524        let base_start = self.start_date();
525        let base_end = self.end_date();
526
527        let new_date = if let Some(date) = self.selection.lead_selection() {
528            if date >= base_start && date <= base_end {
529                date - months - days
530            } else if date < base_start {
531                self.start_date()
532            } else {
533                self.end_date()
534            }
535        } else {
536            self.end_date()
537        };
538
539        let mut r = CalOutcome::Continue;
540
541        if new_date >= base_start && new_date <= base_end {
542            if self.selection.borrow_mut().select(new_date) {
543                r = CalOutcome::Selected;
544            }
545        } else if self.step > 0 {
546            r = r.max(self.scroll_back(self.step));
547            if self.selection.borrow_mut().select(new_date) {
548                r = r.max(CalOutcome::Selected);
549            }
550        }
551
552        if r.is_consumed() {
553            self.focus_lead();
554        }
555
556        r
557    }
558
559    /// impl for next_day(), next_month()
560    fn next(&mut self, months: Months, days: Days) -> CalOutcome {
561        let base_start = self.start_date();
562        let base_end = self.end_date();
563
564        let new_date = if let Some(date) = self.selection.lead_selection() {
565            if date >= base_start && date <= base_end {
566                date + months + days
567            } else if date < base_start {
568                self.start_date()
569            } else {
570                self.end_date()
571            }
572        } else {
573            self.start_date()
574        };
575
576        let mut r = CalOutcome::Continue;
577
578        if new_date >= base_start && new_date <= base_end {
579            if self.selection.borrow_mut().select(new_date) {
580                r = CalOutcome::Selected;
581            }
582        } else if self.step > 0 {
583            r = self.scroll_forward(self.step);
584            if self.selection.borrow_mut().select(new_date) {
585                r = r.max(CalOutcome::Selected);
586            }
587        }
588
589        if r.is_consumed() {
590            self.focus_lead();
591        }
592
593        r
594    }
595}
596
597impl<const N: usize> CalendarState<N, RangeSelection> {
598    /// Clear the selection.
599    pub fn clear_selection(&mut self) {
600        self.selection.borrow_mut().clear();
601    }
602
603    /// Select the full month of the date. Any date of the month will do.
604    /// Can extend the selection to encompass the month.
605    /// Any existing selection is buffed up to fill one month.
606    pub fn select_month(&mut self, date: NaiveDate, extend: bool) -> bool {
607        self.selection.borrow_mut().select_month(date, extend)
608    }
609
610    /// Select the week of the given date. Any date of the week will do.
611    /// Can extend the selection to encompass the week.
612    /// Any existing selection is buffed up to fill one week.
613    pub fn select_week(&mut self, date: NaiveDate, extend: bool) -> bool {
614        self.selection.borrow_mut().select_week(date, extend)
615    }
616
617    /// Select the given date.
618    /// Can extend the selection to the given date.
619    pub fn select_day(&mut self, date: NaiveDate, extend: bool) -> bool {
620        self.selection.borrow_mut().select_day(date, extend)
621    }
622
623    /// Set the selection as (anchor, lead) pair.
624    pub fn select(&mut self, selection: (NaiveDate, NaiveDate)) -> bool {
625        self.selection.borrow_mut().select(selection)
626    }
627
628    /// Selection as (anchor, lead) pair.
629    pub fn selected(&self) -> Option<(NaiveDate, NaiveDate)> {
630        self.selection.borrow().selected()
631    }
632
633    /// Selection as NaiveDate range.
634    pub fn selected_range(&self) -> Option<RangeInclusive<NaiveDate>> {
635        self.selection.borrow().selected_range()
636    }
637
638    /// Move the calendar to today.
639    ///
640    /// Uses [TodayPolicy] to build the new calendar and focuses and selects
641    /// the given date.
642    pub fn move_to_today(&mut self) -> CalOutcome {
643        self.move_to(Local::now().date_naive())
644    }
645
646    /// Move the calendar the given day.
647    ///
648    /// Uses [TodayPolicy] to build the new calendar and focuses and selects
649    /// the given date.
650    pub fn move_to(&mut self, date: NaiveDate) -> CalOutcome {
651        let mut r = CalOutcome::Changed;
652
653        if self.selection.borrow_mut().select_day(date, false) {
654            r = CalOutcome::Selected;
655        }
656        match self.home {
657            TodayPolicy::Index(primary) => {
658                self.primary_idx = primary;
659                self.set_start_date(date - Months::new(primary as u32));
660                self.focus_lead();
661            }
662            TodayPolicy::Year => {
663                let month = date.month0();
664                self.primary_idx = month as usize;
665                self.set_start_date(date - Months::new(month));
666                self.focus_lead();
667            }
668        }
669
670        r
671    }
672
673    /// Move the lead selection back by months/days.
674    /// Clears the current selection and scrolls if necessary.
675    pub fn move_to_prev(&mut self, months: Months, days: Days) -> CalOutcome {
676        let base_start = self.start_date();
677        let base_end = self.end_date();
678
679        let new_date = if let Some(date) = self.selection.lead_selection() {
680            if date >= base_start && date <= base_end {
681                date - months - days
682            } else if date < base_start {
683                self.start_date()
684            } else {
685                self.end_date()
686            }
687        } else {
688            self.end_date()
689        };
690
691        let mut r = CalOutcome::Continue;
692
693        if new_date >= base_start && new_date <= base_end {
694            if self.selection.borrow_mut().select_day(new_date, false) {
695                r = CalOutcome::Selected;
696            }
697        } else if self.step > 0 {
698            r = r.max(self.scroll_back(self.step));
699            if self.selection.borrow_mut().select_day(new_date, false) {
700                r = r.max(CalOutcome::Selected);
701            }
702        }
703
704        if r.is_consumed() {
705            self.focus_lead();
706        }
707
708        r
709    }
710
711    /// Move the lead selection forward by months/days.
712    /// Clears the current selection and scrolls if necessary.
713    pub fn move_to_next(&mut self, months: Months, days: Days) -> CalOutcome {
714        let base_start = self.start_date();
715        let base_end = self.end_date();
716
717        let new_date = if let Some(date) = self.selection.lead_selection() {
718            if date >= base_start && date <= base_end {
719                date + months + days
720            } else if date < base_start {
721                self.start_date()
722            } else {
723                self.end_date()
724            }
725        } else {
726            self.start_date()
727        };
728
729        let mut r = CalOutcome::Continue;
730
731        if new_date >= base_start && new_date <= base_end {
732            if self.selection.borrow_mut().select_day(new_date, false) {
733                r = CalOutcome::Selected;
734            }
735        } else if self.step > 0 {
736            r = self.scroll_forward(self.step);
737            if self.selection.borrow_mut().select_day(new_date, false) {
738                r = r.max(CalOutcome::Selected);
739            }
740        }
741
742        if r.is_consumed() {
743            self.focus_lead();
744        }
745
746        r
747    }
748
749    /// Select previous day.
750    ///
751    /// Can extend the selection to include the new date.
752    pub fn prev_day(&mut self, n: usize, extend: bool) -> CalOutcome {
753        let base_start = self.start_date();
754        let base_end = self.end_date();
755
756        let mut r = CalOutcome::Continue;
757
758        if let Some(date) = self.selection.lead_selection() {
759            let new_date = if date >= base_start && date <= base_end || self.step != 0 {
760                date - Days::new(n as u64)
761            } else if date < base_start {
762                self.start_date()
763            } else {
764                self.end_date()
765            };
766
767            if new_date >= base_start && new_date <= base_end {
768                if self.selection.borrow_mut().select_day(new_date, extend) {
769                    r = CalOutcome::Selected;
770                }
771            } else if self.step > 0 {
772                r = self.scroll_back(self.step);
773                if self.selection.borrow_mut().select_day(new_date, extend) {
774                    r = CalOutcome::Selected;
775                }
776            }
777        } else {
778            let new_date = self.end_date();
779            if self.selection.borrow_mut().select_day(new_date, extend) {
780                r = CalOutcome::Selected;
781            }
782        }
783
784        if r.is_consumed() {
785            self.focus_lead();
786        }
787
788        r
789    }
790
791    /// Select next day.
792    ///
793    /// Can extend the selection to include the new date.
794    pub fn next_day(&mut self, n: usize, extend: bool) -> CalOutcome {
795        let base_start = self.start_date();
796        let base_end = self.end_date();
797
798        let new_date = if let Some(date) = self.selection.lead_selection() {
799            if date >= base_start && date <= base_end || self.step > 0 {
800                date + Days::new(n as u64)
801            } else if date < base_start {
802                self.start_date()
803            } else {
804                self.end_date()
805            }
806        } else {
807            self.start_date()
808        };
809
810        let mut r = CalOutcome::Continue;
811
812        if new_date >= base_start && new_date <= base_end {
813            if self.selection.borrow_mut().select_day(new_date, extend) {
814                r = CalOutcome::Selected;
815            }
816        } else if self.step > 0 {
817            r = self.scroll_forward(self.step);
818            if self.selection.borrow_mut().select_day(new_date, extend) {
819                r = CalOutcome::Selected;
820            }
821        }
822
823        if r.is_consumed() {
824            self.focus_lead();
825        }
826
827        r
828    }
829
830    /// Select the previous week.
831    ///
832    /// When extending a selection, the current selection buffs up to fill one week.
833    pub fn prev_week(&mut self, n: usize, extend: bool) -> CalOutcome {
834        let base_start = self.start_date();
835        let base_end = self.end_date();
836
837        let mut r = CalOutcome::Continue;
838
839        if let Some(date) = self.selection.lead_selection() {
840            let new_date = if date >= base_start && date <= base_end || self.step != 0 {
841                date - Days::new(7 * n as u64)
842            } else if date < base_start {
843                self.start_date()
844            } else {
845                self.end_date()
846            };
847
848            let new_date_end = new_date.week(Weekday::Mon).last_day();
849
850            if new_date_end >= base_start && new_date_end <= base_end {
851                if self.selection.borrow_mut().select_week(new_date, extend) {
852                    r = CalOutcome::Selected;
853                }
854            } else if self.step > 0 {
855                r = self.scroll_back(self.step);
856                if self.selection.borrow_mut().select_week(new_date, extend) {
857                    r = CalOutcome::Selected;
858                }
859            }
860        } else {
861            let new_date = self.end_date();
862            if self.selection.borrow_mut().select_week(new_date, extend) {
863                r = CalOutcome::Selected;
864            }
865        }
866
867        if r.is_consumed() {
868            self.focus_lead();
869        }
870
871        r
872    }
873
874    /// Select the next week.
875    ///
876    /// When extending a selection, the current selection buffs up to fill one week.
877    pub fn next_week(&mut self, n: usize, extend: bool) -> CalOutcome {
878        let base_start = self.start_date();
879        let base_end = self.end_date();
880
881        let new_date = if let Some(date) = self.selection.lead_selection() {
882            let date_end = date.week(Weekday::Mon).last_day();
883            if date_end >= base_start && date_end <= base_end || self.step > 0 {
884                date + Days::new(7 * n as u64)
885            } else if date_end < base_start {
886                self.start_date()
887            } else {
888                self.end_date()
889            }
890        } else {
891            self.start_date()
892        };
893
894        let mut r = CalOutcome::Continue;
895
896        if new_date >= base_start && new_date <= base_end {
897            if self.selection.borrow_mut().select_week(new_date, extend) {
898                r = CalOutcome::Selected;
899            }
900        } else if self.step > 0 {
901            r = self.scroll_forward(self.step);
902            if self.selection.borrow_mut().select_week(new_date, extend) {
903                r = CalOutcome::Selected;
904            }
905        }
906
907        if r.is_consumed() {
908            self.focus_lead();
909        }
910
911        r
912    }
913
914    /// Select the previous month.
915    ///
916    /// When extending a selection, the current selection buffs up to fill one month.
917    ///
918    pub fn prev_month(&mut self, n: usize, extend: bool) -> CalOutcome {
919        let base_start = self.start_date();
920        let base_end = self.end_date();
921
922        let mut r = CalOutcome::Continue;
923
924        if let Some(date) = self.selection.lead_selection() {
925            let new_date = if date >= base_start && date <= base_end || self.step != 0 {
926                date - Months::new(n as u32)
927            } else if date < base_start {
928                self.start_date()
929            } else {
930                self.end_date()
931            };
932
933            if new_date >= base_start && new_date <= base_end {
934                if self.selection.borrow_mut().select_month(new_date, extend) {
935                    r = CalOutcome::Selected;
936                }
937            } else if self.step > 0 {
938                r = self.scroll_back(self.step);
939                if self.selection.borrow_mut().select_month(new_date, extend) {
940                    r = CalOutcome::Selected;
941                }
942            }
943        } else {
944            let new_date = self.end_date();
945            if self.selection.borrow_mut().select_month(new_date, extend) {
946                r = CalOutcome::Selected;
947            }
948        }
949
950        if r.is_consumed() {
951            self.focus_lead();
952        }
953
954        r
955    }
956
957    /// Select the previous month.
958    ///
959    /// When extending a selection, the current selection buffs up to fill one month.
960    ///
961    pub fn next_month(&mut self, n: usize, extend: bool) -> CalOutcome {
962        let base_start = self.start_date();
963        let base_end = self.end_date();
964
965        let new_date = if let Some(date) = self.selection.lead_selection() {
966            if date >= base_start && date <= base_end || self.step > 0 {
967                date + Months::new(n as u32)
968            } else if date < base_start {
969                self.start_date()
970            } else {
971                self.end_date()
972            }
973        } else {
974            self.start_date()
975        };
976
977        let mut r = CalOutcome::Continue;
978
979        if new_date >= base_start && new_date <= base_end {
980            if self.selection.borrow_mut().select_month(new_date, extend) {
981                r = CalOutcome::Selected;
982            }
983        } else if self.step > 0 {
984            r = self.scroll_forward(self.step);
985            if self.selection.borrow_mut().select_month(new_date, extend) {
986                r = CalOutcome::Selected;
987            }
988        }
989
990        if r.is_consumed() {
991            self.focus_lead();
992        }
993
994        r
995    }
996}