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