1use crate::event::TableOutcome;
2use crate::{TableSelection, TableState};
3use rat_event::{ct_event, ConsumedEvent, HandleEvent, MouseOnly, Regular};
4use rat_focus::HasFocus;
5use rat_scrolled::event::ScrollOutcome;
6use rat_scrolled::ScrollAreaState;
7use std::cmp::{max, min};
8
9#[derive(Debug, Default, Clone)]
13pub struct CellSelection {
14 pub lead_cell: Option<(usize, usize)>,
16}
17
18impl TableSelection for CellSelection {
19 fn count(&self) -> usize {
20 if self.lead_cell.is_some() {
21 1
22 } else {
23 0
24 }
25 }
26
27 fn is_selected_row(&self, row: usize) -> bool {
28 self.lead_cell.map(|(_scol, srow)| srow) == Some(row)
29 }
30
31 fn is_selected_column(&self, column: usize) -> bool {
32 self.lead_cell.map(|(scol, _srow)| scol) == Some(column)
33 }
34
35 fn is_selected_cell(&self, col: usize, row: usize) -> bool {
36 self.lead_cell == Some((col, row))
37 }
38
39 fn lead_selection(&self) -> Option<(usize, usize)> {
40 self.lead_cell
41 }
42}
43
44impl CellSelection {
45 pub fn new() -> CellSelection {
47 Self::default()
48 }
49
50 #[inline]
52 pub fn clear(&mut self) {
53 self.lead_cell = None;
54 }
55
56 pub fn selected(&self) -> Option<(usize, usize)> {
58 self.lead_cell
59 }
60
61 #[inline]
62 pub fn has_selection(&mut self) -> bool {
63 self.lead_cell.is_some()
64 }
65
66 pub fn select_cell(&mut self, select: Option<(usize, usize)>) -> bool {
68 let old_cell = self.lead_cell;
69 self.lead_cell = select;
70 old_cell != self.lead_cell
71 }
72
73 pub fn select_row(&mut self, select: Option<usize>) -> bool {
75 let old_cell = self.lead_cell;
76 self.lead_cell = match self.lead_cell {
77 None => select.map(|v| (0, v)),
78 Some((scol, _)) => select.map(|v| (scol, v)),
79 };
80 old_cell != self.lead_cell
81 }
82
83 pub fn select_column(&mut self, select: Option<usize>) -> bool {
85 let old_cell = self.lead_cell;
86 self.lead_cell = match self.lead_cell {
87 None => select.map(|v| (v, 0)),
88 Some((_, srow)) => select.map(|v| (v, srow)),
89 };
90 old_cell != self.lead_cell
91 }
92
93 pub fn move_to(&mut self, select: (usize, usize), maximum: (usize, usize)) -> bool {
95 let c = self.move_to_col(select.0, maximum.0);
96 let r = self.move_to_row(select.1, maximum.1);
97 c || r
98 }
99
100 pub fn move_to_col(&mut self, col: usize, maximum: usize) -> bool {
102 let old = self.lead_cell;
103 let col = min(col, maximum);
104 self.lead_cell = self
105 .lead_cell
106 .map_or(Some((col, 0)), |(_, srow)| Some((col, srow)));
107 old != self.lead_cell
108 }
109
110 pub fn move_to_row(&mut self, row: usize, maximum: usize) -> bool {
112 let old = self.lead_cell;
113 let row = min(row, maximum);
114 self.lead_cell = self
115 .lead_cell
116 .map_or(Some((0, row)), |(scol, _)| Some((scol, row)));
117 old != self.lead_cell
118 }
119
120 pub fn move_down(&mut self, n: usize, maximum: usize) -> bool {
122 let old_cell = self.lead_cell;
123 self.lead_cell = match self.lead_cell {
124 None => Some((0, 0)),
125 Some((scol, srow)) => Some((scol, min(srow + n, maximum))),
126 };
127 old_cell != self.lead_cell
128 }
129
130 pub fn move_up(&mut self, n: usize, maximum: usize) -> bool {
132 let old_cell = self.lead_cell;
133 self.lead_cell = match self.lead_cell {
134 None => Some((0, maximum)),
135 Some((scol, srow)) => Some((scol, srow.saturating_sub(n))),
136 };
137 old_cell != self.lead_cell
138 }
139
140 pub fn move_right(&mut self, n: usize, maximum: usize) -> bool {
142 let old_cell = self.lead_cell;
143 self.lead_cell = match self.lead_cell {
144 None => Some((0, 0)),
145 Some((scol, srow)) => Some((min(scol + n, maximum), srow)),
146 };
147 old_cell != self.lead_cell
148 }
149
150 pub fn move_left(&mut self, n: usize, maximum: usize) -> bool {
152 let old_cell = self.lead_cell;
153 self.lead_cell = match self.lead_cell {
154 None => Some((maximum, 0)),
155 Some((scol, srow)) => Some((scol.saturating_sub(n), srow)),
156 };
157 old_cell != self.lead_cell
158 }
159}
160
161impl HandleEvent<crossterm::event::Event, Regular, TableOutcome> for TableState<CellSelection> {
162 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TableOutcome {
163 let res = if self.is_focused() {
164 match event {
165 ct_event!(keycode press Up) => {
166 if self.move_up(1) {
167 TableOutcome::Selected
168 } else {
169 TableOutcome::Unchanged
170 }
171 }
172 ct_event!(keycode press Down) => {
173 if self.move_down(1) {
174 TableOutcome::Selected
175 } else {
176 TableOutcome::Unchanged
177 }
178 }
179 ct_event!(keycode press CONTROL-Up) | ct_event!(keycode press CONTROL-Home) => {
180 if self.move_to_row(0) {
181 TableOutcome::Selected
182 } else {
183 TableOutcome::Unchanged
184 }
185 }
186 ct_event!(keycode press CONTROL-Down) | ct_event!(keycode press CONTROL-End) => {
187 if self.move_to_row(self.rows.saturating_sub(1)) {
188 TableOutcome::Selected
189 } else {
190 TableOutcome::Unchanged
191 }
192 }
193
194 ct_event!(keycode press PageUp) => {
195 if self.move_up(max(1, self.page_len().saturating_sub(1))) {
196 TableOutcome::Selected
197 } else {
198 TableOutcome::Unchanged
199 }
200 }
201 ct_event!(keycode press PageDown) => {
202 if self.move_down(max(1, self.page_len().saturating_sub(1))) {
203 TableOutcome::Selected
204 } else {
205 TableOutcome::Unchanged
206 }
207 }
208
209 ct_event!(keycode press Left) => {
210 if self.move_left(1) {
211 TableOutcome::Selected
212 } else {
213 TableOutcome::Unchanged
214 }
215 }
216 ct_event!(keycode press Right) => {
217 if self.move_right(1) {
218 TableOutcome::Selected
219 } else {
220 TableOutcome::Unchanged
221 }
222 }
223 ct_event!(keycode press CONTROL-Left) | ct_event!(keycode press Home) => {
224 if self.move_to_col(0) {
225 TableOutcome::Selected
226 } else {
227 TableOutcome::Unchanged
228 }
229 }
230 ct_event!(keycode press CONTROL-Right) | ct_event!(keycode press End) => {
231 if self.move_to_col(self.columns.saturating_sub(1)) {
232 TableOutcome::Selected
233 } else {
234 TableOutcome::Unchanged
235 }
236 }
237
238 _ => TableOutcome::Continue,
239 }
240 } else {
241 TableOutcome::Continue
242 };
243
244 if res == TableOutcome::Continue {
245 self.handle(event, MouseOnly)
246 } else {
247 res
248 }
249 }
250}
251
252impl HandleEvent<crossterm::event::Event, MouseOnly, TableOutcome> for TableState<CellSelection> {
253 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TableOutcome {
254 let mut r = match event {
255 ct_event!(mouse any for m) if self.mouse.drag(self.table_area, m) => {
256 if self.move_to(self.cell_at_drag((m.column, m.row))) {
257 TableOutcome::Selected
258 } else {
259 TableOutcome::Unchanged
260 }
261 }
262 ct_event!(mouse down Left for column, row) => {
263 if self.area.contains((*column, *row).into()) {
264 if let Some(new_cell) = self.cell_at_clicked((*column, *row)) {
265 if self.move_to(new_cell) {
266 TableOutcome::Selected
267 } else {
268 TableOutcome::Unchanged
269 }
270 } else {
271 TableOutcome::Continue
272 }
273 } else {
274 TableOutcome::Continue
275 }
276 }
277 _ => TableOutcome::Continue,
278 };
279
280 r = r.or_else(|| {
281 let mut sas = ScrollAreaState::new()
282 .area(self.inner)
283 .h_scroll(&mut self.hscroll)
284 .v_scroll(&mut self.vscroll);
285 match sas.handle(event, MouseOnly) {
286 ScrollOutcome::Up(v) => {
287 if self.scroll_up(v) {
288 TableOutcome::Changed
289 } else {
290 TableOutcome::Unchanged
291 }
292 }
293 ScrollOutcome::Down(v) => {
294 if self.scroll_down(v) {
295 TableOutcome::Changed
296 } else {
297 TableOutcome::Unchanged
298 }
299 }
300 ScrollOutcome::VPos(v) => {
301 if self.set_row_offset(self.vscroll.limited_offset(v)) {
302 TableOutcome::Changed
303 } else {
304 TableOutcome::Unchanged
305 }
306 }
307 ScrollOutcome::Left(v) => {
308 if self.scroll_left(v) {
309 TableOutcome::Changed
310 } else {
311 TableOutcome::Unchanged
312 }
313 }
314 ScrollOutcome::Right(v) => {
315 if self.scroll_right(v) {
316 TableOutcome::Changed
317 } else {
318 TableOutcome::Unchanged
319 }
320 }
321 ScrollOutcome::HPos(v) => {
322 if self.set_x_offset(self.hscroll.limited_offset(v)) {
323 TableOutcome::Changed
324 } else {
325 TableOutcome::Unchanged
326 }
327 }
328 ScrollOutcome::Continue => TableOutcome::Continue,
329 ScrollOutcome::Unchanged => TableOutcome::Unchanged,
330 ScrollOutcome::Changed => TableOutcome::Changed,
331 }
332 });
333 r
334 }
335}
336
337pub fn handle_events(
341 state: &mut TableState<CellSelection>,
342 focus: bool,
343 event: &crossterm::event::Event,
344) -> TableOutcome {
345 state.focus.set(focus);
346 state.handle(event, Regular)
347}
348
349pub fn handle_mouse_events(
351 state: &mut TableState<CellSelection>,
352 event: &crossterm::event::Event,
353) -> TableOutcome {
354 state.handle(event, MouseOnly)
355}