rat_widget/calendar/
single_selection.rs1use 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#[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}