1use super::*;
5
6impl DataTableState {
7 pub fn select_next(&mut self) -> bool {
9 self.table_state.select_next();
10 if let Some(selected) = self.table_state.selected()
11 && selected >= self.visible_rows
12 && self.visible_rows > 0
13 {
14 self.table_state.select(Some(self.visible_rows - 1));
17 return self.slide_table(1);
18 }
19 false
20 }
21
22 pub fn page_down(&mut self) -> bool {
24 self.slide_table(self.visible_rows as i64)
25 }
26
27 pub fn select_previous(&mut self) -> bool {
29 if let Some(selected) = self.table_state.selected() {
30 self.table_state.select_previous();
31 if selected == 0 && self.view.start_row > 0 {
32 return self.slide_table(-1);
33 }
34 } else {
35 self.table_state.select(Some(0));
36 }
37 false
38 }
39
40 pub fn scroll_to(&mut self, index: usize) -> bool {
42 if self.view.start_row == index {
43 return false;
44 }
45 self.view.start_row = index;
46 true }
48
49 pub fn scroll_to_row_centered(&mut self, row_index: usize) -> bool {
51 if self.view.num_rows == 0 || self.visible_rows == 0 {
52 return false;
53 }
54 let center_offset = self.visible_rows / 2;
55 let mut start_row = row_index.saturating_sub(center_offset);
56 let max_start = self.view.num_rows.saturating_sub(self.visible_rows);
57 start_row = start_row.min(max_start);
58
59 if self.view.start_row == start_row {
60 let display_idx = row_index
61 .saturating_sub(start_row)
62 .min(self.visible_rows.saturating_sub(1));
63 self.table_state.select(Some(display_idx));
64 return false;
65 }
66
67 self.view.start_row = start_row;
68 let display_idx = row_index
69 .saturating_sub(start_row)
70 .min(self.visible_rows.saturating_sub(1));
71 self.table_state.select(Some(display_idx));
72 true }
74
75 pub fn scroll_to_start(&mut self) -> bool {
77 self.table_state.select(Some(0));
78 self.scroll_to(0)
79 }
80
81 pub fn scroll_to_end(&mut self) -> bool {
83 if self.view.num_rows == 0 {
84 self.view.start_row = 0;
85 self.view.buffered_start_row = 0;
86 self.view.buffered_end_row = 0;
87 return false;
88 }
89 let end_start = self.view.num_rows.saturating_sub(self.visible_rows);
90 if self.view.start_row == end_start {
91 self.select_last_visible_row();
92 return false;
93 }
94 self.view.start_row = end_start;
95 self.select_last_visible_row();
96 true }
98
99 fn select_last_visible_row(&mut self) {
101 if self.view.num_rows == 0 {
102 return;
103 }
104 let last_row_display_idx = (self.view.num_rows - 1).saturating_sub(self.view.start_row);
105 let sel = last_row_display_idx.min(self.visible_rows.saturating_sub(1));
106 self.table_state.select(Some(sel));
107 }
108
109 pub fn half_page_down(&mut self) -> bool {
111 let half = (self.visible_rows / 2).max(1) as i64;
112 self.slide_table(half)
113 }
114
115 pub fn half_page_up(&mut self) -> bool {
117 if self.view.start_row == 0 {
118 return false;
119 }
120 let half = (self.visible_rows / 2).max(1) as i64;
121 self.slide_table(-half)
122 }
123
124 pub fn page_up(&mut self) -> bool {
126 if self.view.start_row == 0 {
127 return false;
128 }
129 self.slide_table(-(self.visible_rows as i64))
130 }
131
132 #[cfg(test)]
133 pub(crate) fn scroll_right(&mut self) {
134 self.scroll_columns(ColumnMove::StepRight);
135 }
136
137 #[cfg(test)]
138 pub(crate) fn scroll_left(&mut self) {
139 self.scroll_columns(ColumnMove::StepLeft);
140 }
141
142 pub fn columns_on_screen(&self) -> Option<OnScreen> {
145 self.on_screen
146 }
147
148 fn scroll_count(&self) -> usize {
150 self.view
151 .column_order
152 .len()
153 .saturating_sub(self.frozen_shown())
154 }
155
156 pub(super) fn scroll_columns(&mut self, mv: ColumnMove) {
161 if matches!(
162 mv,
163 ColumnMove::First | ColumnMove::Last | ColumnMove::Reveal(_)
164 ) {
165 self.column_moves.clear();
167 }
168 if self.column_moves.is_empty()
169 && let Some(start) = self.plan_known(mv)
170 {
171 self.apply_column_move(mv, start);
172 } else {
173 self.wait(WaitingMove::View(mv));
174 }
175 }
176
177 pub fn move_cursor(&mut self, mv: CursorMove) {
181 if matches!(mv, CursorMove::First | CursorMove::Last) {
182 self.column_moves.clear();
183 }
184 if !self.column_moves.is_empty()
185 || !self.land_cursor_move(mv, &mut |state: &mut Self, view| state.plan_known(view))
186 {
187 self.wait(WaitingMove::Cursor(mv));
188 }
189 }
190
191 pub fn go_to_column(&mut self, name: &str) {
195 let Some(at) = self.view.column_order.iter().position(|c| c == name) else {
196 return;
197 };
198 self.column_moves.clear();
199 self.place_cursor_at(at);
200 if let Some(index) = at.checked_sub(self.frozen_shown()) {
201 self.scroll_columns(ColumnMove::Reveal(index));
202 }
203 }
204
205 pub fn set_current_column(&mut self, name: &str) {
208 let Some(at) = self.view.column_order.iter().position(|c| c == name) else {
209 return;
210 };
211 self.column_moves.clear();
212 self.place_cursor_at(at);
213 self.follow_cursor(&mut |state: &mut Self, view| state.plan_known(view));
214 }
215
216 pub fn current_column(&self) -> Option<&str> {
220 self.cursor_index()
221 .map(|at| self.view.column_order[at].as_str())
222 }
223
224 pub fn current_column_index(&self) -> Option<usize> {
226 self.cursor_index()
227 }
228
229 pub(crate) fn cursor_index(&self) -> Option<usize> {
230 let last = self.view.column_order.len().checked_sub(1)?;
231 Some(
232 self.view
233 .cursor_column
234 .as_deref()
235 .and_then(|name| self.view.column_order.iter().position(|c| c == name))
236 .unwrap_or(self.view.cursor_at.min(last)),
237 )
238 }
239
240 pub(super) fn place_cursor_at(&mut self, at: usize) {
241 self.view.cursor_column = self.view.column_order.get(at).cloned();
242 self.view.cursor_at = at;
243 }
244
245 pub(super) fn settle_cursor(&mut self) {
248 let at = self.cursor_index().unwrap_or(0);
249 self.place_cursor_at(at);
250 self.reveal_cursor = true;
251 }
252
253 fn wait(&mut self, mv: WaitingMove) {
255 if self.column_moves.len() < MAX_WAITING_MOVES {
256 self.column_moves.push(mv);
257 }
258 }
259
260 fn follow_cursor(&mut self, plan: &mut impl FnMut(&mut Self, ColumnMove) -> Option<usize>) {
263 let Some(index) = self
264 .cursor_index()
265 .and_then(|at| at.checked_sub(self.frozen_shown()))
266 else {
267 return;
268 };
269 let view = ColumnMove::Keep(index);
270 match plan(self, view) {
271 Some(start) if start == self.termcol_index => {}
273 Some(start) => self.apply_column_move(view, start),
274 None => self.wait(WaitingMove::View(view)),
275 }
276 }
277
278 fn land_cursor_move(
281 &mut self,
282 mv: CursorMove,
283 plan: &mut impl FnMut(&mut Self, ColumnMove) -> Option<usize>,
284 ) -> bool {
285 let Some(cursor) = self.cursor_index() else {
286 return true;
287 };
288 let last = self.view.column_order.len() - 1;
289 let frozen = self.frozen_shown();
290 match mv {
291 CursorMove::Left | CursorMove::Right => {
292 let at = if mv == CursorMove::Left {
293 cursor.saturating_sub(1)
294 } else {
295 (cursor + 1).min(last)
296 };
297 self.place_cursor_at(at);
298 self.follow_cursor(plan);
299 }
300 CursorMove::First | CursorMove::Last => {
301 let (at, view) = if mv == CursorMove::First {
302 (0, ColumnMove::First)
303 } else {
304 (last, ColumnMove::Last)
305 };
306 self.place_cursor_at(at);
307 match plan(self, view) {
308 Some(start) => self.apply_column_move(view, start),
309 None => self.wait(WaitingMove::View(view)),
310 }
311 }
312 CursorMove::PageLeft | CursorMove::PageRight => {
313 let view = if mv == CursorMove::PageLeft {
314 ColumnMove::PageLeft
315 } else {
316 ColumnMove::PageRight
317 };
318 let Some(start) = plan(self, view) else {
319 return false;
320 };
321 let from = self.termcol_index;
322 self.apply_column_move(view, start);
323 let at = if self.termcol_index != from {
324 frozen + self.termcol_index
326 } else if mv == CursorMove::PageRight {
327 last
329 } else if cursor > frozen {
330 frozen
332 } else {
333 0
334 };
335 self.place_cursor_at(at.min(last));
336 }
337 }
338 true
339 }
340
341 pub(super) fn scrolling_names(&self) -> &[String] {
343 &self.view.column_order[self.frozen_shown().min(self.view.column_order.len())..]
344 }
345
346 fn retrace(&self, mv: ColumnMove) -> Option<usize> {
349 let &(back, to) = self.page_trail.last()?;
350 (mv == ColumnMove::PageLeft && to == self.termcol_index).then_some(back)
351 }
352
353 fn plan_known(&self, mv: ColumnMove) -> Option<usize> {
356 if let Some(back) = self.retrace(mv) {
357 return Some(back);
358 }
359 let needs_widths = match mv {
360 #[cfg(test)]
361 ColumnMove::StepLeft | ColumnMove::StepRight => false,
362 ColumnMove::First => false,
363 ColumnMove::Keep(column) => column > self.termcol_index,
365 _ => true,
366 };
367 let room = match self.scroll_room {
368 Some(room) => room,
369 None if needs_widths => return None,
370 None => Room::default(),
371 };
372 let names = self.scrolling_names();
373 crate::widgets::column_paging::plan(mv, self.termcol_index, names.len(), room, |i| {
374 self.drawn_width(&names[i])
375 })
376 }
377
378 fn apply_column_move(&mut self, mv: ColumnMove, start: usize) {
381 let from = self.termcol_index;
382 let start = start.min(self.scroll_count().saturating_sub(1));
383 match mv {
384 ColumnMove::PageRight => {
385 if start > from {
386 self.page_trail.push((from, start));
387 }
388 }
389 ColumnMove::PageLeft if self.retrace(mv) == Some(start) => {
390 self.page_trail.pop();
391 }
392 _ => self.page_trail.clear(),
393 }
394 self.scroll_columns_to(start);
395 let frozen = self.frozen_shown();
396 if let Some(cursor) = self.cursor_index()
397 && cursor >= frozen
398 && cursor < frozen + self.termcol_index
399 {
400 self.place_cursor_at(frozen + self.termcol_index);
401 }
402 }
403
404 pub(super) fn clear_column_moves(&mut self) {
407 self.column_moves.clear();
408 self.page_trail.clear();
409 }
410
411 fn scroll_columns_to(&mut self, start: usize) {
413 let start = start.min(self.scroll_count().saturating_sub(1));
414 if start != self.termcol_index {
415 self.termcol_index = start;
416 self.rescroll_columns();
417 }
418 }
419
420 pub(crate) fn land_column_moves(
425 &mut self,
426 room: Room,
427 mut width: impl FnMut(&mut Self, &str) -> u16,
428 ) {
429 if self.scroll_room != Some(room) {
430 self.reveal_cursor = true;
432 }
433 self.scroll_room = Some(room);
434 if (self.column_moves.is_empty() && !self.reveal_cursor)
435 || !self.buffer_on_hand()
436 || self.defer_collect
437 {
438 return;
439 }
440 let mut plan = |state: &mut Self, mv: ColumnMove| -> Option<usize> {
441 if let Some(back) = state.retrace(mv) {
442 return Some(back);
443 }
444 let from = state.termcol_index;
445 let count = state.scroll_count();
446 Some(
447 crate::widgets::column_paging::plan(mv, from, count, room, |i| {
448 let name = state.scrolling_names()[i].clone();
449 Some(width(state, &name))
450 })
451 .unwrap_or(from),
452 )
453 };
454 for mv in std::mem::take(&mut self.column_moves) {
455 match mv {
456 WaitingMove::View(mv) => {
457 let start = plan(self, mv).unwrap_or(self.termcol_index);
458 self.apply_column_move(mv, start);
459 }
460 WaitingMove::Cursor(mv) => {
461 self.land_cursor_move(mv, &mut plan);
462 }
463 }
464 }
465 if std::mem::take(&mut self.reveal_cursor) {
466 self.follow_cursor(&mut plan);
467 }
468 }
469
470 fn rescroll_columns(&mut self) {
478 if self.defer_collect || !self.buffer_on_hand() {
479 return;
480 }
481 self.slice_buffer_into_display();
482 if self.table_state.selected().is_none() {
483 self.table_state.select(Some(0));
484 }
485 }
486
487 pub fn headers(&self) -> Vec<String> {
488 self.view.column_order.clone()
489 }
490
491 pub fn set_column_order(&mut self, order: Vec<String>) {
492 self.view.column_order = order;
493 self.clear_column_moves();
494 self.termcol_index = self
496 .termcol_index
497 .min(self.scroll_count().saturating_sub(1));
498 self.drop_buffer();
499 self.settle_cursor();
500 self.collect();
501 }
502
503 pub fn set_locked_columns(&mut self, count: usize) {
504 self.view.locked_columns_count = count.min(self.view.column_order.len());
505 self.clear_column_moves();
506 self.settle_cursor();
507 self.termcol_index = self
508 .termcol_index
509 .min(self.scroll_count().saturating_sub(1));
510 self.drop_buffer();
511 self.collect();
512 }
513
514 pub fn locked_columns_count(&self) -> usize {
515 self.view.locked_columns_count
516 }
517
518 pub fn frozen_shown(&self) -> usize {
522 let (asked, shown) = self.view.frozen_fit;
523 if asked == self.view.locked_columns_count {
524 shown.min(asked)
525 } else {
526 self.view.locked_columns_count
527 }
528 }
529
530 pub(crate) fn fit_frozen(&mut self, shown: usize) {
535 let before = self.frozen_shown();
536 let shown = shown.min(self.view.locked_columns_count);
537 if shown == before {
538 self.view.frozen_fit = (self.view.locked_columns_count, shown);
539 return;
540 }
541 if self.defer_collect || !self.buffer_on_hand() {
542 return;
543 }
544 self.view.frozen_fit = (self.view.locked_columns_count, shown);
545 self.page_trail.clear();
547 if self.termcol_index > 0 {
548 let first = before + self.termcol_index;
549 let last = self.view.column_order.len().saturating_sub(1);
550 self.termcol_index = first.min(last).saturating_sub(shown);
551 }
552 self.slice_buffer_into_display();
553 }
554
555 pub(crate) fn width_dtype(&self, name: &str) -> DataType {
558 self.view
559 .schema
560 .get(name)
561 .cloned()
562 .unwrap_or(DataType::Null)
563 }
564
565 pub fn width_choice(&self, name: &str) -> WidthChoice {
567 self.widths.choice(name, &self.width_dtype(name))
568 }
569
570 pub fn shown_width(&self, name: &str) -> Option<u16> {
572 self.widths.shown(name, &self.width_dtype(name))
573 }
574
575 pub fn on_screen_width(&self, name: &str) -> Option<u16> {
578 self.widths.on_screen(name, &self.width_dtype(name))
579 }
580
581 pub(crate) fn drawn_width(&self, name: &str) -> Option<u16> {
584 self.widths.drawn(name, &self.width_dtype(name))
585 }
586
587 pub fn set_width_choices(&mut self, choices: impl IntoIterator<Item = (String, WidthChoice)>) {
590 for (name, choice) in choices {
591 let dtype = self.width_dtype(&name);
592 self.widths.set_choice(&name, &dtype, choice);
593 }
594 }
595
596 pub(crate) fn page_column(&self, name: &str, offset: usize, len: usize) -> Option<DataFrame> {
599 let column = self.view.buffered_df.as_ref()?.select([name]).ok()?;
600 visible_slice(&column, offset, len)
601 }
602}