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 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}