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