Skip to main content

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        if !self.has_mouse_focus() {
72            return CalOutcome::Continue;
73        }
74
75        match event {
76            ct_event!(mouse drag Left for x, y) | ct_event!(mouse down Left for x, y) => {
77                if let Some(sel) = item_at(&self.area_days, *x, *y) {
78                    self.select_day(sel)
79                } else {
80                    CalOutcome::Continue
81                }
82            }
83
84            _ => CalOutcome::Continue,
85        }
86    }
87}
88
89impl<const N: usize> HandleEvent<Event, Regular, CalOutcome> for CalendarState<N, SingleSelection> {
90    fn handle(&mut self, event: &Event, _qualifier: Regular) -> CalOutcome {
91        let mut r = 'f: {
92            for month in &mut self.months {
93                let r = month.handle(event, Regular);
94                if r.is_consumed() {
95                    self.focus_lead();
96                    break 'f r;
97                }
98            }
99            CalOutcome::Continue
100        };
101
102        r = r.or_else(|| {
103            if self.is_focused() {
104                match event {
105                    ct_event!(keycode press CONTROL-Home) => self.move_to_today(),
106                    ct_event!(keycode press PageUp) => self.prev_month(1),
107                    ct_event!(keycode press PageDown) => self.next_month(1),
108                    ct_event!(keycode press Up) => self.prev_day(7),
109                    ct_event!(keycode press Down) => self.next_day(7),
110                    ct_event!(keycode press Left) => self.prev_day(1),
111                    ct_event!(keycode press Right) => self.next_day(1),
112
113                    _ => CalOutcome::Continue,
114                }
115            } else {
116                CalOutcome::Continue
117            }
118        });
119
120        r.or_else(|| self.handle(event, MouseOnly))
121    }
122}
123
124impl<const N: usize> HandleEvent<Event, MouseOnly, CalOutcome>
125    for CalendarState<N, SingleSelection>
126{
127    fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> CalOutcome {
128        if !self.has_mouse_focus() {
129            return CalOutcome::Continue;
130        }
131
132        for i in 0..self.months.len() {
133            if self.months[i].gained_focus() {
134                self.set_primary_idx(i);
135                break;
136            }
137        }
138
139        let all_areas = self
140            .months
141            .iter()
142            .map(|v| v.area)
143            .reduce(|v, w| v.union(w))
144            .unwrap_or_default();
145        match event {
146            ct_event!(scroll up for x,y) if all_areas.contains((*x, *y).into()) => {
147                self.scroll_back(self.step())
148            }
149            ct_event!(scroll down for x,y) if all_areas.contains((*x, *y).into()) => {
150                self.scroll_forward(self.step())
151            }
152            _ => CalOutcome::Continue,
153        }
154    }
155}