rat_ftable/
cellselection.rs

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/// Select a single cell in the table.
10///
11/// This one supports cell + column + row selection.
12#[derive(Debug, Default, Clone)]
13pub struct CellSelection {
14    /// Selected cell.
15    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    /// New
46    pub fn new() -> CellSelection {
47        Self::default()
48    }
49
50    /// Clear the selection.
51    #[inline]
52    pub fn clear(&mut self) {
53        self.lead_cell = None;
54    }
55
56    /// Selected cell.
57    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    /// Select a cell.
67    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    /// Select a row. Column stays the same.
74    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    /// Select a column, row stays the same.
84    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    /// Select a cell, clamp between 0 and maximum.
94    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    /// Select a column. Row stays the same.
101    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    /// Select a row. Column stays the same.
111    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    /// Select the next row, clamp between 0 and maximum.
121    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    /// Select the previous row, clamp between 0 and maximum.
131    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    /// Select the next column, clamp between 0 and maximum.
141    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    /// Select the previous row, clamp between 0 and maximum.
151    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
337/// Handle all events.
338/// Table events are only processed if focus is true.
339/// Mouse events are processed if they are in range.
340pub 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
349/// Handle only mouse-events.
350pub fn handle_mouse_events(
351    state: &mut TableState<CellSelection>,
352    event: &crossterm::event::Event,
353) -> TableOutcome {
354    state.handle(event, MouseOnly)
355}