rat_widget/calendar/
calendar.rs

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