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