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