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