rat_widget/calendar/
single_selection.rs

1use crate::calendar::event::CalOutcome;
2use crate::calendar::{CalendarSelection, CalendarState, MonthState};
3use chrono::NaiveDate;
4use rat_event::util::item_at;
5use rat_event::{ConsumedEvent, HandleEvent, MouseOnly, Regular, ct_event, flow};
6use rat_focus::HasFocus;
7
8/// Can select a single date.
9///
10/// Movement with the arrow keys and PageUp/PageDown.
11/// Ctrl+Home moves to today.
12///
13#[derive(Debug, Default, Clone)]
14pub struct SingleSelection {
15    selected: Option<NaiveDate>,
16}
17
18impl CalendarSelection for SingleSelection {
19    fn count(&self) -> usize {
20        if self.selected.is_some() { 1 } else { 0 }
21    }
22
23    fn is_selected(&self, date: NaiveDate) -> bool {
24        self.selected == Some(date)
25    }
26
27    fn lead_selection(&self) -> Option<NaiveDate> {
28        self.selected
29    }
30}
31
32impl SingleSelection {
33    pub fn clear(&mut self) {
34        self.selected = None;
35    }
36
37    pub fn select(&mut self, date: NaiveDate) -> bool {
38        let old = self.selected;
39        self.selected = Some(date);
40        old != self.selected
41    }
42
43    pub fn selected(&self) -> Option<NaiveDate> {
44        self.selected
45    }
46}
47
48impl HandleEvent<crossterm::event::Event, Regular, CalOutcome> for MonthState<SingleSelection> {
49    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> CalOutcome {
50        if self.is_focused() {
51            flow!(match event {
52                ct_event!(keycode press Home) => self.select_day(0),
53                ct_event!(keycode press End) => self.select_last(),
54                ct_event!(keycode press Up) => self.prev_day(7),
55                ct_event!(keycode press Down) => self.next_day(7),
56                ct_event!(keycode press Left) => self.prev_day(1),
57                ct_event!(keycode press Right) => self.next_day(1),
58                _ => CalOutcome::Continue,
59            })
60        }
61
62        self.handle(event, MouseOnly)
63    }
64}
65
66impl HandleEvent<crossterm::event::Event, MouseOnly, CalOutcome> for MonthState<SingleSelection> {
67    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> CalOutcome {
68        match event {
69            ct_event!(mouse drag Left for x, y) | ct_event!(mouse down Left for x, y) => {
70                if let Some(sel) = item_at(&self.area_days, *x, *y) {
71                    self.select_day(sel)
72                } else {
73                    CalOutcome::Continue
74                }
75            }
76
77            _ => CalOutcome::Continue,
78        }
79    }
80}
81
82impl<const N: usize> HandleEvent<crossterm::event::Event, Regular, CalOutcome>
83    for CalendarState<N, SingleSelection>
84{
85    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> CalOutcome {
86        let mut r = 'f: {
87            for month in &mut self.months {
88                let r = month.handle(event, Regular);
89                if r.is_consumed() {
90                    self.focus_lead();
91                    break 'f r;
92                }
93            }
94            CalOutcome::Continue
95        };
96
97        r = r.or_else(|| {
98            if self.is_focused() {
99                match event {
100                    ct_event!(keycode press CONTROL-Home) => self.move_to_today(),
101                    ct_event!(keycode press PageUp) => self.prev_month(1),
102                    ct_event!(keycode press PageDown) => self.next_month(1),
103                    ct_event!(keycode press Up) => self.prev_day(7),
104                    ct_event!(keycode press Down) => self.next_day(7),
105                    ct_event!(keycode press Left) => self.prev_day(1),
106                    ct_event!(keycode press Right) => self.next_day(1),
107
108                    _ => CalOutcome::Continue,
109                }
110            } else {
111                CalOutcome::Continue
112            }
113        });
114
115        r.or_else(|| self.handle(event, MouseOnly))
116    }
117}
118
119impl<const N: usize> HandleEvent<crossterm::event::Event, MouseOnly, CalOutcome>
120    for CalendarState<N, SingleSelection>
121{
122    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> CalOutcome {
123        for i in 0..self.months.len() {
124            if self.months[i].gained_focus() {
125                self.set_primary_idx(i);
126                break;
127            }
128        }
129
130        let all_areas = self
131            .months
132            .iter()
133            .map(|v| v.area)
134            .reduce(|v, w| v.union(w))
135            .unwrap_or_default();
136        match event {
137            ct_event!(scroll up for x,y) if all_areas.contains((*x, *y).into()) => {
138                self.scroll_back(self.step())
139            }
140            ct_event!(scroll down for x,y) if all_areas.contains((*x, *y).into()) => {
141                self.scroll_forward(self.step())
142            }
143            _ => CalOutcome::Continue,
144        }
145    }
146}