rat_widget/calendar/
single_selection.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use crate::calendar::event::CalOutcome;
use crate::calendar::{CalendarSelection, CalendarState, MonthState};
use chrono::NaiveDate;
use rat_event::util::item_at;
use rat_event::{ct_event, flow, ConsumedEvent, HandleEvent, MouseOnly, Regular};
use rat_focus::HasFocus;

/// Can select a single date.
///
/// Movement with the arrow keys and PageUp/PageDown.
/// Ctrl+Home moves to today.
///
#[derive(Debug, Default, Clone)]
pub struct SingleSelection {
    selected: Option<NaiveDate>,
}

impl CalendarSelection for SingleSelection {
    fn count(&self) -> usize {
        if self.selected.is_some() {
            1
        } else {
            0
        }
    }

    fn is_selected(&self, date: NaiveDate) -> bool {
        self.selected == Some(date)
    }

    fn lead_selection(&self) -> Option<NaiveDate> {
        self.selected
    }
}

impl SingleSelection {
    pub fn clear(&mut self) {
        self.selected = None;
    }

    pub fn select(&mut self, date: NaiveDate) -> bool {
        let old = self.selected;
        self.selected = Some(date);
        old != self.selected
    }

    pub fn selected(&self) -> Option<NaiveDate> {
        self.selected
    }
}

impl HandleEvent<crossterm::event::Event, Regular, CalOutcome> for MonthState<SingleSelection> {
    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> CalOutcome {
        if self.is_focused() {
            flow!(match event {
                ct_event!(keycode press Up) => self.prev_day(7),
                ct_event!(keycode press Down) => self.next_day(7),
                ct_event!(keycode press Left) => self.prev_day(1),
                ct_event!(keycode press Right) => self.next_day(1),
                _ => CalOutcome::Continue,
            })
        }

        self.handle(event, MouseOnly)
    }
}

impl HandleEvent<crossterm::event::Event, MouseOnly, CalOutcome> for MonthState<SingleSelection> {
    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> CalOutcome {
        match event {
            ct_event!(mouse drag Left for x, y) | ct_event!(mouse down Left for x, y) => {
                if let Some(sel) = item_at(&self.area_days, *x, *y) {
                    self.select_day(sel)
                } else {
                    CalOutcome::Continue
                }
            }

            _ => CalOutcome::Continue,
        }
    }
}

impl<const N: usize> HandleEvent<crossterm::event::Event, Regular, CalOutcome>
    for CalendarState<N, SingleSelection>
{
    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> CalOutcome {
        let mut r = 'f: {
            for month in &mut self.months {
                let r = month.handle(event, Regular);
                if r.is_consumed() {
                    //todo: change to on selected
                    self.focus_lead();
                    break 'f r;
                }
            }
            CalOutcome::Continue
        };

        r = r.or_else(|| {
            if self.is_focused() {
                match event {
                    ct_event!(keycode press CONTROL-Home) => self.move_to_today(),
                    ct_event!(keycode press PageUp) => self.prev_month(1),
                    ct_event!(keycode press PageDown) => self.next_month(1),
                    ct_event!(keycode press Up) => self.prev_day(7),
                    ct_event!(keycode press Down) => self.next_day(7),
                    ct_event!(keycode press Left) => self.prev_day(1),
                    ct_event!(keycode press Right) => self.next_day(1),

                    _ => CalOutcome::Continue,
                }
            } else {
                CalOutcome::Continue
            }
        });

        r.or_else(|| self.handle(event, MouseOnly))
    }
}

impl<const N: usize> HandleEvent<crossterm::event::Event, MouseOnly, CalOutcome>
    for CalendarState<N, SingleSelection>
{
    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: MouseOnly) -> CalOutcome {
        for i in 0..self.months.len() {
            if self.months[i].gained_focus() {
                self.set_primary_idx(i);
                break;
            }
        }

        let all_areas = self
            .months
            .iter()
            .map(|v| v.area)
            .reduce(|v, w| v.union(w))
            .unwrap_or_default();
        match event {
            ct_event!(scroll up for x,y) if all_areas.contains((*x, *y).into()) => {
                self.scroll_back(self.step())
            }
            ct_event!(scroll down for x,y) if all_areas.contains((*x, *y).into()) => {
                self.scroll_forward(self.step())
            }
            _ => CalOutcome::Continue,
        }
    }
}