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 return self.slide_table(1);
15 }
16 false
17 }
18
19 pub fn page_down(&mut self) -> bool {
21 self.slide_table(self.visible_rows as i64)
22 }
23
24 pub fn select_previous(&mut self) -> bool {
26 if let Some(selected) = self.table_state.selected() {
27 self.table_state.select_previous();
28 if selected == 0 && self.view.start_row > 0 {
29 return self.slide_table(-1);
30 }
31 } else {
32 self.table_state.select(Some(0));
33 }
34 false
35 }
36
37 pub fn scroll_to(&mut self, index: usize) -> bool {
39 if self.view.start_row == index {
40 return false;
41 }
42 self.view.start_row = index;
43 true }
45
46 pub fn scroll_to_row_centered(&mut self, row_index: usize) -> bool {
48 if self.view.num_rows == 0 || self.visible_rows == 0 {
49 return false;
50 }
51 let center_offset = self.visible_rows / 2;
52 let mut start_row = row_index.saturating_sub(center_offset);
53 let max_start = self.view.num_rows.saturating_sub(self.visible_rows);
54 start_row = start_row.min(max_start);
55
56 if self.view.start_row == start_row {
57 let display_idx = row_index
58 .saturating_sub(start_row)
59 .min(self.visible_rows.saturating_sub(1));
60 self.table_state.select(Some(display_idx));
61 return false;
62 }
63
64 self.view.start_row = start_row;
65 let display_idx = row_index
66 .saturating_sub(start_row)
67 .min(self.visible_rows.saturating_sub(1));
68 self.table_state.select(Some(display_idx));
69 true }
71
72 pub fn scroll_to_start(&mut self) -> bool {
74 self.table_state.select(Some(0));
75 self.scroll_to(0)
76 }
77
78 pub fn scroll_to_end(&mut self) -> bool {
80 if self.view.num_rows == 0 {
81 self.view.start_row = 0;
82 self.view.buffered_start_row = 0;
83 self.view.buffered_end_row = 0;
84 return false;
85 }
86 let end_start = self.view.num_rows.saturating_sub(self.visible_rows);
87 if self.view.start_row == end_start {
88 self.select_last_visible_row();
89 return false;
90 }
91 self.view.start_row = end_start;
92 self.select_last_visible_row();
93 true }
95
96 fn select_last_visible_row(&mut self) {
98 if self.view.num_rows == 0 {
99 return;
100 }
101 let last_row_display_idx = (self.view.num_rows - 1).saturating_sub(self.view.start_row);
102 let sel = last_row_display_idx.min(self.visible_rows.saturating_sub(1));
103 self.table_state.select(Some(sel));
104 }
105
106 pub fn half_page_down(&mut self) -> bool {
108 let half = (self.visible_rows / 2).max(1) as i64;
109 self.slide_table(half)
110 }
111
112 pub fn half_page_up(&mut self) -> bool {
114 if self.view.start_row == 0 {
115 return false;
116 }
117 let half = (self.visible_rows / 2).max(1) as i64;
118 self.slide_table(-half)
119 }
120
121 pub fn page_up(&mut self) -> bool {
123 if self.view.start_row == 0 {
124 return false;
125 }
126 self.slide_table(-(self.visible_rows as i64))
127 }
128
129 #[cfg(test)]
130 pub(crate) fn scroll_right(&mut self) {
131 self.scroll_columns(ColumnMove::StepRight);
132 }
133
134 #[cfg(test)]
135 pub(crate) fn scroll_left(&mut self) {
136 self.scroll_columns(ColumnMove::StepLeft);
137 }
138
139 pub fn columns_on_screen(&self) -> Option<OnScreen> {
142 self.on_screen
143 }
144
145 fn scroll_count(&self) -> usize {
147 self.view
148 .column_order
149 .len()
150 .saturating_sub(self.frozen_shown())
151 }
152
153 pub(super) fn scroll_columns(&mut self, mv: ColumnMove) {
158 if matches!(
159 mv,
160 ColumnMove::First | ColumnMove::Last | ColumnMove::Reveal(_)
161 ) {
162 self.column_moves.clear();
164 }
165 if self.column_moves.is_empty()
166 && let Some(start) = self.plan_known(mv)
167 {
168 self.apply_column_move(mv, start);
169 } else {
170 self.wait(WaitingMove::View(mv));
171 }
172 }
173
174 pub fn move_cursor(&mut self, mv: CursorMove) {
178 if matches!(mv, CursorMove::First | CursorMove::Last) {
179 self.column_moves.clear();
180 }
181 if !self.column_moves.is_empty()
182 || !self.land_cursor_move(mv, &mut |state: &mut Self, view| state.plan_known(view))
183 {
184 self.wait(WaitingMove::Cursor(mv));
185 }
186 }
187
188 pub fn go_to_column(&mut self, name: &str) {
192 let Some(at) = self.view.column_order.iter().position(|c| c == name) else {
193 return;
194 };
195 self.column_moves.clear();
196 self.place_cursor_at(at);
197 if let Some(index) = at.checked_sub(self.frozen_shown()) {
198 self.scroll_columns(ColumnMove::Reveal(index));
199 }
200 }
201
202 pub fn set_current_column(&mut self, name: &str) {
205 let Some(at) = self.view.column_order.iter().position(|c| c == name) else {
206 return;
207 };
208 self.column_moves.clear();
209 self.place_cursor_at(at);
210 self.follow_cursor(&mut |state: &mut Self, view| state.plan_known(view));
211 }
212
213 pub fn current_column(&self) -> Option<&str> {
217 self.cursor_index()
218 .map(|at| self.view.column_order[at].as_str())
219 }
220
221 pub fn current_column_index(&self) -> Option<usize> {
223 self.cursor_index()
224 }
225
226 pub(crate) fn cursor_index(&self) -> Option<usize> {
227 let last = self.view.column_order.len().checked_sub(1)?;
228 Some(
229 self.view
230 .cursor_column
231 .as_deref()
232 .and_then(|name| self.view.column_order.iter().position(|c| c == name))
233 .unwrap_or(self.view.cursor_at.min(last)),
234 )
235 }
236
237 pub(super) fn place_cursor_at(&mut self, at: usize) {
238 self.view.cursor_column = self.view.column_order.get(at).cloned();
239 self.view.cursor_at = at;
240 }
241
242 pub(super) fn settle_cursor(&mut self) {
245 let at = self.cursor_index().unwrap_or(0);
246 self.place_cursor_at(at);
247 self.reveal_cursor = true;
248 }
249
250 fn wait(&mut self, mv: WaitingMove) {
252 if self.column_moves.len() < MAX_WAITING_MOVES {
253 self.column_moves.push(mv);
254 }
255 }
256
257 fn follow_cursor(&mut self, plan: &mut impl FnMut(&mut Self, ColumnMove) -> Option<usize>) {
260 let Some(index) = self
261 .cursor_index()
262 .and_then(|at| at.checked_sub(self.frozen_shown()))
263 else {
264 return;
265 };
266 let view = ColumnMove::Keep(index);
267 match plan(self, view) {
268 Some(start) if start == self.termcol_index => {}
270 Some(start) => self.apply_column_move(view, start),
271 None => self.wait(WaitingMove::View(view)),
272 }
273 }
274
275 fn land_cursor_move(
278 &mut self,
279 mv: CursorMove,
280 plan: &mut impl FnMut(&mut Self, ColumnMove) -> Option<usize>,
281 ) -> bool {
282 let Some(cursor) = self.cursor_index() else {
283 return true;
284 };
285 let last = self.view.column_order.len() - 1;
286 let frozen = self.frozen_shown();
287 match mv {
288 CursorMove::Left | CursorMove::Right => {
289 let at = if mv == CursorMove::Left {
290 cursor.saturating_sub(1)
291 } else {
292 (cursor + 1).min(last)
293 };
294 self.place_cursor_at(at);
295 self.follow_cursor(plan);
296 }
297 CursorMove::First | CursorMove::Last => {
298 let (at, view) = if mv == CursorMove::First {
299 (0, ColumnMove::First)
300 } else {
301 (last, ColumnMove::Last)
302 };
303 self.place_cursor_at(at);
304 match plan(self, view) {
305 Some(start) => self.apply_column_move(view, start),
306 None => self.wait(WaitingMove::View(view)),
307 }
308 }
309 CursorMove::PageLeft | CursorMove::PageRight => {
310 let view = if mv == CursorMove::PageLeft {
311 ColumnMove::PageLeft
312 } else {
313 ColumnMove::PageRight
314 };
315 let Some(start) = plan(self, view) else {
316 return false;
317 };
318 let from = self.termcol_index;
319 self.apply_column_move(view, start);
320 let at = if self.termcol_index != from {
321 frozen + self.termcol_index
323 } else if mv == CursorMove::PageRight {
324 last
326 } else if cursor > frozen {
327 frozen
329 } else {
330 0
331 };
332 self.place_cursor_at(at.min(last));
333 }
334 }
335 true
336 }
337
338 pub(super) fn scrolling_names(&self) -> &[String] {
340 &self.view.column_order[self.frozen_shown().min(self.view.column_order.len())..]
341 }
342
343 fn retrace(&self, mv: ColumnMove) -> Option<usize> {
346 let &(back, to) = self.page_trail.last()?;
347 (mv == ColumnMove::PageLeft && to == self.termcol_index).then_some(back)
348 }
349
350 fn plan_known(&self, mv: ColumnMove) -> Option<usize> {
353 if let Some(back) = self.retrace(mv) {
354 return Some(back);
355 }
356 let needs_widths = match mv {
357 #[cfg(test)]
358 ColumnMove::StepLeft | ColumnMove::StepRight => false,
359 ColumnMove::First => false,
360 ColumnMove::Keep(column) => column > self.termcol_index,
362 _ => true,
363 };
364 let room = match self.scroll_room {
365 Some(room) => room,
366 None if needs_widths => return None,
367 None => Room::default(),
368 };
369 let names = self.scrolling_names();
370 crate::widgets::column_paging::plan(mv, self.termcol_index, names.len(), room, |i| {
371 self.drawn_width(&names[i])
372 })
373 }
374
375 fn apply_column_move(&mut self, mv: ColumnMove, start: usize) {
378 let from = self.termcol_index;
379 let start = start.min(self.scroll_count().saturating_sub(1));
380 match mv {
381 ColumnMove::PageRight => {
382 if start > from {
383 self.page_trail.push((from, start));
384 }
385 }
386 ColumnMove::PageLeft if self.retrace(mv) == Some(start) => {
387 self.page_trail.pop();
388 }
389 _ => self.page_trail.clear(),
390 }
391 self.scroll_columns_to(start);
392 let frozen = self.frozen_shown();
393 if let Some(cursor) = self.cursor_index()
394 && cursor >= frozen
395 && cursor < frozen + self.termcol_index
396 {
397 self.place_cursor_at(frozen + self.termcol_index);
398 }
399 }
400
401 pub(super) fn clear_column_moves(&mut self) {
404 self.column_moves.clear();
405 self.page_trail.clear();
406 }
407
408 fn scroll_columns_to(&mut self, start: usize) {
410 let start = start.min(self.scroll_count().saturating_sub(1));
411 if start != self.termcol_index {
412 self.termcol_index = start;
413 self.rescroll_columns();
414 }
415 }
416
417 pub(crate) fn land_column_moves(
422 &mut self,
423 room: Room,
424 mut width: impl FnMut(&mut Self, &str) -> u16,
425 ) {
426 if self.scroll_room != Some(room) {
427 self.reveal_cursor = true;
429 }
430 self.scroll_room = Some(room);
431 if (self.column_moves.is_empty() && !self.reveal_cursor)
432 || !self.buffer_on_hand()
433 || self.defer_collect
434 {
435 return;
436 }
437 let mut plan = |state: &mut Self, mv: ColumnMove| -> Option<usize> {
438 if let Some(back) = state.retrace(mv) {
439 return Some(back);
440 }
441 let from = state.termcol_index;
442 let count = state.scroll_count();
443 Some(
444 crate::widgets::column_paging::plan(mv, from, count, room, |i| {
445 let name = state.scrolling_names()[i].clone();
446 Some(width(state, &name))
447 })
448 .unwrap_or(from),
449 )
450 };
451 for mv in std::mem::take(&mut self.column_moves) {
452 match mv {
453 WaitingMove::View(mv) => {
454 let start = plan(self, mv).unwrap_or(self.termcol_index);
455 self.apply_column_move(mv, start);
456 }
457 WaitingMove::Cursor(mv) => {
458 self.land_cursor_move(mv, &mut plan);
459 }
460 }
461 }
462 if std::mem::take(&mut self.reveal_cursor) {
463 self.follow_cursor(&mut plan);
464 }
465 }
466
467 fn rescroll_columns(&mut self) {
475 if self.defer_collect || !self.buffer_on_hand() {
476 return;
477 }
478 self.slice_buffer_into_display();
479 if self.table_state.selected().is_none() {
480 self.table_state.select(Some(0));
481 }
482 }
483
484 pub fn headers(&self) -> Vec<String> {
485 self.view.column_order.clone()
486 }
487
488 pub fn set_column_order(&mut self, order: Vec<String>) {
489 self.view.column_order = order;
490 self.clear_column_moves();
491 self.termcol_index = self
493 .termcol_index
494 .min(self.scroll_count().saturating_sub(1));
495 self.drop_buffer();
496 self.settle_cursor();
497 self.collect();
498 }
499
500 pub fn set_locked_columns(&mut self, count: usize) {
501 self.view.locked_columns_count = count.min(self.view.column_order.len());
502 self.clear_column_moves();
503 self.settle_cursor();
504 self.termcol_index = self
505 .termcol_index
506 .min(self.scroll_count().saturating_sub(1));
507 self.drop_buffer();
508 self.collect();
509 }
510
511 pub fn locked_columns_count(&self) -> usize {
512 self.view.locked_columns_count
513 }
514
515 pub fn frozen_shown(&self) -> usize {
519 let (asked, shown) = self.view.frozen_fit;
520 if asked == self.view.locked_columns_count {
521 shown.min(asked)
522 } else {
523 self.view.locked_columns_count
524 }
525 }
526
527 pub(crate) fn fit_frozen(&mut self, shown: usize) {
532 let before = self.frozen_shown();
533 let shown = shown.min(self.view.locked_columns_count);
534 if shown == before {
535 self.view.frozen_fit = (self.view.locked_columns_count, shown);
536 return;
537 }
538 if self.defer_collect || !self.buffer_on_hand() {
539 return;
540 }
541 self.view.frozen_fit = (self.view.locked_columns_count, shown);
542 self.page_trail.clear();
544 if self.termcol_index > 0 {
545 let first = before + self.termcol_index;
546 let last = self.view.column_order.len().saturating_sub(1);
547 self.termcol_index = first.min(last).saturating_sub(shown);
548 }
549 self.slice_buffer_into_display();
550 }
551
552 pub(crate) fn width_dtype(&self, name: &str) -> DataType {
555 self.view
556 .schema
557 .get(name)
558 .cloned()
559 .unwrap_or(DataType::Null)
560 }
561
562 pub fn width_choice(&self, name: &str) -> WidthChoice {
564 self.widths.choice(name, &self.width_dtype(name))
565 }
566
567 pub fn shown_width(&self, name: &str) -> Option<u16> {
569 self.widths.shown(name, &self.width_dtype(name))
570 }
571
572 pub fn on_screen_width(&self, name: &str) -> Option<u16> {
575 self.widths.on_screen(name, &self.width_dtype(name))
576 }
577
578 pub(crate) fn drawn_width(&self, name: &str) -> Option<u16> {
581 self.widths.drawn(name, &self.width_dtype(name))
582 }
583
584 pub fn set_width_choices(&mut self, choices: impl IntoIterator<Item = (String, WidthChoice)>) {
587 for (name, choice) in choices {
588 let dtype = self.width_dtype(&name);
589 self.widths.set_choice(&name, &dtype, choice);
590 }
591 }
592
593 pub(crate) fn page_column(&self, name: &str, offset: usize, len: usize) -> Option<DataFrame> {
596 let column = self.view.buffered_df.as_ref()?.select([name]).ok()?;
597 visible_slice(&column, offset, len)
598 }
599}