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