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