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 if !self.has_mouse_focus() {
292 return TableOutcome::Continue;
293 }
294
295 let mut r = match event {
296 ct_event!(mouse any for m) if self.mouse.drag(self.table_area, m) => {
297 if self.move_to(self.cell_at_drag((m.column, m.row))) {
298 TableOutcome::Selected
299 } else {
300 TableOutcome::Unchanged
301 }
302 }
303 ct_event!(mouse down Left for column, row) => {
304 if self.area.contains((*column, *row).into()) {
305 if let Some(new_cell) = self.cell_at_clicked((*column, *row)) {
306 if self.move_to(new_cell) {
307 TableOutcome::Selected
308 } else {
309 TableOutcome::Unchanged
310 }
311 } else {
312 TableOutcome::Continue
313 }
314 } else {
315 TableOutcome::Continue
316 }
317 }
318 _ => TableOutcome::Continue,
319 };
320
321 r = r.or_else(|| {
322 let mut sas = ScrollAreaState::new()
323 .area(self.inner)
324 .h_scroll(&mut self.hscroll)
325 .v_scroll(&mut self.vscroll);
326 match sas.handle(event, MouseOnly) {
327 ScrollOutcome::Up(v) => {
328 if self.scroll_up(v) {
329 TableOutcome::Changed
330 } else {
331 TableOutcome::Unchanged
332 }
333 }
334 ScrollOutcome::Down(v) => {
335 if self.scroll_down(v) {
336 TableOutcome::Changed
337 } else {
338 TableOutcome::Unchanged
339 }
340 }
341 ScrollOutcome::VPos(v) => {
342 if self.set_row_offset(self.vscroll.limited_offset(v)) {
343 TableOutcome::Changed
344 } else {
345 TableOutcome::Unchanged
346 }
347 }
348 ScrollOutcome::Left(v) => {
349 if self.scroll_left(v) {
350 TableOutcome::Changed
351 } else {
352 TableOutcome::Unchanged
353 }
354 }
355 ScrollOutcome::Right(v) => {
356 if self.scroll_right(v) {
357 TableOutcome::Changed
358 } else {
359 TableOutcome::Unchanged
360 }
361 }
362 ScrollOutcome::HPos(v) => {
363 if self.set_x_offset(self.hscroll.limited_offset(v)) {
364 TableOutcome::Changed
365 } else {
366 TableOutcome::Unchanged
367 }
368 }
369 ScrollOutcome::Continue => TableOutcome::Continue,
370 ScrollOutcome::Unchanged => TableOutcome::Unchanged,
371 ScrollOutcome::Changed => TableOutcome::Changed,
372 }
373 });
374 r
375 }
376}
377
378pub fn handle_events(
382 state: &mut TableState<CellSelection>,
383 focus: bool,
384 event: &Event,
385) -> TableOutcome {
386 state.focus.set(focus);
387 state.handle(event, Regular)
388}
389
390pub fn handle_mouse_events(state: &mut TableState<CellSelection>, event: &Event) -> TableOutcome {
392 state.handle(event, MouseOnly)
393}