rat_widget/calendar/
month.rs

1use crate::_private::NonExhaustive;
2use crate::calendar::event::CalOutcome;
3use crate::calendar::selection::{NoSelection, RangeSelection, SingleSelection};
4use crate::calendar::style::CalendarStyle;
5use crate::calendar::{CalendarSelection, first_day_of_month, last_day_of_month};
6use crate::util::{block_size, revert_style};
7use chrono::{Datelike, Days, NaiveDate, Weekday};
8use rat_event::util::MouseFlagsN;
9use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
10use rat_reloc::RelocatableState;
11use ratatui::buffer::Buffer;
12use ratatui::layout::{Alignment, Rect};
13use ratatui::style::Style;
14use ratatui::style::Stylize;
15use ratatui::text::Span;
16use ratatui::widgets::block::Title;
17use ratatui::widgets::{Block, StatefulWidget, Widget};
18use std::cell::RefCell;
19use std::cmp::max;
20use std::collections::HashMap;
21use std::marker::PhantomData;
22use std::rc::Rc;
23
24/// Month widget.
25///
26/// Renders one month of a calendar.
27///
28/// There is movement and selection support within the month,
29/// but no scrolling. Use a Calendar for full calendar features.
30///
31/// ```rust ignore
32/// Month::new()
33///     .locale(Locale::de_AT_euro)
34///     .styles(THEME.month_style())
35///     .title_align(Alignment::Left)
36///     .day_styles(&date_styles)
37///     .show_weekdays()
38///     .block(Block::bordered().borders(Borders::TOP))
39///     .render(l2[2], frame.buffer_mut(), &mut state.month1);
40/// ```
41///
42/// ```rust ignore
43/// match state.month1.handle(event, Regular) {
44///     CalOutcome::Selected => {
45///         // doit
46///         Outcome::Changed
47///     }
48///     r => r.into()
49/// }
50/// ```
51///
52#[derive(Debug, Clone)]
53pub struct Month<'a, Selection> {
54    /// Start date of the month.
55    start_date: Option<NaiveDate>,
56
57    /// Base style.
58    style: Style,
59    /// Title style.
60    title_style: Option<Style>,
61    /// Title align.
62    title_align: Alignment,
63    /// Week number style.
64    weeknum_style: Option<Style>,
65    /// Week day style.
66    weekday_style: Option<Style>,
67    /// Default day style.
68    day_style: Option<Style>,
69    /// Styling for a single date.
70    day_styles: Option<&'a HashMap<NaiveDate, Style>>,
71    /// Selection
72    select_style: Option<Style>,
73    /// Focus
74    focus_style: Option<Style>,
75
76    /// Show month name
77    show_month: bool,
78    /// Show Weekdays above
79    show_weekdays: bool,
80
81    /// Block
82    block: Option<Block<'a>>,
83
84    /// Locale
85    loc: chrono::Locale,
86
87    phantom: PhantomData<Selection>,
88}
89
90/// State & event-handling.
91#[derive(Debug)]
92pub struct MonthState<Selection = SingleSelection> {
93    /// Total area.
94    /// __readonly__. renewed for each render.
95    pub area: Rect,
96    /// Area inside the border.
97    /// __readonly__. renewed for each render.
98    pub inner: Rect,
99    /// Area of the main calendar.
100    /// __readonly__. renewed for each render.
101    pub area_cal: Rect,
102    /// Area for the days of the month.
103    /// __readonly__. renewed for each render.
104    pub area_days: [Rect; 31],
105    /// Area for all the week numbers.
106    /// __readonly__. renewed for each render.
107    pub area_weeknum: Rect,
108    /// Area for the week numbers.
109    /// __readonly__. renewed for each render.
110    pub area_weeks: [Rect; 6],
111
112    /// Startdate
113    start_date: NaiveDate,
114
115    /// Selection model.
116    /// The selection model can be shared with other Month widgets.
117    pub selection: Rc<RefCell<Selection>>,
118
119    /// Set to the container-focus if part of a container.
120    /// __read+write__
121    pub container: Option<FocusFlag>,
122
123    /// Focus
124    /// __read+write__
125    pub focus: FocusFlag,
126    /// Mouse flags
127    /// __read+write__
128    pub mouse: MouseFlagsN,
129
130    pub non_exhaustive: NonExhaustive,
131}
132
133impl<Selection> Default for Month<'_, Selection> {
134    fn default() -> Self {
135        Self {
136            start_date: None,
137            style: Default::default(),
138            title_style: Default::default(),
139            title_align: Default::default(),
140            weeknum_style: Default::default(),
141            weekday_style: Default::default(),
142            day_style: Default::default(),
143            day_styles: Default::default(),
144            select_style: Default::default(),
145            focus_style: Default::default(),
146            show_month: true,
147            show_weekdays: true,
148            block: Default::default(),
149            loc: Default::default(),
150            phantom: PhantomData,
151        }
152    }
153}
154
155impl<'a, Selection> Month<'a, Selection> {
156    /// New calendar.
157    pub fn new() -> Self {
158        Self::default()
159    }
160
161    /// Sets the starting date. This can be any date of the month.
162    /// If no date is set, the start_date of the state is used.
163    #[inline]
164    pub fn date(mut self, s: NaiveDate) -> Self {
165        self.start_date = Some(first_day_of_month(s));
166        self
167    }
168
169    /// Locale for month-names, day-names.
170    #[inline]
171    pub fn locale(mut self, loc: chrono::Locale) -> Self {
172        self.loc = loc;
173        self
174    }
175
176    /// Show month title
177    #[inline]
178    pub fn show_show_month(mut self) -> Self {
179        self.show_month = true;
180        self
181    }
182
183    /// Show weekday titles
184    #[inline]
185    pub fn show_weekdays(mut self) -> Self {
186        self.show_weekdays = true;
187        self
188    }
189
190    /// Set the composite style.
191    #[inline]
192    pub fn styles(mut self, s: CalendarStyle) -> Self {
193        self.style = s.style;
194        if s.title.is_some() {
195            self.title_style = s.title;
196        }
197        if s.weeknum.is_some() {
198            self.weeknum_style = s.weeknum;
199        }
200        if s.weekday.is_some() {
201            self.weekday_style = s.weekday;
202        }
203        if s.day.is_some() {
204            self.day_style = s.day;
205        }
206        if s.select.is_some() {
207            self.select_style = s.select;
208        }
209        if s.focus.is_some() {
210            self.focus_style = s.focus;
211        }
212        if s.block.is_some() {
213            self.block = s.block;
214        }
215        self.block = self.block.map(|v| v.style(self.style));
216        self
217    }
218
219    /// Style for the selection
220    pub fn select_style(mut self, style: Style) -> Self {
221        self.select_style = Some(style);
222        self
223    }
224
225    /// Style for the focus.
226    pub fn focus_style(mut self, style: Style) -> Self {
227        self.focus_style = Some(style);
228        self
229    }
230
231    /// Sets the default day-style.
232    #[inline]
233    pub fn day_style(mut self, s: impl Into<Style>) -> Self {
234        self.day_style = Some(s.into());
235        self
236    }
237
238    /// Set a map date->Style for highlighting some dates.
239    #[inline]
240    pub fn day_styles(mut self, styles: &'a HashMap<NaiveDate, Style>) -> Self {
241        self.day_styles = Some(styles);
242        self
243    }
244
245    /// Set the week number style.
246    #[inline]
247    pub fn week_style(mut self, s: impl Into<Style>) -> Self {
248        self.weeknum_style = Some(s.into());
249        self
250    }
251
252    /// Set the week day style.
253    #[inline]
254    pub fn weekday_style(mut self, s: impl Into<Style>) -> Self {
255        self.weekday_style = Some(s.into());
256        self
257    }
258
259    /// Set the month-name style.
260    #[inline]
261    pub fn title_style(mut self, s: impl Into<Style>) -> Self {
262        self.title_style = Some(s.into());
263        self
264    }
265
266    /// Set the month-name align.
267    #[inline]
268    pub fn title_align(mut self, a: Alignment) -> Self {
269        self.title_align = a;
270        self
271    }
272
273    /// Block
274    #[inline]
275    pub fn block(mut self, b: Block<'a>) -> Self {
276        self.block = Some(b);
277        self.block = self.block.map(|v| v.style(self.style));
278        self
279    }
280
281    /// Inherent width of the widget.
282    #[inline]
283    pub fn width(&self) -> u16 {
284        8 * 3 + block_size(&self.block).width
285    }
286
287    /// Inherent height for the widget.
288    /// This is not a fixed value, depends on the month.
289    #[inline]
290    pub fn height(&self, state: &MonthState<Selection>) -> u16 {
291        let start_date = if let Some(start_date) = self.start_date {
292            start_date
293        } else {
294            state.start_date
295        };
296
297        let r = MonthState::<Selection>::count_weeks(start_date) as u16;
298        let w = if self.show_weekdays { 1 } else { 0 };
299        let b = max(1, block_size(&self.block).height);
300        r + w + b
301    }
302}
303
304impl<'a, Selection> StatefulWidget for &Month<'a, Selection>
305where
306    Selection: CalendarSelection,
307{
308    type State = MonthState<Selection>;
309
310    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
311        render_ref(self, area, buf, state);
312    }
313}
314
315impl<Selection> StatefulWidget for Month<'_, Selection>
316where
317    Selection: CalendarSelection,
318{
319    type State = MonthState<Selection>;
320
321    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
322        render_ref(&self, area, buf, state);
323    }
324}
325
326fn render_ref<Selection: CalendarSelection>(
327    widget: &Month<'_, Selection>,
328    area: Rect,
329    buf: &mut Buffer,
330    state: &mut MonthState<Selection>,
331) {
332    state.area = area;
333    if let Some(start_date) = widget.start_date {
334        state.start_date = start_date;
335    }
336
337    let mut day = state.start_date;
338
339    let focus_style = widget.focus_style.unwrap_or(revert_style(widget.style));
340    let select_style = if let Some(select_style) = widget.select_style {
341        if state.is_container_focused() || state.is_focused() {
342            focus_style
343        } else {
344            select_style
345        }
346    } else {
347        if state.is_container_focused() || state.is_focused() {
348            focus_style
349        } else {
350            revert_style(widget.style)
351        }
352    };
353    let day_style = widget.day_style.unwrap_or(widget.style);
354    let week_style = widget.weeknum_style.unwrap_or(widget.style);
355    let weekday_style = widget.weekday_style.unwrap_or(widget.style);
356
357    let title_style = if let Some(title_style) = widget.title_style {
358        title_style
359    } else {
360        widget.style
361    };
362    let title_style = if state.is_focused() {
363        title_style.patch(focus_style)
364    } else {
365        title_style
366    };
367
368    let block = if widget.show_month {
369        if let Some(block) = widget.block.clone() {
370            block
371                .title(Title::from(
372                    day.format_localized("%B", widget.loc).to_string(),
373                ))
374                .title_style(title_style)
375                .title_alignment(widget.title_align)
376        } else {
377            Block::new()
378                .style(widget.style)
379                .title(Title::from(
380                    day.format_localized("%B", widget.loc).to_string(),
381                ))
382                .title_style(title_style)
383                .title_alignment(widget.title_align)
384        }
385    } else {
386        if let Some(block) = widget.block.clone() {
387            block
388        } else {
389            Block::new().style(widget.style)
390        }
391    };
392    state.inner = block.inner(area);
393    block.render(area, buf);
394
395    let month = day.month();
396    let mut w = 0;
397    let mut x = state.inner.x;
398    let mut y = state.inner.y;
399
400    // week days
401    if widget.show_weekdays {
402        let mut week_0 = day.week(Weekday::Mon).first_day();
403
404        x += 3;
405        buf.set_style(Rect::new(x, y, 3 * 7, 1), weekday_style);
406        for _ in 0..7 {
407            let area = Rect::new(x, y, 2, 1).intersection(state.inner);
408
409            let day_name = week_0.format_localized("%a", widget.loc).to_string();
410            Span::from(format!("{:2} ", day_name)).render(area, buf);
411
412            x += 3;
413            week_0 = week_0 + Days::new(1);
414        }
415        x = state.inner.x;
416        y += 1;
417    }
418
419    // reset areas
420    for i in 0..31 {
421        state.area_days[i] = Rect::default();
422    }
423    for i in 0..6 {
424        state.area_weeks[i] = Rect::default();
425    }
426    state.area_cal = Rect::new(x + 3, y, 7 * 3, state.week_len() as u16);
427    state.area_weeknum = Rect::new(x, y, 3, state.week_len() as u16);
428
429    // first line may omit a few days
430    state.area_weeks[w] = Rect::new(x, y, 2, 1).intersection(state.inner);
431    Span::from(day.format_localized("%V", widget.loc).to_string())
432        .style(week_style)
433        .render(state.area_weeks[w], buf);
434
435    x += 3;
436
437    for wd in [
438        Weekday::Mon,
439        Weekday::Tue,
440        Weekday::Wed,
441        Weekday::Thu,
442        Weekday::Fri,
443        Weekday::Sat,
444        Weekday::Sun,
445    ] {
446        if day.weekday() != wd {
447            x += 3;
448        } else {
449            let day_style = calc_day_style(widget, state, day, day_style, select_style);
450            state.area_days[day.day0() as usize] = Rect::new(x, y, 2, 1).intersection(state.inner);
451
452            Span::from(day.format_localized("%e", widget.loc).to_string())
453                .style(day_style)
454                .render(state.area_days[day.day0() as usize], buf);
455
456            if wd != Weekday::Sun && state.selection.is_selected(day + Days::new(1)) {
457                let mut gap_area = state.area_days[day.day0() as usize];
458                gap_area.x += 2;
459                gap_area.width = 1;
460                Span::from(" ").style(day_style).render(gap_area, buf);
461            }
462
463            x += 3;
464            day = day + Days::new(1);
465        }
466    }
467
468    w += 1;
469    x = state.inner.x;
470    y += 1;
471
472    while month == day.month() {
473        state.area_weeks[w] = Rect::new(x, y, 2, 1).intersection(state.inner);
474        Span::from(day.format_localized("%V", widget.loc).to_string())
475            .style(week_style)
476            .render(state.area_weeks[w], buf);
477
478        x += 3;
479
480        for wd in [
481            Weekday::Mon,
482            Weekday::Tue,
483            Weekday::Wed,
484            Weekday::Thu,
485            Weekday::Fri,
486            Weekday::Sat,
487            Weekday::Sun,
488        ] {
489            if day.month() == month {
490                let day_style = calc_day_style(widget, state, day, day_style, select_style);
491
492                state.area_days[day.day0() as usize] =
493                    Rect::new(x, y, 2, 1).intersection(state.inner);
494
495                Span::from(day.format_localized("%e", widget.loc).to_string())
496                    .style(day_style)
497                    .render(state.area_days[day.day0() as usize], buf);
498
499                if wd != Weekday::Sun && state.selection.is_selected(day + Days::new(1)) {
500                    let mut gap_area = state.area_days[day.day0() as usize];
501                    gap_area.x += 2;
502                    gap_area.width = 1;
503                    Span::from(" ").style(day_style).render(gap_area, buf);
504                }
505
506                x += 3;
507                day = day + Days::new(1);
508            } else {
509                x += 3;
510            }
511        }
512
513        w += 1;
514        x = state.inner.x;
515        y += 1;
516    }
517}
518
519fn calc_day_style<Selection: CalendarSelection>(
520    widget: &Month<'_, Selection>,
521    state: &mut MonthState<Selection>,
522    day: NaiveDate,
523    day_style: Style,
524    select_style: Style,
525) -> Style {
526    let day_style = if let Some(day_styles) = widget.day_styles {
527        if let Some(day_style) = day_styles.get(&day) {
528            *day_style
529        } else {
530            day_style
531        }
532    } else {
533        day_style
534    };
535
536    if (state.is_container_focused() || state.is_focused())
537        && state.selection.lead_selection() == Some(day)
538    {
539        day_style.patch(select_style.underlined())
540    } else if state.selection.is_selected(day) {
541        day_style.patch(select_style)
542    } else {
543        day_style
544    }
545}
546
547impl<Selection> HasFocus for MonthState<Selection> {
548    fn build(&self, builder: &mut FocusBuilder) {
549        builder.leaf_widget(self);
550    }
551
552    #[inline]
553    fn focus(&self) -> FocusFlag {
554        self.focus.clone()
555    }
556
557    #[inline]
558    fn area(&self) -> Rect {
559        self.area
560    }
561}
562
563impl<Selection> RelocatableState for MonthState<Selection> {
564    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
565        self.area.relocate(shift, clip);
566        self.inner.relocate(shift, clip);
567        self.area_cal.relocate(shift, clip);
568        self.area_weeknum.relocate(shift, clip);
569        self.area_days.relocate(shift, clip);
570        self.area_weeks.relocate(shift, clip);
571    }
572}
573
574impl<Selection> Clone for MonthState<Selection>
575where
576    Selection: Clone,
577{
578    fn clone(&self) -> Self {
579        Self {
580            area: self.area,
581            inner: self.inner,
582            area_cal: self.area_cal.clone(),
583            area_days: self.area_days.clone(),
584            area_weeknum: self.area_weeknum.clone(),
585            area_weeks: self.area_weeks.clone(),
586            start_date: self.start_date,
587            selection: self.selection.clone(),
588            container: self.container.clone(),
589            focus: FocusFlag::named(self.focus.name()),
590            mouse: Default::default(),
591            non_exhaustive: NonExhaustive,
592        }
593    }
594}
595
596impl<Selection> Default for MonthState<Selection>
597where
598    Selection: Default,
599{
600    fn default() -> Self {
601        Self {
602            area: Default::default(),
603            inner: Default::default(),
604            area_cal: Default::default(),
605            area_days: Default::default(),
606            area_weeknum: Default::default(),
607            area_weeks: Default::default(),
608            start_date: Default::default(),
609            selection: Default::default(),
610            container: Default::default(),
611            focus: Default::default(),
612            mouse: Default::default(),
613            non_exhaustive: NonExhaustive,
614        }
615    }
616}
617
618impl<Selection> MonthState<Selection> {
619    pub fn new() -> Self
620    where
621        Selection: Default,
622    {
623        Self::default()
624    }
625
626    pub fn named(name: &str) -> Self
627    where
628        Selection: Default,
629    {
630        Self {
631            focus: FocusFlag::named(name),
632            ..Self::default()
633        }
634    }
635
636    /// Sets the start-date of the calendar. You can set every date, it
637    /// will always be changed to the first of the month.
638    ///
639    /// Setting this will be useless if the date is set with the Month widget.
640    pub fn set_start_date(&mut self, date: NaiveDate) -> bool {
641        let old_value = self.start_date;
642        self.start_date = first_day_of_month(date);
643        old_value != self.start_date
644    }
645
646    /// Start date of this month. Will always be the first.
647    pub fn start_date(&self) -> NaiveDate {
648        self.start_date
649    }
650
651    /// End date of this month.
652    pub fn end_date(&self) -> NaiveDate {
653        last_day_of_month(self.start_date)
654    }
655
656    fn in_range(&self, date: NaiveDate) -> bool {
657        date >= self.start_date() && date <= self.end_date()
658    }
659
660    /// Nr of weeks in this month.
661    pub fn week_len(&self) -> usize {
662        Self::count_weeks(self.start_date)
663    }
664
665    /// Nr of weeks for the given month
666    pub fn count_weeks(day: NaiveDate) -> usize {
667        let mut day = day.with_day0(0).expect("date");
668        let month = day.month();
669
670        let mut weeks = 1;
671        for weekday in [
672            Weekday::Mon,
673            Weekday::Tue,
674            Weekday::Wed,
675            Weekday::Thu,
676            Weekday::Fri,
677            Weekday::Sat,
678            Weekday::Sun,
679        ] {
680            // run through first week
681            if day.weekday() == weekday {
682                day = day + Days::new(1);
683            }
684        }
685        // count mondays
686        while month == day.month() {
687            weeks += 1;
688            day = day + Days::new(7);
689        }
690
691        weeks
692    }
693
694    // is there a container for this month?
695    fn is_container_focused(&self) -> bool {
696        self.container
697            .as_ref()
698            .map(|v| v.is_focused())
699            .unwrap_or(false)
700    }
701}
702
703impl<Selection> MonthState<Selection>
704where
705    Selection: CalendarSelection,
706{
707    /// Lead selection
708    pub fn lead_selection(&self) -> Option<NaiveDate> {
709        self.selection.lead_selection()
710    }
711}
712
713impl MonthState<NoSelection> {}
714
715impl MonthState<SingleSelection> {
716    /// Removes all selection.
717    pub fn clear_selection(&mut self) {
718        self.selection.borrow_mut().clear();
719    }
720
721    /// Select a day by index.
722    pub fn select_day(&mut self, n: usize) -> CalOutcome {
723        if let Some(date) = self.start_date.with_day0(n as u32) {
724            if self.selection.borrow_mut().select(date) {
725                CalOutcome::Selected
726            } else {
727                CalOutcome::Continue
728            }
729        } else {
730            CalOutcome::Continue
731        }
732    }
733
734    /// Select the last day of the month.
735    pub fn select_last(&mut self) -> CalOutcome {
736        let date = self.end_date();
737        if self.selection.borrow_mut().select(date) {
738            CalOutcome::Selected
739        } else {
740            CalOutcome::Continue
741        }
742    }
743
744    /// Select by date.
745    /// Returns true if the date is valid for this month.
746    /// If false it doesn't change the selection.
747    pub fn select_date(&mut self, d: NaiveDate) -> bool {
748        let start = self.start_date;
749        if d.year() == start.year() && d.month() == start.month() {
750            self.selection.borrow_mut().select(d)
751        } else {
752            false
753        }
754    }
755
756    /// Lead selected day
757    pub fn selected_date(&self) -> Option<NaiveDate> {
758        self.selection.lead_selection()
759    }
760
761    /// Select previous day.
762    pub fn prev_day(&mut self, n: usize) -> CalOutcome {
763        let base_start = self.start_date();
764        let base_end = self.end_date();
765
766        let date = if let Some(date) = self.selection.lead_selection() {
767            if date >= base_start && date <= base_end {
768                date - Days::new(n as u64)
769            } else if date < base_start {
770                self.start_date()
771            } else {
772                self.end_date()
773            }
774        } else {
775            self.end_date()
776        };
777
778        if self.in_range(date) {
779            if self.selection.borrow_mut().select(date) {
780                CalOutcome::Selected
781            } else {
782                CalOutcome::Continue
783            }
784        } else {
785            CalOutcome::Continue
786        }
787    }
788
789    /// Select next day.
790    pub fn next_day(&mut self, n: usize) -> CalOutcome {
791        let base_start = self.start_date();
792        let base_end = self.end_date();
793
794        let date = if let Some(date) = self.selection.lead_selection() {
795            if date >= base_start && date <= base_end {
796                date + Days::new(n as u64)
797            } else if date < base_start {
798                self.start_date()
799            } else {
800                self.end_date()
801            }
802        } else {
803            self.start_date()
804        };
805
806        if self.in_range(date) {
807            if self.selection.borrow_mut().select(date) {
808                CalOutcome::Selected
809            } else {
810                CalOutcome::Continue
811            }
812        } else {
813            CalOutcome::Continue
814        }
815    }
816}
817
818impl MonthState<RangeSelection> {
819    /// Removes all selection.
820    pub fn clear_selection(&mut self) {
821        self.selection.borrow_mut().clear();
822    }
823
824    /// Select a week by index.
825    pub fn select_week(&mut self, n: usize, extend: bool) -> CalOutcome {
826        if n < self.week_len() {
827            let date = self.start_date() + Days::new(7 * n as u64);
828            if self.selection.borrow_mut().select_week(date, extend) {
829                CalOutcome::Selected
830            } else {
831                CalOutcome::Continue
832            }
833        } else {
834            CalOutcome::Continue
835        }
836    }
837
838    /// Select a day by index.
839    pub fn select_day(&mut self, n: usize, extend: bool) -> CalOutcome {
840        if let Some(date) = self.start_date.with_day0(n as u32) {
841            if self.selection.borrow_mut().select_day(date, extend) {
842                CalOutcome::Selected
843            } else {
844                CalOutcome::Continue
845            }
846        } else {
847            CalOutcome::Continue
848        }
849    }
850
851    /// Select the last day
852    pub fn select_last(&mut self, extend: bool) -> CalOutcome {
853        let date = self.end_date();
854        if self.selection.borrow_mut().select_day(date, extend) {
855            CalOutcome::Selected
856        } else {
857            CalOutcome::Continue
858        }
859    }
860
861    /// Select a week by date
862    /// Returns true if the date is valid for this month.
863    /// If false it doesn't change the selection.
864    pub fn select_week_by_date(&mut self, date: NaiveDate, extend: bool) -> bool {
865        let base = self.start_date;
866
867        let start = date.week(Weekday::Mon).first_day();
868        let end = date.week(Weekday::Mon).last_day();
869
870        if (start.year() == base.year() && start.month() == base.month())
871            || (end.year() == base.year() && end.month() == base.month())
872        {
873            self.selection.borrow_mut().select_week(start, extend)
874        } else {
875            false
876        }
877    }
878
879    /// Select by date.
880    /// Returns true if the date is valid for this month.
881    /// If false it doesn't change the selection.
882    pub fn select_date(&mut self, d: NaiveDate, extend: bool) -> bool {
883        let base = self.start_date;
884        if d.year() == base.year() && d.month() == base.month() {
885            self.selection.borrow_mut().select_day(d, extend)
886        } else {
887            false
888        }
889    }
890
891    /// Lead selected day
892    pub fn selected_date(&self) -> Option<NaiveDate> {
893        self.selection.lead_selection()
894    }
895
896    /// Select previous day.
897    pub fn prev_day(&mut self, n: usize, extend: bool) -> CalOutcome {
898        let base_start = self.start_date();
899        let base_end = self.end_date();
900
901        let date = if let Some(date) = self.selection.lead_selection() {
902            if date >= base_start && date <= base_end {
903                date - Days::new(n as u64)
904            } else if date < base_start {
905                self.start_date()
906            } else {
907                self.end_date()
908            }
909        } else {
910            self.end_date()
911        };
912
913        if self.in_range(date) {
914            if self.selection.borrow_mut().select_day(date, extend) {
915                CalOutcome::Selected
916            } else {
917                CalOutcome::Continue
918            }
919        } else {
920            CalOutcome::Continue
921        }
922    }
923
924    /// Select previous day.
925    pub fn next_day(&mut self, n: usize, extend: bool) -> CalOutcome {
926        let base_start = self.start_date();
927        let base_end = self.end_date();
928
929        let date = if let Some(date) = self.selection.lead_selection() {
930            if date >= base_start && date <= base_end {
931                date + Days::new(n as u64)
932            } else if date < base_start {
933                self.start_date()
934            } else {
935                self.end_date()
936            }
937        } else {
938            self.start_date()
939        };
940
941        if self.in_range(date) {
942            if self.selection.borrow_mut().select_day(date, extend) {
943                CalOutcome::Selected
944            } else {
945                CalOutcome::Continue
946            }
947        } else {
948            CalOutcome::Continue
949        }
950    }
951
952    /// Select previous week.
953    pub fn prev_week(&mut self, n: usize, extend: bool) -> CalOutcome {
954        let base_start = self.start_date();
955        let base_end = self.end_date();
956
957        if let Some(date) = self.selection.lead_selection() {
958            let new_date = if date >= base_start && date <= base_end {
959                date - Days::new(7 * n as u64)
960            } else if date < base_start {
961                self.start_date()
962            } else {
963                self.end_date()
964            };
965            let new_date_end = new_date.week(Weekday::Mon).last_day();
966            if new_date_end >= base_start && new_date_end <= base_end {
967                if self.selection.borrow_mut().select_week(new_date, extend) {
968                    CalOutcome::Selected
969                } else {
970                    CalOutcome::Continue
971                }
972            } else {
973                CalOutcome::Continue
974            }
975        } else {
976            let new_date = self.end_date();
977            if self.selection.borrow_mut().select_week(new_date, extend) {
978                CalOutcome::Selected
979            } else {
980                CalOutcome::Continue
981            }
982        }
983    }
984
985    /// Select previous day.
986    pub fn next_week(&mut self, n: usize, extend: bool) -> CalOutcome {
987        let start = self.start_date();
988        let end = self.end_date();
989
990        let new_date = if let Some(date) = self.selection.lead_selection() {
991            let date_end = date.week(Weekday::Mon).last_day();
992            if date_end >= start && date_end <= end {
993                date + Days::new(7 * n as u64)
994            } else if date_end < start {
995                self.start_date()
996            } else {
997                self.end_date()
998            }
999        } else {
1000            self.start_date()
1001        };
1002
1003        if new_date >= start && new_date <= end {
1004            if self.selection.borrow_mut().select_week(new_date, extend) {
1005                CalOutcome::Selected
1006            } else {
1007                CalOutcome::Continue
1008            }
1009        } else {
1010            CalOutcome::Continue
1011        }
1012    }
1013}