1#![deny(
3 missing_docs,
4 missing_copy_implementations,
5 trivial_casts,
6 trivial_numeric_casts,
7 unsafe_code,
8 unused_import_braces,
9 unused_qualifications
10)]
11
12use cursive_core as cursive;
14
15use std::cmp::{self, Ordering};
17use std::collections::HashMap;
18use std::hash::Hash;
19use std::sync::Arc;
20
21use cursive::{
23 align::HAlign,
24 direction::Direction,
25 event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent},
26 theme,
27 vec::Vec2,
28 view::{scroll, CannotFocus, View},
29 Cursive, Printer, Rect, With,
30};
31
32pub trait TableViewItem<H>: Clone + Sized
35where
36 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
37{
38 fn to_column(&self, column: H) -> String;
41
42 fn cmp(&self, other: &Self, column: H) -> Ordering
44 where
45 Self: Sized;
46}
47
48#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50pub enum SelectMode {
51 Row,
53 Cell,
55}
56
57pub trait ArrayViewItem<H>: Clone + Sized
60where
61 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
62{
63 fn to_column(&self, column: H) -> String;
66
67 fn to_row(&self) -> String;
70
71 fn columns() -> Vec<(H, String)> {
76 Vec::new()
77 }
78
79 fn to_array_view(items: Vec<Self>) -> ArrayView<Self, H>
83 where
84 Self: Sized,
85 {
86 let mut array = ArrayView::<Self, H>::new();
87 for (column, title) in Self::columns() {
88 array.add_column(column, title, |c| c);
89 }
90 array.set_items(items);
91 array
92 }
93}
94
95#[derive(Clone)]
99pub struct ArrayDataRow<T> {
100 row_index: usize,
101 row_headers: Arc<Vec<String>>,
102 cells: Vec<T>,
103}
104
105impl<T> ArrayDataRow<T> {
106 fn new(row_index: usize, row_headers: Arc<Vec<String>>, cells: Vec<T>) -> Self {
107 Self {
108 row_index,
109 row_headers,
110 cells,
111 }
112 }
113}
114
115impl<T> ArrayViewItem<usize> for ArrayDataRow<T>
116where
117 T: ToString + Clone,
118{
119 fn to_column(&self, column: usize) -> String {
120 self.cells
121 .get(column)
122 .map(|value| value.to_string())
123 .unwrap_or_default()
124 }
125
126 fn to_row(&self) -> String {
127 self.row_headers
128 .get(self.row_index)
129 .cloned()
130 .unwrap_or_else(|| format!("R{}", self.row_index + 1))
131 }
132}
133
134pub struct ArrayBuilder<T> {
136 row_headers: Vec<String>,
137 column_headers: Vec<String>,
138 array_name: Option<String>,
139 data: Vec<Vec<T>>,
140 row_header_alignment: HAlign,
141 column_header_alignment: HAlign,
142 data_alignment: HAlign,
143}
144
145impl<T> Default for ArrayBuilder<T> {
146 fn default() -> Self {
147 Self {
148 row_headers: Vec::new(),
149 column_headers: Vec::new(),
150 array_name: None,
151 data: Vec::new(),
152 row_header_alignment: HAlign::Center,
153 column_header_alignment: HAlign::Center,
154 data_alignment: HAlign::Center,
155 }
156 }
157}
158
159impl<T> ArrayBuilder<T>
160where
161 T: ToString + Clone,
162{
163 pub fn new() -> Self {
165 Self::default()
166 }
167
168 pub fn row_header<S: Into<String>>(mut self, rows: Vec<S>) -> Self {
170 self.row_headers = rows.into_iter().map(Into::into).collect();
171 self
172 }
173
174 pub fn row_headers<S: Into<String>>(self, rows: Vec<S>) -> Self {
176 self.row_header(rows)
177 }
178
179 pub fn column_header<S: Into<String>>(mut self, columns: Vec<S>) -> Self {
181 self.column_headers = columns.into_iter().map(Into::into).collect();
182 self
183 }
184
185 pub fn column_headers<S: Into<String>>(self, columns: Vec<S>) -> Self {
187 self.column_header(columns)
188 }
189
190 pub fn row_header_orientation(mut self, alignment: HAlign) -> Self {
192 self.row_header_alignment = alignment;
193 self
194 }
195
196 pub fn column_header_orientation(mut self, alignment: HAlign) -> Self {
198 self.column_header_alignment = alignment;
199 self
200 }
201
202 pub fn data_orientation(mut self, alignment: HAlign) -> Self {
204 self.data_alignment = alignment;
205 self
206 }
207
208 pub fn array_name<S: Into<String>>(mut self, name: S) -> Self {
210 self.array_name = Some(name.into());
211 self
212 }
213
214 pub fn data(mut self, data: Vec<Vec<T>>) -> Self {
216 self.data = data;
217 self
218 }
219
220 pub fn add_column<S: Into<String>>(mut self, column: S) -> Self {
222 self.column_headers.push(column.into());
223 self
224 }
225
226 pub fn add_column_with_default<S: Into<String>>(mut self, column: S, default: T) -> Self {
228 self.column_headers.push(column.into());
229 for row in &mut self.data {
230 row.push(default.clone());
231 }
232 self
233 }
234
235 pub fn add_row<S: Into<String>>(mut self, row: S, data: Vec<T>) -> Self {
237 self.row_headers.push(row.into());
238 self.data.push(data);
239 self
240 }
241
242 pub fn remove_row(mut self) -> Self {
244 if self.data.pop().is_some() {
245 self.row_headers.pop();
246 }
247 self
248 }
249
250 pub fn remove_column(mut self) -> Self {
252 if self.column_headers.pop().is_some() {
253 for row in &mut self.data {
254 row.pop();
255 }
256 }
257 self
258 }
259
260 pub fn build(self) -> ArrayView<ArrayDataRow<T>, usize> {
262 let col_count = if self.column_headers.is_empty() {
263 self.data.iter().map(Vec::len).max().unwrap_or(0)
264 } else {
265 self.column_headers.len()
266 };
267
268 let mut row_headers = self.row_headers;
269 if row_headers.len() < self.data.len() {
270 let start = row_headers.len();
271 let end = self.data.len();
272 row_headers.extend((start..end).map(|i| format!("R{}", i + 1)));
273 }
274
275 let mut row_header_width = row_headers
276 .iter()
277 .map(|header| header.chars().count())
278 .max()
279 .unwrap_or(0);
280 if let Some(name) = self.array_name.as_ref() {
281 row_header_width = row_header_width.max(name.chars().count());
282 }
283
284 let mut titles = Vec::with_capacity(col_count);
285 let mut column_widths = vec![0; col_count];
286 for i in 0..col_count {
287 let title = self
288 .column_headers
289 .get(i)
290 .cloned()
291 .unwrap_or_else(|| format!("C{}", i + 1));
292 column_widths[i] = title.chars().count();
293 titles.push(title);
294 }
295
296 for row in &self.data {
297 for (i, cell) in row.iter().enumerate() {
298 if i >= col_count {
299 break;
300 }
301 let width = cell.to_string().chars().count();
302 if width > column_widths[i] {
303 column_widths[i] = width;
304 }
305 }
306 }
307 let mut array = ArrayView::<ArrayDataRow<T>, usize>::new();
308 array.set_row_header(|h| {
309 let h = h.align(self.row_header_alignment);
310 if row_header_width > 0 {
311 h.width(row_header_width)
312 } else {
313 h
314 }
315 });
316
317 for i in 0..col_count {
318 let title = titles[i].clone();
319 let width = column_widths[i];
320 let column_header_alignment = self.column_header_alignment;
321 let data_alignment = self.data_alignment;
322 array.add_column(i, title, move |c| {
323 let c = c.align(data_alignment).header_align(column_header_alignment);
324 if width > 0 {
325 c.width(width)
326 } else {
327 c
328 }
329 });
330 }
331
332 let row_headers = Arc::new(row_headers);
333 let mut items = Vec::with_capacity(self.data.len());
334 for (i, mut cells) in self.data.into_iter().enumerate() {
335 if col_count > 0 && cells.len() > col_count {
336 cells.truncate(col_count);
337 }
338 items.push(ArrayDataRow::new(i, Arc::clone(&row_headers), cells));
339 }
340 array.set_items(items);
341
342 if let Some(name) = self.array_name {
343 array.set_array_name(name);
344 }
345
346 array
347 }
348}
349
350#[derive(Clone)]
352pub struct TableDataRow<T> {
353 cells: Vec<T>,
354}
355
356impl<T> TableDataRow<T> {
357 fn new(cells: Vec<T>) -> Self {
358 Self { cells }
359 }
360}
361
362impl<T> TableViewItem<usize> for TableDataRow<T>
363where
364 T: ToString + Clone,
365{
366 fn to_column(&self, column: usize) -> String {
367 self.cells
368 .get(column)
369 .map(|value| value.to_string())
370 .unwrap_or_default()
371 }
372
373 fn cmp(&self, other: &Self, column: usize) -> Ordering {
374 let left = self
375 .cells
376 .get(column)
377 .map(|value| value.to_string())
378 .unwrap_or_default();
379 let right = other
380 .cells
381 .get(column)
382 .map(|value| value.to_string())
383 .unwrap_or_default();
384 left.cmp(&right)
385 }
386}
387
388pub struct TableBuilder<T> {
390 column_headers: Vec<String>,
391 data: Vec<Vec<T>>,
392 column_header_alignment: HAlign,
393 data_alignment: HAlign,
394 sortable: bool,
395}
396
397impl<T> Default for TableBuilder<T> {
398 fn default() -> Self {
399 Self {
400 column_headers: Vec::new(),
401 data: Vec::new(),
402 column_header_alignment: HAlign::Center,
403 data_alignment: HAlign::Center,
404 sortable: false,
405 }
406 }
407}
408
409impl<T> TableBuilder<T>
410where
411 T: ToString + Clone,
412{
413 pub fn new() -> Self {
415 Self::default()
416 }
417
418 pub fn column_header<S: Into<String>>(mut self, columns: Vec<S>) -> Self {
420 self.column_headers = columns.into_iter().map(Into::into).collect();
421 self
422 }
423
424 pub fn column_headers<S: Into<String>>(self, columns: Vec<S>) -> Self {
426 self.column_header(columns)
427 }
428
429 pub fn column_header_orientation(mut self, alignment: HAlign) -> Self {
431 self.column_header_alignment = alignment;
432 self
433 }
434
435 pub fn data_orientation(mut self, alignment: HAlign) -> Self {
437 self.data_alignment = alignment;
438 self
439 }
440
441 pub fn sortable(mut self, sortable: bool) -> Self {
443 self.sortable = sortable;
444 self
445 }
446
447 pub fn data(mut self, data: Vec<Vec<T>>) -> Self {
449 self.data = data;
450 self
451 }
452
453 pub fn add_column<S: Into<String>>(mut self, column: S) -> Self {
455 self.column_headers.push(column.into());
456 self
457 }
458
459 pub fn add_column_with_default<S: Into<String>>(mut self, column: S, default: T) -> Self {
461 self.column_headers.push(column.into());
462 for row in &mut self.data {
463 row.push(default.clone());
464 }
465 self
466 }
467
468 pub fn add_row(mut self, data: Vec<T>) -> Self {
470 self.data.push(data);
471 self
472 }
473
474 pub fn remove_row(mut self) -> Self {
476 self.data.pop();
477 self
478 }
479
480 pub fn remove_column(mut self) -> Self {
482 if self.column_headers.pop().is_some() {
483 for row in &mut self.data {
484 row.pop();
485 }
486 }
487 self
488 }
489
490 pub fn build(self) -> TableView<TableDataRow<T>, usize> {
492 let col_count = if self.column_headers.is_empty() {
493 self.data.iter().map(Vec::len).max().unwrap_or(0)
494 } else {
495 self.column_headers.len()
496 };
497
498 let mut titles = Vec::with_capacity(col_count);
499 let mut column_widths = vec![0; col_count];
500 for i in 0..col_count {
501 let title = self
502 .column_headers
503 .get(i)
504 .cloned()
505 .unwrap_or_else(|| format!("C{}", i + 1));
506 column_widths[i] = title.chars().count();
507 titles.push(title);
508 }
509
510 for row in &self.data {
511 for (i, cell) in row.iter().enumerate() {
512 if i >= col_count {
513 break;
514 }
515 let width = cell.to_string().chars().count();
516 if width > column_widths[i] {
517 column_widths[i] = width;
518 }
519 }
520 }
521 if self.sortable {
522 for (i, title) in titles.iter().enumerate() {
523 let target = title.chars().count().saturating_add(4);
524 if column_widths[i] < target {
525 column_widths[i] = target;
526 }
527 }
528 }
529
530 let mut table = TableView::<TableDataRow<T>, usize>::new();
531 for i in 0..col_count {
532 let title = titles[i].clone();
533 let width = column_widths[i];
534 let header_alignment = self.column_header_alignment;
535 let data_alignment = self.data_alignment;
536 table.add_column(i, title, move |c| {
537 let c = c.align(data_alignment).header_align(header_alignment);
538 if width > 0 {
539 c.width(width)
540 } else {
541 c
542 }
543 });
544 }
545
546 table.set_sortable(self.sortable);
547
548 let mut items = Vec::with_capacity(self.data.len());
549 for mut row in self.data.into_iter() {
550 if col_count > 0 && row.len() > col_count {
551 row.truncate(col_count);
552 }
553 items.push(TableDataRow::new(row));
554 }
555 table.set_items(items);
556
557 table
558 }
559}
560
561type OnSortCallback<H> = Arc<dyn Fn(&mut Cursive, H, Ordering) + Send + Sync>;
567
568type IndexCallback = Arc<dyn Fn(&mut Cursive, usize, usize) + Send + Sync>;
572
573type CellCallback<H> = Arc<dyn Fn(&mut Cursive, usize, H) + Send + Sync>;
577
578pub struct TableView<T, H> {
636 enabled: bool,
637 sortable: bool,
638 scroll_core: scroll::Core,
639 needs_relayout: bool,
640
641 column_select: bool,
642 columns: Vec<TableColumn<H>>,
643 column_indices: HashMap<H, usize>,
645
646 focus: usize,
647 focus_col: usize,
648 items: Vec<T>,
649 rows_to_items: Vec<usize>,
651
652 on_sort: Option<OnSortCallback<H>>,
653 on_submit: Option<IndexCallback>,
656 on_submit_cell: Option<CellCallback<H>>,
657 on_select: Option<IndexCallback>,
658 selection_mode: SelectMode,
659}
660
661cursive::impl_scroller!(TableView < T, H > ::scroll_core);
662
663impl<T, H> Default for TableView<T, H>
664where
665 T: TableViewItem<H> + PartialEq,
666 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
667{
668 fn default() -> Self {
672 Self::new()
673 }
674}
675
676impl<T, H> TableView<T, H>
677where
678 T: TableViewItem<H> + PartialEq,
679 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
680{
681 pub fn set_items_stable(&mut self, items: Vec<T>) {
689 let new_location = self
691 .item()
692 .and_then(|old_item| {
693 let old_item = &self.items[old_item];
694 items.iter().position(|new| new == old_item)
695 })
696 .unwrap_or(0);
697
698 self.set_items_and_focus(items, new_location);
699 }
700}
701
702impl<T, H> TableView<T, H>
703where
704 T: TableViewItem<H>,
705 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
706{
707 pub fn new() -> Self {
712 Self {
713 enabled: true,
714 sortable: true,
715 scroll_core: scroll::Core::new(),
716 needs_relayout: true,
717
718 column_select: false,
719 columns: Vec::new(),
720 column_indices: HashMap::new(),
721
722 focus: 0,
723 focus_col: 0,
724 items: Vec::new(),
725 rows_to_items: Vec::new(),
726
727 on_sort: None,
728 on_submit: None,
729 on_submit_cell: None,
730 on_select: None,
731 selection_mode: SelectMode::Row,
732 }
733 }
734
735 pub fn column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
741 mut self,
742 column: H,
743 title: S,
744 callback: C,
745 ) -> Self {
746 self.add_column(column, title, callback);
747 self
748 }
749
750 pub fn add_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
756 &mut self,
757 column: H,
758 title: S,
759 callback: C,
760 ) {
761 self.insert_column(self.columns.len(), column, title, callback);
762 }
763
764 pub fn remove_column(&mut self, i: usize) {
766 for column in &self.columns[i + 1..] {
768 *self.column_indices.get_mut(&column.column).unwrap() -= 1;
769 }
770
771 let column = self.columns.remove(i);
772 self.column_indices.remove(&column.column);
773 if self.focus_col >= self.columns.len() {
774 self.focus_col = self.columns.len().saturating_sub(1);
775 } else if i <= self.focus_col {
776 self.focus_col = self.focus_col.saturating_sub(1);
777 }
778 self.needs_relayout = true;
779 }
780
781 pub fn insert_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
787 &mut self,
788 i: usize,
789 column: H,
790 title: S,
791 callback: C,
792 ) {
793 for column in &self.columns[i..] {
795 *self.column_indices.get_mut(&column.column).unwrap() += 1;
796 }
797
798 self.column_indices.insert(column, i);
799 self.columns
800 .insert(i, callback(TableColumn::new(column, title.into())));
801
802 if i <= self.focus_col {
803 self.focus_col += 1;
804 }
805
806 if self.columns.len() == 1 {
808 self.set_default_column(column);
809 }
810 self.needs_relayout = true;
811 }
812
813 pub fn default_column(mut self, column: H) -> Self {
815 self.set_default_column(column);
816 self
817 }
818
819 pub fn set_default_column(&mut self, column: H) {
821 if self.column_indices.contains_key(&column) {
822 for c in &mut self.columns {
823 c.selected = c.column == column;
824 if c.selected {
825 c.order = c.default_order;
826 } else {
827 c.order = Ordering::Equal;
828 }
829 }
830 if let Some(index) = self.column_indices.get(&column) {
831 self.focus_col = *index;
832 }
833 }
834 }
835
836 pub fn sort_by(&mut self, column: H, order: Ordering) {
839 if !self.sortable {
840 return;
841 }
842
843 if self.column_indices.contains_key(&column) {
844 for c in &mut self.columns {
845 let selected = c.column == column;
847 c.selected = selected;
848 c.order = if selected { order } else { Ordering::Equal };
849 }
850 }
851
852 self.sort_items(column, order);
853 }
854
855 pub fn sort(&mut self) {
858 if !self.sortable || self.items.len() <= 1 {
859 return;
860 }
861
862 if let Some((column, order)) = self.order() {
863 self.sort_items(column, order);
864 }
865 }
866
867 pub fn order(&self) -> Option<(H, Ordering)> {
873 for c in &self.columns {
874 if c.order != Ordering::Equal {
875 return Some((c.column, c.order));
876 }
877 }
878 None
879 }
880
881 pub fn disable(&mut self) {
885 self.enabled = false;
886 }
887
888 pub fn enable(&mut self) {
890 self.enabled = true;
891 }
892
893 pub fn set_enabled(&mut self, enabled: bool) {
895 self.enabled = enabled;
896 }
897
898 pub fn set_sortable(&mut self, sortable: bool) {
900 self.sortable = sortable;
901 self.column_select &= sortable;
902 }
903
904 pub fn set_selection_mode(&mut self, mode: SelectMode) {
906 self.selection_mode = mode;
907 if mode == SelectMode::Row {
908 self.column_select = false;
909 }
910 }
911
912 pub fn selection_mode(mut self, mode: SelectMode) -> Self {
916 self.set_selection_mode(mode);
917 self
918 }
919
920 pub fn sortable(self, sortable: bool) -> Self {
924 self.with(|t| t.set_sortable(sortable))
925 }
926
927 pub fn is_sortable(&self) -> bool {
929 self.sortable
930 }
931
932 pub fn is_enabled(&self) -> bool {
934 self.enabled
935 }
936
937 pub fn set_on_sort<F>(&mut self, cb: F)
964 where
965 F: Fn(&mut Cursive, H, Ordering) + Send + Sync + 'static,
966 {
967 self.on_sort = Some(Arc::new(move |s, h, o| cb(s, h, o)));
968 }
969
970 pub fn on_sort<F>(self, cb: F) -> Self
999 where
1000 F: Fn(&mut Cursive, H, Ordering) + Send + Sync + 'static,
1001 {
1002 self.with(|t| t.set_on_sort(cb))
1003 }
1004
1005 pub fn set_on_submit<F>(&mut self, cb: F)
1035 where
1036 F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
1037 {
1038 self.on_submit = Some(Arc::new(move |s, row, index| cb(s, row, index)));
1039 }
1040
1041 pub fn set_on_submit_cell<F>(&mut self, cb: F)
1046 where
1047 F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
1048 {
1049 self.on_submit_cell = Some(Arc::new(move |s, row, column| cb(s, row, column)));
1050 }
1051
1052 pub fn on_submit<F>(self, cb: F) -> Self
1083 where
1084 F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
1085 {
1086 self.with(|t| t.set_on_submit(cb))
1087 }
1088
1089 pub fn on_submit_cell<F>(self, cb: F) -> Self
1096 where
1097 F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
1098 {
1099 self.with(|t| t.set_on_submit_cell(cb))
1100 }
1101
1102 pub fn set_on_select<F>(&mut self, cb: F)
1131 where
1132 F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
1133 {
1134 self.on_select = Some(Arc::new(move |s, row, index| cb(s, row, index)));
1135 }
1136
1137 pub fn on_select<F>(self, cb: F) -> Self
1167 where
1168 F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
1169 {
1170 self.with(|t| t.set_on_select(cb))
1171 }
1172
1173 pub fn clear(&mut self) {
1175 self.items.clear();
1176 self.rows_to_items.clear();
1177 self.focus = 0;
1178 self.needs_relayout = true;
1179 }
1180
1181 pub fn len(&self) -> usize {
1183 self.items.len()
1184 }
1185
1186 pub fn is_empty(&self) -> bool {
1188 self.items.is_empty()
1189 }
1190
1191 pub fn row(&self) -> Option<usize> {
1193 if self.items.is_empty() {
1194 None
1195 } else {
1196 Some(self.focus)
1197 }
1198 }
1199
1200 pub fn set_selected_row(&mut self, row_index: usize) {
1202 self.focus = row_index;
1203 self.scroll_core.scroll_to_y(row_index);
1204 }
1205
1206 pub fn selected_row(self, row_index: usize) -> Self {
1210 self.with(|t| t.set_selected_row(row_index))
1211 }
1212
1213 pub fn set_items(&mut self, items: Vec<T>) {
1218 self.set_items_and_focus(items, 0);
1219 }
1220
1221 fn set_items_and_focus(&mut self, items: Vec<T>, new_location: usize) {
1222 self.items = items;
1223 self.rows_to_items = Vec::with_capacity(self.items.len());
1224
1225 for i in 0..self.items.len() {
1226 self.rows_to_items.push(i);
1227 }
1228
1229 if let Some((column, order)) = self.order() {
1230 let selected_column = self.columns.iter().find(|c| c.selected).map(|c| c.column);
1232 self.sort_by(column, order);
1233 if let Some(column) = selected_column {
1234 for c in &mut self.columns {
1235 c.selected = c.column == column;
1236 }
1237 }
1238 }
1239
1240 self.set_selected_item(new_location);
1241 self.needs_relayout = true;
1242 }
1243
1244 pub fn items(self, items: Vec<T>) -> Self {
1250 self.with(|t| t.set_items(items))
1251 }
1252
1253 pub fn borrow_item(&self, index: usize) -> Option<&T> {
1256 self.items.get(index)
1257 }
1258
1259 pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
1262 self.items.get_mut(index)
1263 }
1264
1265 pub fn borrow_items(&self) -> &[T] {
1267 &self.items
1268 }
1269
1270 pub fn borrow_items_mut(&mut self) -> &mut [T] {
1274 self.needs_relayout = true;
1275 &mut self.items
1276 }
1277
1278 pub fn item(&self) -> Option<usize> {
1281 self.rows_to_items.get(self.focus).copied()
1282 }
1283
1284 pub fn set_selected_item(&mut self, item_index: usize) {
1287 if item_index >= self.items.len() {
1288 return;
1289 }
1290
1291 if let Some(row) = self
1292 .rows_to_items
1293 .iter()
1294 .position(|&item| item == item_index)
1295 {
1296 self.focus = row;
1297 self.scroll_core.scroll_to_y(row);
1298 }
1299 }
1300
1301 pub fn selected_item(self, item_index: usize) -> Self {
1306 self.with(|t| t.set_selected_item(item_index))
1307 }
1308
1309 pub fn insert_item(&mut self, item: T) {
1316 self.insert_item_at(self.items.len(), item);
1317 }
1318
1319 pub fn insert_item_at(&mut self, index: usize, item: T) {
1330 self.items.push(item);
1331
1332 self.rows_to_items.insert(index, self.items.len() - 1);
1334
1335 if let Some((column, order)) = self.order() {
1336 self.sort_by(column, order);
1337 }
1338 self.needs_relayout = true;
1339 }
1340
1341 pub fn remove_item(&mut self, item_index: usize) -> Option<T> {
1344 if item_index < self.items.len() {
1345 if let Some(selected_index) = self.item() {
1347 if selected_index == item_index {
1348 self.focus_up(1);
1349 }
1350 }
1351
1352 self.rows_to_items.retain(|i| *i != item_index);
1354
1355 for ref_index in &mut self.rows_to_items {
1357 if *ref_index > item_index {
1358 *ref_index -= 1;
1359 }
1360 }
1361 self.needs_relayout = true;
1362
1363 Some(self.items.remove(item_index))
1365 } else {
1366 None
1367 }
1368 }
1369
1370 pub fn take_items(&mut self) -> Vec<T> {
1372 self.set_selected_row(0);
1373 self.rows_to_items.clear();
1374 self.needs_relayout = true;
1375 self.items.drain(0..).collect()
1376 }
1377}
1378
1379impl<T, H> TableView<T, H>
1380where
1381 T: TableViewItem<H>,
1382 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1383{
1384 fn draw_columns<C: Fn(&Printer, &TableColumn<H>)>(
1385 &self,
1386 printer: &Printer,
1387 sep: &str,
1388 callback: C,
1389 ) {
1390 let mut column_offset = 0;
1391 let column_count = self.columns.len();
1392 for (index, column) in self.columns.iter().enumerate() {
1393 let printer = &printer.offset((column_offset, 0)).focused(true);
1394
1395 callback(printer, column);
1396
1397 if 1 + index < column_count {
1398 printer.print((column.width + 1, 0), sep);
1399 }
1400
1401 column_offset += column.width + 3;
1402 }
1403 }
1404
1405 fn sort_items(&mut self, column: H, order: Ordering) {
1406 if !self.is_empty() {
1407 let old_item = self.item();
1408
1409 let items = &self.items;
1410 let rows_to_items = &mut self.rows_to_items;
1411 let ascending = order == Ordering::Less;
1412 rows_to_items.sort_by(|a, b| {
1413 if ascending {
1414 items[*a].cmp(&items[*b], column)
1415 } else {
1416 items[*b].cmp(&items[*a], column)
1417 }
1418 });
1419
1420 if let Some(old_item) = old_item {
1421 self.set_selected_item(old_item);
1422 }
1423 }
1424 }
1425
1426 fn draw_item(&self, printer: &Printer, i: usize) {
1427 let focused_row = i == self.focus && self.enabled;
1428 let focused_col = self.focus_col;
1429 self.draw_columns(printer, "┆ ", |printer, column| {
1430 let index = self.column_indices[&column.column];
1431 let color = if self.selection_mode == SelectMode::Cell
1432 && focused_row
1433 && index == focused_col
1434 {
1435 if printer.focused {
1436 theme::ColorStyle::highlight()
1437 } else {
1438 theme::ColorStyle::highlight_inactive()
1439 }
1440 } else if self.selection_mode == SelectMode::Row && focused_row {
1441 if printer.focused {
1442 theme::ColorStyle::highlight()
1443 } else {
1444 theme::ColorStyle::highlight_inactive()
1445 }
1446 } else {
1447 theme::ColorStyle::primary()
1448 };
1449
1450 let value = self.items[self.rows_to_items[i]].to_column(column.column);
1451 printer.with_color(color, |printer| {
1452 column.draw_row(printer, value.as_str());
1453 });
1454 });
1455 }
1456
1457 fn on_focus_change(&self) -> EventResult {
1458 let row = self.row().unwrap();
1459 let index = self.item().unwrap();
1460 EventResult::Consumed(
1461 self.on_select
1462 .clone()
1463 .map(|cb| Callback::from_fn(move |s| cb(s, row, index))),
1464 )
1465 }
1466
1467 fn focus_up(&mut self, n: usize) {
1468 self.focus -= cmp::min(self.focus, n);
1469 }
1470
1471 fn focus_down(&mut self, n: usize) {
1472 self.focus = cmp::min(self.focus + n, self.items.len().saturating_sub(1));
1473 }
1474
1475 fn active_column(&self) -> usize {
1476 self.columns.iter().position(|c| c.selected).unwrap_or(0)
1477 }
1478
1479 fn column_cancel(&mut self) {
1480 self.column_select = false;
1481 for column in &mut self.columns {
1482 column.selected = column.order != Ordering::Equal;
1483 }
1484 }
1485
1486 fn column_next(&mut self) -> bool {
1487 let column = self.active_column();
1488 if 1 + column < self.columns.len() {
1489 self.columns[column].selected = false;
1490 self.columns[column + 1].selected = true;
1491 self.focus_col = column + 1;
1492 true
1493 } else {
1494 false
1495 }
1496 }
1497
1498 fn column_prev(&mut self) -> bool {
1499 let column = self.active_column();
1500 if column > 0 {
1501 self.columns[column].selected = false;
1502 self.columns[column - 1].selected = true;
1503 self.focus_col = column - 1;
1504 true
1505 } else {
1506 false
1507 }
1508 }
1509
1510 fn column_select(&mut self) -> EventResult {
1511 if !self.sortable {
1512 self.column_cancel();
1513 return EventResult::Ignored;
1514 }
1515
1516 let next = self.active_column();
1517 let column = self.columns[next].column;
1518 let current = self
1519 .columns
1520 .iter()
1521 .position(|c| c.order != Ordering::Equal)
1522 .unwrap_or(0);
1523
1524 let order = if current != next {
1525 self.columns[next].default_order
1526 } else if self.columns[current].order == Ordering::Less {
1527 Ordering::Greater
1528 } else {
1529 Ordering::Less
1530 };
1531
1532 self.sort_by(column, order);
1533
1534 if self.on_sort.is_some() {
1535 let c = &self.columns[self.active_column()];
1536 let column = c.column;
1537 let order = c.order;
1538
1539 let cb = self.on_sort.clone().unwrap();
1540 EventResult::with_cb(move |s| cb(s, column, order))
1541 } else {
1542 EventResult::Consumed(None)
1543 }
1544 }
1545
1546 fn column_for_x(&self, mut x: usize) -> Option<usize> {
1547 for (i, col) in self.columns.iter().enumerate() {
1548 x = match x.checked_sub(col.width) {
1549 None => return Some(i),
1550 Some(x) => x.checked_sub(3)?,
1551 };
1552 }
1553
1554 None
1555 }
1556
1557 fn draw_content(&self, printer: &Printer) {
1558 for i in 0..self.rows_to_items.len() {
1559 let printer = printer.offset((0, i));
1560 let color = if i == self.focus && self.enabled {
1561 if !self.column_select && self.enabled && printer.focused {
1562 theme::ColorStyle::highlight()
1563 } else {
1564 theme::ColorStyle::highlight_inactive()
1565 }
1566 } else {
1567 theme::ColorStyle::primary()
1568 };
1569
1570 if i < self.items.len() {
1571 printer.with_color(color, |printer| {
1572 self.draw_item(printer, i);
1573 });
1574 }
1575 }
1576 }
1577
1578 fn layout_content(&mut self, size: Vec2) {
1579 let column_count = self.columns.len();
1580
1581 let (mut sized, mut flexible): (Vec<&mut TableColumn<H>>, Vec<&mut TableColumn<H>>) = self
1583 .columns
1584 .iter_mut()
1585 .partition(|c| c.requested_width.is_some());
1586
1587 let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
1589
1590 let mut remaining_width = available_width;
1592 for column in &mut sized {
1593 column.width = match *column.requested_width.as_ref().unwrap() {
1594 TableColumnWidth::Percent(width) => cmp::min(
1595 (size.x as f32 / 100.0 * width as f32).ceil() as usize,
1596 remaining_width,
1597 ),
1598 TableColumnWidth::Absolute(width) => width,
1599 };
1600 remaining_width = remaining_width.saturating_sub(column.width);
1601 }
1602
1603 let remaining_columns = flexible.len();
1605 let base_width = if remaining_columns > 0 {
1606 remaining_width / remaining_columns
1607 } else {
1608 0
1609 };
1610 for column in &mut flexible {
1611 column.width = base_width;
1612 }
1613
1614 self.needs_relayout = false;
1615 }
1616
1617 fn content_required_size(&mut self, req: Vec2) -> Vec2 {
1618 let rows = self.rows_to_items.len();
1619 let separators = self.columns.len().saturating_sub(1).saturating_mul(3);
1620
1621 let resolve_width = |requested: &TableColumnWidth| match *requested {
1622 TableColumnWidth::Percent(width) => {
1623 (req.x as f32 / 100.0 * width as f32).ceil() as usize
1624 }
1625 TableColumnWidth::Absolute(width) => width,
1626 };
1627
1628 let mut width: usize = 0;
1629 for column in &self.columns {
1630 let col_width = column
1631 .requested_width
1632 .as_ref()
1633 .map(resolve_width)
1634 .unwrap_or(column.width);
1635 width = width.saturating_add(col_width);
1636 }
1637
1638 let width = if width == 0 {
1639 req.x
1640 } else {
1641 width.saturating_add(separators)
1642 };
1643
1644 Vec2::new(width, rows)
1645 }
1646
1647 fn on_inner_event(&mut self, event: Event) -> EventResult {
1648 let last_focus = self.focus;
1649 match event {
1650 Event::Key(Key::Right) => {
1651 if self.selection_mode == SelectMode::Cell && !self.sortable {
1652 if self.focus_col + 1 < self.columns.len() {
1653 self.focus_col += 1;
1654 } else {
1655 return EventResult::Ignored;
1656 }
1657 } else if self.sortable {
1658 if self.column_select {
1659 if !self.column_next() {
1660 return EventResult::Ignored;
1661 }
1662 } else {
1663 self.column_select = true;
1664 }
1665 } else {
1666 return EventResult::Ignored;
1667 }
1668 }
1669 Event::Key(Key::Left) => {
1670 if self.selection_mode == SelectMode::Cell && !self.sortable {
1671 if self.focus_col > 0 {
1672 self.focus_col -= 1;
1673 } else {
1674 return EventResult::Ignored;
1675 }
1676 } else if self.sortable {
1677 if self.column_select {
1678 if !self.column_prev() {
1679 return EventResult::Ignored;
1680 }
1681 } else {
1682 self.column_select = true;
1683 }
1684 } else {
1685 return EventResult::Ignored;
1686 }
1687 }
1688 Event::Key(Key::Up) if self.focus > 0 || self.column_select => {
1689 if self.column_select {
1690 self.column_cancel();
1691 } else {
1692 self.focus_up(1);
1693 }
1694 }
1695 Event::Key(Key::Down) if self.focus + 1 < self.items.len() || self.column_select => {
1696 if self.column_select {
1697 self.column_cancel();
1698 } else {
1699 self.focus_down(1);
1700 }
1701 }
1702 Event::Key(Key::PageUp) => {
1703 self.column_cancel();
1704 self.focus_up(10);
1705 }
1706 Event::Key(Key::PageDown) => {
1707 self.column_cancel();
1708 self.focus_down(10);
1709 }
1710 Event::Key(Key::Home) => {
1711 self.column_cancel();
1712 self.focus = 0;
1713 }
1714 Event::Key(Key::End) => {
1715 self.column_cancel();
1716 self.focus = self.items.len().saturating_sub(1);
1717 }
1718 Event::Key(Key::Enter) => {
1719 if self.column_select && self.sortable {
1720 return self.column_select();
1721 } else if !self.is_empty()
1722 && self.selection_mode == SelectMode::Cell
1723 && self.on_submit_cell.is_some()
1724 {
1725 return self.on_submit_cell_event();
1726 } else if !self.is_empty() && self.on_submit.is_some() {
1727 return self.on_submit_event();
1728 }
1729 }
1730 Event::Mouse {
1731 position,
1732 offset,
1733 event: MouseEvent::Press(MouseButton::Left),
1734 } if !self.is_empty()
1735 && position
1736 .checked_sub(offset)
1737 .map_or(false, |p| p.y == self.focus) =>
1738 {
1739 if let Some(p) = position.checked_sub(offset) {
1740 if let Some(col) = self.column_for_x(p.x) {
1741 self.focus_col = col;
1742 }
1743 }
1744 self.column_cancel();
1745 if self.selection_mode == SelectMode::Cell && self.on_submit_cell.is_some() {
1746 return self.on_submit_cell_event();
1747 }
1748 return self.on_submit_event();
1749 }
1750 Event::Mouse {
1751 position,
1752 offset,
1753 event: MouseEvent::Press(_),
1754 } if !self.is_empty() => match position.checked_sub(offset) {
1755 Some(position) if position.y < self.rows_to_items.len() => {
1756 self.column_cancel();
1757 if let Some(col) = self.column_for_x(position.x) {
1758 self.focus_col = col;
1759 }
1760 self.focus = position.y;
1761 }
1762 _ => return EventResult::Ignored,
1763 },
1764 _ => return EventResult::Ignored,
1765 }
1766
1767 let focus = self.focus;
1768
1769 if self.column_select {
1770 EventResult::Consumed(None)
1771 } else if !self.is_empty() && last_focus != focus {
1772 self.on_focus_change()
1773 } else {
1774 EventResult::Ignored
1775 }
1776 }
1777
1778 fn inner_important_area(&self, size: Vec2) -> Rect {
1779 Rect::from_size((0, self.focus), (size.x, 1))
1780 }
1781
1782 fn on_submit_event(&mut self) -> EventResult {
1783 if let Some(cb) = &self.on_submit {
1784 let cb = Arc::clone(cb);
1785 let row = self.row().unwrap();
1786 let index = self.item().unwrap();
1787 return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row, index))));
1788 }
1789 EventResult::Ignored
1790 }
1791
1792 fn on_submit_cell_event(&mut self) -> EventResult {
1793 if let Some(cb) = &self.on_submit_cell {
1794 let cb = Arc::clone(cb);
1795 let row = self.row().unwrap();
1796 let col_index = cmp::min(self.focus_col, self.columns.len().saturating_sub(1));
1797 if let Some(column) = self.columns.get(col_index).map(|c| c.column) {
1798 return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row, column))));
1799 }
1800 }
1801 EventResult::Ignored
1802 }
1803}
1804
1805impl<T, H> View for TableView<T, H>
1806where
1807 T: TableViewItem<H> + Send + Sync + 'static,
1808 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1809{
1810 fn required_size(&mut self, req: Vec2) -> Vec2 {
1811 let content = self.content_required_size(req);
1812 Vec2::new(content.x, content.y.saturating_add(2))
1813 }
1814
1815 fn draw(&self, printer: &Printer) {
1816 self.draw_columns(printer, "╷ ", |printer, column| {
1817 let color = if self.enabled
1818 && self.sortable
1819 && (column.order != Ordering::Equal || column.selected)
1820 {
1821 if self.column_select && column.selected && printer.focused {
1822 theme::ColorStyle::highlight()
1823 } else {
1824 theme::ColorStyle::highlight_inactive()
1825 }
1826 } else {
1827 theme::ColorStyle::primary()
1828 };
1829
1830 printer.with_color(color, |printer| {
1831 column.draw_header(printer, self.sortable);
1832 });
1833 });
1834
1835 self.draw_columns(
1836 &printer.offset((0, 1)).focused(true),
1837 "┴─",
1838 |printer, column| {
1839 printer.print_hline((0, 0), column.width + 1, "─");
1840 },
1841 );
1842
1843 let available_height = printer.size.y.saturating_sub(2);
1845 let filled_rows = self.rows_to_items.len().min(available_height);
1846 for y in 2 + filled_rows..printer.size.y {
1847 self.draw_columns(&printer.offset((0, y)), "┆ ", |_, _| ());
1848 }
1849
1850 let printer = &printer.offset((0, 2)).focused(true);
1851 scroll::draw(self, printer, Self::draw_content);
1852 }
1853
1854 fn layout(&mut self, size: Vec2) {
1855 scroll::layout(
1856 self,
1857 size.saturating_sub((0, 2)),
1858 self.needs_relayout,
1859 Self::layout_content,
1860 Self::content_required_size,
1861 );
1862 }
1863
1864 fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
1865 self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
1866 }
1867
1868 fn on_event(&mut self, event: Event) -> EventResult {
1869 if !self.enabled {
1870 return EventResult::Ignored;
1871 }
1872
1873 match event {
1874 Event::Mouse {
1875 position,
1876 offset,
1877 event: MouseEvent::Press(MouseButton::Left),
1878 } if position.checked_sub(offset).map_or(false, |p| p.y == 0) => {
1879 if !self.sortable {
1880 return EventResult::Ignored;
1881 }
1882
1883 if let Some(position) = position.checked_sub(offset) {
1884 if let Some(col) = self.column_for_x(position.x) {
1885 if self.column_select && self.columns[col].selected {
1886 return self.column_select();
1887 } else {
1888 let active = self.active_column();
1889 self.columns[active].selected = false;
1890 self.columns[col].selected = true;
1891 self.column_select = true;
1892 }
1893 }
1894 }
1895 EventResult::Ignored
1896 }
1897 event => scroll::on_event(
1898 self,
1899 event.relativized((0, 2)),
1900 Self::on_inner_event,
1901 Self::inner_important_area,
1902 ),
1903 }
1904 }
1905
1906 fn important_area(&self, size: Vec2) -> Rect {
1907 self.inner_important_area(size.saturating_sub((0, 2))) + (0, 2)
1908 }
1909}
1910
1911pub struct ArrayView<T, H> {
1913 enabled: bool,
1914 scroll_core: scroll::Core,
1915 needs_relayout: bool,
1916
1917 columns: Vec<TableColumn<H>>,
1918 row_header: ArrayRowHeader,
1919 array_name: String,
1920
1921 focus: usize,
1922 focus_col: usize,
1923 items: Vec<T>,
1924
1925 on_submit: Option<CellCallback<H>>,
1926 on_select: Option<CellCallback<H>>,
1927}
1928
1929cursive::impl_scroller!(ArrayView < T, H > ::scroll_core);
1930
1931impl<T, H> Default for ArrayView<T, H>
1932where
1933 T: ArrayViewItem<H>,
1934 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1935{
1936 fn default() -> Self {
1940 Self::new()
1941 }
1942}
1943
1944impl<T, H> ArrayView<T, H>
1945where
1946 T: ArrayViewItem<H> + PartialEq,
1947 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1948{
1949 pub fn set_items_stable(&mut self, items: Vec<T>) {
1954 let new_location = self
1956 .item()
1957 .and_then(|old_item| {
1958 let old_item = &self.items[old_item];
1959 items.iter().position(|new| new == old_item)
1960 })
1961 .unwrap_or(0);
1962
1963 self.set_items_and_focus(items, new_location);
1964 }
1965}
1966
1967impl<T, H> ArrayView<T, H>
1968where
1969 T: ArrayViewItem<H>,
1970 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1971{
1972 pub fn new() -> Self {
1977 Self {
1978 enabled: true,
1979 scroll_core: scroll::Core::new(),
1980 needs_relayout: true,
1981
1982 columns: Vec::new(),
1983 row_header: ArrayRowHeader::new(),
1984 array_name: String::new(),
1985
1986 focus: 0,
1987 focus_col: 0,
1988 items: Vec::new(),
1989
1990 on_submit: None,
1991 on_select: None,
1992 }
1993 }
1994
1995 pub fn row_header<C: FnOnce(ArrayRowHeader) -> ArrayRowHeader>(mut self, callback: C) -> Self {
2002 self.set_row_header(callback);
2003 self
2004 }
2005
2006 pub fn array_name<S: Into<String>>(mut self, name: S) -> Self {
2010 self.set_array_name(name);
2011 self
2012 }
2013
2014 pub fn set_array_name<S: Into<String>>(&mut self, name: S) {
2016 self.array_name = name.into();
2017 }
2018
2019 pub fn set_row_header<C: FnOnce(ArrayRowHeader) -> ArrayRowHeader>(
2024 &mut self,
2025 callback: C,
2026 ) {
2027 self.row_header = callback(ArrayRowHeader::new());
2028 self.needs_relayout = true;
2029 }
2030
2031 pub fn column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
2037 mut self,
2038 column: H,
2039 title: S,
2040 callback: C,
2041 ) -> Self {
2042 self.add_column(column, title, callback);
2043 self
2044 }
2045
2046 pub fn add_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
2052 &mut self,
2053 column: H,
2054 title: S,
2055 callback: C,
2056 ) {
2057 self.insert_column(self.columns.len(), column, title, callback);
2058 }
2059
2060 pub fn remove_column(&mut self, i: usize) {
2062 self.columns.remove(i);
2063 if self.focus_col >= self.columns.len() {
2064 self.focus_col = self.columns.len().saturating_sub(1);
2065 }
2066 self.needs_relayout = true;
2067 }
2068
2069 pub fn insert_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
2075 &mut self,
2076 i: usize,
2077 column: H,
2078 title: S,
2079 callback: C,
2080 ) {
2081 self.columns
2082 .insert(i, callback(TableColumn::new(column, title.into())));
2083 if self.focus_col >= self.columns.len() {
2084 self.focus_col = self.columns.len().saturating_sub(1);
2085 }
2086 self.needs_relayout = true;
2087 }
2088
2089 pub fn disable(&mut self) {
2093 self.enabled = false;
2094 }
2095
2096 pub fn enable(&mut self) {
2098 self.enabled = true;
2099 }
2100
2101 pub fn set_enabled(&mut self, enabled: bool) {
2103 self.enabled = enabled;
2104 }
2105
2106 pub fn is_enabled(&self) -> bool {
2108 self.enabled
2109 }
2110
2111 pub fn set_on_submit<F>(&mut self, cb: F)
2140 where
2141 F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
2142 {
2143 self.on_submit = Some(Arc::new(move |s, row, column| cb(s, row, column)));
2144 }
2145
2146 pub fn on_submit<F>(self, cb: F) -> Self
2174 where
2175 F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
2176 {
2177 self.with(|t| t.set_on_submit(cb))
2178 }
2179
2180 pub fn set_on_select<F>(&mut self, cb: F)
2208 where
2209 F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
2210 {
2211 self.on_select = Some(Arc::new(move |s, row, column| cb(s, row, column)));
2212 }
2213
2214 pub fn on_select<F>(self, cb: F) -> Self
2241 where
2242 F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
2243 {
2244 self.with(|t| t.set_on_select(cb))
2245 }
2246
2247 pub fn clear(&mut self) {
2249 self.items.clear();
2250 self.focus = 0;
2251 self.needs_relayout = true;
2252 }
2253
2254 pub fn len(&self) -> usize {
2256 self.items.len()
2257 }
2258
2259 pub fn is_empty(&self) -> bool {
2261 self.items.is_empty()
2262 }
2263
2264 pub fn row(&self) -> Option<usize> {
2266 if self.items.is_empty() {
2267 None
2268 } else {
2269 Some(self.focus)
2270 }
2271 }
2272
2273 pub fn set_selected_row(&mut self, row_index: usize) {
2275 self.focus = row_index;
2276 self.scroll_core.scroll_to_y(self.row_y(row_index));
2277 }
2278
2279 pub fn selected_row(self, row_index: usize) -> Self {
2283 self.with(|t| t.set_selected_row(row_index))
2284 }
2285
2286 pub fn set_items(&mut self, items: Vec<T>) {
2288 self.set_items_and_focus(items, 0);
2289 }
2290
2291 fn set_items_and_focus(&mut self, items: Vec<T>, new_location: usize) {
2292 self.items = items;
2293 self.set_selected_item(new_location);
2294 self.needs_relayout = true;
2295 }
2296
2297 pub fn items(self, items: Vec<T>) -> Self {
2301 self.with(|t| t.set_items(items))
2302 }
2303
2304 pub fn borrow_item(&self, index: usize) -> Option<&T> {
2307 self.items.get(index)
2308 }
2309
2310 pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
2313 self.items.get_mut(index)
2314 }
2315
2316 pub fn borrow_items(&self) -> &[T] {
2318 &self.items
2319 }
2320
2321 pub fn borrow_items_mut(&mut self) -> &mut [T] {
2325 self.needs_relayout = true;
2326 &mut self.items
2327 }
2328
2329 pub fn item(&self) -> Option<usize> {
2332 if self.items.is_empty() {
2333 None
2334 } else {
2335 Some(self.focus)
2336 }
2337 }
2338
2339 pub fn set_selected_item(&mut self, item_index: usize) {
2342 if item_index < self.items.len() {
2343 self.focus = item_index;
2344 self.scroll_core.scroll_to_y(self.row_y(item_index));
2345 }
2346 }
2347
2348 pub fn selected_item(self, item_index: usize) -> Self {
2353 self.with(|t| t.set_selected_item(item_index))
2354 }
2355
2356 pub fn insert_item(&mut self, item: T) {
2360 self.insert_item_at(self.items.len(), item);
2361 }
2362
2363 pub fn insert_item_at(&mut self, index: usize, item: T) {
2371 self.items.insert(index, item);
2372 self.needs_relayout = true;
2373 }
2374
2375 pub fn remove_item(&mut self, item_index: usize) -> Option<T> {
2378 if item_index < self.items.len() {
2379 if let Some(selected_index) = self.item() {
2380 if selected_index == item_index {
2381 self.focus_up(1);
2382 }
2383 }
2384
2385 self.needs_relayout = true;
2386 Some(self.items.remove(item_index))
2387 } else {
2388 None
2389 }
2390 }
2391
2392 pub fn take_items(&mut self) -> Vec<T> {
2394 self.set_selected_row(0);
2395 self.needs_relayout = true;
2396 self.items.drain(0..).collect()
2397 }
2398}
2399
2400impl<T, H> ArrayView<T, H>
2401where
2402 T: ArrayViewItem<H>,
2403 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
2404{
2405 fn row_y(&self, row_index: usize) -> usize {
2406 row_index.saturating_mul(2)
2407 }
2408
2409 fn row_for_y(&self, y: usize) -> Option<usize> {
2410 if y % 2 == 0 {
2411 Some(y / 2)
2412 } else {
2413 None
2414 }
2415 }
2416
2417 fn column_for_x(&self, mut x: usize) -> Option<usize> {
2418 x = match x.checked_sub(self.row_header.width) {
2419 None => return None,
2420 Some(x) => x,
2421 };
2422 x = x.checked_sub(3)?;
2423
2424 for (i, col) in self.columns.iter().enumerate() {
2425 x = match x.checked_sub(col.width) {
2426 None => return Some(i),
2427 Some(x) => x.checked_sub(3)?,
2428 };
2429 }
2430
2431 None
2432 }
2433
2434 fn draw_columns<C, R>(&self, printer: &Printer, sep: &str, row_cb: R, mut col_cb: C)
2435 where
2436 C: FnMut(&Printer, &TableColumn<H>, usize),
2437 R: Fn(&Printer, &ArrayRowHeader),
2438 {
2439 let row_header = &self.row_header;
2440 let printer = &printer.offset((0, 0)).focused(true);
2441 row_cb(printer, row_header);
2442
2443 if !self.columns.is_empty() {
2444 printer.print((row_header.width + 1, 0), sep);
2445 }
2446
2447 let mut column_offset = row_header.width + 3;
2448
2449 let column_count = self.columns.len();
2450 for (index, column) in self.columns.iter().enumerate() {
2451 let printer = &printer.offset((column_offset, 0)).focused(true);
2452
2453 col_cb(printer, column, index);
2454
2455 if 1 + index < column_count {
2456 printer.print((column.width + 1, 0), sep);
2457 }
2458
2459 column_offset += column.width + 3;
2460 }
2461 }
2462
2463 fn draw_item(&self, printer: &Printer, i: usize) {
2464 let item = &self.items[i];
2465 let focused_row = i == self.focus && self.enabled;
2466 let focused_col = self.focus_col;
2467 self.draw_columns(
2468 printer,
2469 "┆ ",
2470 |printer, row_header| {
2471 let value = item.to_row();
2472 row_header.draw_row(printer, value.as_str());
2473 },
2474 |printer, column, index| {
2475 let color = if focused_row && index == focused_col {
2476 if printer.focused {
2477 theme::ColorStyle::highlight()
2478 } else {
2479 theme::ColorStyle::highlight_inactive()
2480 }
2481 } else {
2482 theme::ColorStyle::primary()
2483 };
2484 let value = item.to_column(column.column);
2485 printer.with_color(color, |printer| {
2486 column.draw_row(printer, value.as_str());
2487 });
2488 },
2489 );
2490 }
2491
2492 fn on_focus_change(&self) -> EventResult {
2493 let row = self.row().unwrap();
2494 let column = match self.columns.get(self.focus_col) {
2495 Some(column) => column.column,
2496 None => return EventResult::Ignored,
2497 };
2498 EventResult::Consumed(
2499 self.on_select
2500 .clone()
2501 .map(|cb| Callback::from_fn(move |s| cb(s, row, column))),
2502 )
2503 }
2504
2505 fn focus_up(&mut self, n: usize) {
2506 self.focus -= cmp::min(self.focus, n);
2507 }
2508
2509 fn focus_down(&mut self, n: usize) {
2510 self.focus = cmp::min(self.focus + n, self.items.len().saturating_sub(1));
2511 }
2512
2513 fn draw_content(&self, printer: &Printer) {
2514 let row_count = self.items.len();
2515 for i in 0..row_count {
2516 let row_y = self.row_y(i);
2517 let printer = printer.offset((0, row_y));
2518 self.draw_item(&printer, i);
2519
2520 self.draw_columns(
2521 &printer.offset((0, 1)).focused(true),
2522 "┼─",
2523 |printer, row_header| {
2524 printer.print_hline((0, 0), row_header.width + 1, "─");
2525 },
2526 |printer, column, _| {
2527 printer.print_hline((0, 0), column.width + 1, "─");
2528 },
2529 );
2530 }
2531 }
2532
2533 fn layout_content(&mut self, size: Vec2) {
2534 let column_count = self.columns.len() + 1;
2535
2536 let (mut sized, mut flexible): (Vec<&mut TableColumn<H>>, Vec<&mut TableColumn<H>>) = self
2538 .columns
2539 .iter_mut()
2540 .partition(|c| c.requested_width.is_some());
2541
2542 let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
2544
2545 let mut remaining_width = available_width;
2547 if let Some(requested_width) = self.row_header.requested_width.as_ref() {
2548 self.row_header.width = match *requested_width {
2549 TableColumnWidth::Percent(width) => cmp::min(
2550 (size.x as f32 / 100.0 * width as f32).ceil() as usize,
2551 remaining_width,
2552 ),
2553 TableColumnWidth::Absolute(width) => width,
2554 };
2555 remaining_width = remaining_width.saturating_sub(self.row_header.width);
2556 }
2557
2558 for column in &mut sized {
2559 column.width = match *column.requested_width.as_ref().unwrap() {
2560 TableColumnWidth::Percent(width) => cmp::min(
2561 (size.x as f32 / 100.0 * width as f32).ceil() as usize,
2562 remaining_width,
2563 ),
2564 TableColumnWidth::Absolute(width) => width,
2565 };
2566 remaining_width = remaining_width.saturating_sub(column.width);
2567 }
2568
2569 let mut remaining_columns = flexible.len();
2571 if self.row_header.requested_width.is_none() {
2572 remaining_columns += 1;
2573 }
2574
2575 let base_width = if remaining_columns > 0 {
2576 remaining_width / remaining_columns
2577 } else {
2578 0
2579 };
2580
2581 if self.row_header.requested_width.is_none() {
2582 self.row_header.width = base_width;
2583 }
2584
2585 for column in &mut flexible {
2586 column.width = base_width;
2587 }
2588
2589 self.needs_relayout = false;
2590 }
2591
2592 fn content_required_size(&mut self, req: Vec2) -> Vec2 {
2593 let rows = self.items.len();
2594 let height = rows.saturating_mul(2);
2595 let separators = self.columns.len().saturating_mul(3);
2596
2597 let resolve_width = |requested: &TableColumnWidth| match *requested {
2598 TableColumnWidth::Percent(width) => {
2599 (req.x as f32 / 100.0 * width as f32).ceil() as usize
2600 }
2601 TableColumnWidth::Absolute(width) => width,
2602 };
2603
2604 let mut width = self
2605 .row_header
2606 .requested_width
2607 .as_ref()
2608 .map(resolve_width)
2609 .unwrap_or(self.row_header.width);
2610
2611 for column in &self.columns {
2612 let col_width = column
2613 .requested_width
2614 .as_ref()
2615 .map(resolve_width)
2616 .unwrap_or(column.width);
2617 width = width.saturating_add(col_width);
2618 }
2619
2620 let width = if width == 0 {
2621 req.x
2622 } else {
2623 width.saturating_add(separators).saturating_add(1)
2624 };
2625
2626 Vec2::new(width, height)
2627 }
2628
2629 fn on_inner_event(&mut self, event: Event) -> EventResult {
2630 let last_focus = (self.focus, self.focus_col);
2631 match event {
2632 Event::Key(Key::Up) if self.focus > 0 => {
2633 self.focus_up(1);
2634 }
2635 Event::Key(Key::Down) if self.focus + 1 < self.items.len() => {
2636 self.focus_down(1);
2637 }
2638 Event::Key(Key::Left) if self.focus_col > 0 => {
2639 self.focus_col -= 1;
2640 }
2641 Event::Key(Key::Right) if self.focus_col + 1 < self.columns.len() => {
2642 self.focus_col += 1;
2643 }
2644 Event::Key(Key::PageUp) => {
2645 self.focus_up(10);
2646 }
2647 Event::Key(Key::PageDown) => {
2648 self.focus_down(10);
2649 }
2650 Event::Key(Key::Home) => {
2651 self.focus = 0;
2652 }
2653 Event::Key(Key::End) => {
2654 self.focus = self.items.len().saturating_sub(1);
2655 }
2656 Event::Key(Key::Enter) => {
2657 if !self.is_empty() && self.on_submit.is_some() {
2658 return self.on_submit_event();
2659 }
2660 }
2661 Event::Mouse {
2662 position,
2663 offset,
2664 event: MouseEvent::Press(MouseButton::Left),
2665 } if !self.is_empty() => {
2666 if let Some(position) = position.checked_sub(offset) {
2667 let row = self.row_for_y(position.y);
2668 let column = self.column_for_x(position.x);
2669 if let (Some(row), Some(column)) = (row, column) {
2670 if row == self.focus && column == self.focus_col {
2671 return self.on_submit_event();
2672 }
2673 if row < self.items.len() {
2674 self.focus = row;
2675 self.focus_col = column;
2676 }
2677 } else {
2678 return EventResult::Ignored;
2679 }
2680 } else {
2681 return EventResult::Ignored;
2682 }
2683 }
2684 Event::Mouse {
2685 position,
2686 offset,
2687 event: MouseEvent::Press(_),
2688 } if !self.is_empty() => {
2689 if let Some(position) = position.checked_sub(offset) {
2690 let row = self.row_for_y(position.y);
2691 let column = self.column_for_x(position.x);
2692 if let (Some(row), Some(column)) = (row, column) {
2693 if row < self.items.len() {
2694 self.focus = row;
2695 self.focus_col = column;
2696 }
2697 } else {
2698 return EventResult::Ignored;
2699 }
2700 } else {
2701 return EventResult::Ignored;
2702 }
2703 }
2704 _ => return EventResult::Ignored,
2705 }
2706
2707 let focus = (self.focus, self.focus_col);
2708
2709 if !self.is_empty() && last_focus != focus {
2710 self.on_focus_change()
2711 } else {
2712 EventResult::Ignored
2713 }
2714 }
2715
2716 fn inner_important_area(&self, size: Vec2) -> Rect {
2717 Rect::from_size((0, self.row_y(self.focus)), (size.x, 1))
2718 }
2719
2720 fn on_submit_event(&mut self) -> EventResult {
2721 if let Some(cb) = &self.on_submit {
2722 let cb = Arc::clone(cb);
2723 let row = self.row().unwrap();
2724 let column = match self.columns.get(self.focus_col) {
2725 Some(column) => column.column,
2726 None => return EventResult::Ignored,
2727 };
2728 return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row, column))));
2729 }
2730 EventResult::Ignored
2731 }
2732}
2733
2734impl<T, H> View for ArrayView<T, H>
2735where
2736 T: ArrayViewItem<H> + Send + Sync + 'static,
2737 H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
2738{
2739 fn required_size(&mut self, req: Vec2) -> Vec2 {
2740 let content = self.content_required_size(req);
2741 Vec2::new(content.x, content.y.saturating_add(2))
2742 }
2743
2744 fn draw(&self, printer: &Printer) {
2745 self.draw_columns(
2746 printer,
2747 "╷ ",
2748 |printer, row_header| {
2749 let title = self.array_name.as_str();
2750 printer.with_color(theme::ColorStyle::primary(), |printer| {
2751 row_header.draw_header(printer, title);
2752 });
2753 },
2754 |printer, column, _| {
2755 printer.with_color(theme::ColorStyle::primary(), |printer| {
2756 column.draw_header(printer, false);
2757 });
2758 },
2759 );
2760
2761 self.draw_columns(
2762 &printer.offset((0, 1)).focused(true),
2763 "┴─",
2764 |printer, row_header| {
2765 printer.print_hline((0, 0), row_header.width + 1, "─");
2766 },
2767 |printer, column, _| {
2768 printer.print_hline((0, 0), column.width + 1, "─");
2769 },
2770 );
2771
2772 let available_height = printer.size.y.saturating_sub(2);
2774 let filled_rows = self.items.len().saturating_mul(2).min(available_height);
2775 for y in 2 + filled_rows..printer.size.y {
2776 self.draw_columns(
2777 &printer.offset((0, y)),
2778 "┆ ",
2779 |_, _| (),
2780 |_, _, _| (),
2781 );
2782 }
2783
2784 let printer = &printer.offset((0, 2)).focused(true);
2785 scroll::draw(self, printer, Self::draw_content);
2786 }
2787
2788 fn layout(&mut self, size: Vec2) {
2789 scroll::layout(
2790 self,
2791 size.saturating_sub((0, 2)),
2792 self.needs_relayout,
2793 Self::layout_content,
2794 Self::content_required_size,
2795 );
2796 }
2797
2798 fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
2799 self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
2800 }
2801
2802 fn on_event(&mut self, event: Event) -> EventResult {
2803 if !self.enabled {
2804 return EventResult::Ignored;
2805 }
2806
2807 match event {
2808 Event::Mouse {
2809 position,
2810 offset,
2811 event: MouseEvent::Press(_),
2812 } if position.checked_sub(offset).map_or(false, |p| p.y < 2) => EventResult::Ignored,
2813 event => scroll::on_event(
2814 self,
2815 event.relativized((0, 2)),
2816 Self::on_inner_event,
2817 Self::inner_important_area,
2818 ),
2819 }
2820 }
2821
2822 fn important_area(&self, size: Vec2) -> Rect {
2823 self.inner_important_area(size.saturating_sub((0, 2))) + (0, 2)
2824 }
2825}
2826
2827pub struct ArrayRowHeader {
2830 alignment: HAlign,
2831 width: usize,
2832 requested_width: Option<TableColumnWidth>,
2833}
2834
2835impl ArrayRowHeader {
2836 pub fn align(mut self, alignment: HAlign) -> Self {
2838 self.alignment = alignment;
2839 self
2840 }
2841
2842 pub fn width(mut self, width: usize) -> Self {
2844 self.requested_width = Some(TableColumnWidth::Absolute(width));
2845 self
2846 }
2847
2848 pub fn width_percent(mut self, width: usize) -> Self {
2851 self.requested_width = Some(TableColumnWidth::Percent(width));
2852 self
2853 }
2854
2855 fn new() -> Self {
2856 Self {
2857 alignment: HAlign::Left,
2858 width: 0,
2859 requested_width: None,
2860 }
2861 }
2862
2863 fn draw_header(&self, printer: &Printer, title: &str) {
2864 let header = match self.alignment {
2865 HAlign::Left => format!("{:<width$}", title, width = self.width),
2866 HAlign::Right => format!("{:>width$}", title, width = self.width),
2867 HAlign::Center => format!("{:^width$}", title, width = self.width),
2868 };
2869
2870 printer.print((0, 0), header.as_str());
2871 }
2872
2873 fn draw_row(&self, printer: &Printer, value: &str) {
2874 let value = match self.alignment {
2875 HAlign::Left => format!("{:<width$} ", value, width = self.width),
2876 HAlign::Right => format!("{:>width$} ", value, width = self.width),
2877 HAlign::Center => format!("{:^width$} ", value, width = self.width),
2878 };
2879
2880 printer.print((0, 0), value.as_str());
2881 }
2882}
2883
2884pub struct TableColumn<H> {
2887 column: H,
2888 title: String,
2889 selected: bool,
2890 alignment: HAlign,
2891 header_alignment: Option<HAlign>,
2892 order: Ordering,
2893 width: usize,
2894 default_order: Ordering,
2895 requested_width: Option<TableColumnWidth>,
2896}
2897
2898enum TableColumnWidth {
2899 Percent(usize),
2900 Absolute(usize),
2901}
2902
2903impl<H: Copy + Clone + 'static> TableColumn<H> {
2904 pub fn ordering(mut self, order: Ordering) -> Self {
2906 self.default_order = order;
2907 self
2908 }
2909
2910 pub fn align(mut self, alignment: HAlign) -> Self {
2912 self.alignment = alignment;
2913 self
2914 }
2915
2916 pub fn header_align(mut self, alignment: HAlign) -> Self {
2918 self.header_alignment = Some(alignment);
2919 self
2920 }
2921
2922 pub fn width(mut self, width: usize) -> Self {
2924 self.requested_width = Some(TableColumnWidth::Absolute(width));
2925 self
2926 }
2927
2928 pub fn width_percent(mut self, width: usize) -> Self {
2931 self.requested_width = Some(TableColumnWidth::Percent(width));
2932 self
2933 }
2934
2935 fn new(column: H, title: String) -> Self {
2936 Self {
2937 column,
2938 title,
2939 selected: false,
2940 alignment: HAlign::Left,
2941 header_alignment: None,
2942 order: Ordering::Equal,
2943 width: 0,
2944 default_order: Ordering::Less,
2945 requested_width: None,
2946 }
2947 }
2948
2949 fn draw_header(&self, printer: &Printer, sortable: bool) {
2950 let title_width = if sortable {
2951 self.width.saturating_sub(4)
2952 } else {
2953 self.width
2954 };
2955 let title = self.title.as_str();
2956
2957 let alignment = self.header_alignment.unwrap_or(self.alignment);
2958 let mut header = match alignment {
2959 HAlign::Left => format!("{:<width$}", title, width = title_width),
2960 HAlign::Right => format!("{:>width$}", title, width = title_width),
2961 HAlign::Center => format!("{:^width$}", title, width = title_width),
2962 };
2963
2964 if sortable {
2965 let order = match self.order {
2966 Ordering::Less => "^",
2967 Ordering::Greater => "v",
2968 Ordering::Equal => " ",
2969 };
2970 header.push_str(" [");
2971 header.push_str(order);
2972 header.push(']');
2973 }
2974
2975 printer.print((0, 0), header.as_str());
2976 }
2977
2978 fn draw_row(&self, printer: &Printer, value: &str) {
2979 let value = match self.alignment {
2980 HAlign::Left => format!("{:<width$} ", value, width = self.width),
2981 HAlign::Right => format!("{:>width$} ", value, width = self.width),
2982 HAlign::Center => format!("{:^width$} ", value, width = self.width),
2983 };
2984
2985 printer.print((0, 0), value.as_str());
2986 }
2987}
2988
2989#[cfg(test)]
2990mod tests {
2991 use super::*;
2992
2993 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
2994 enum SimpleColumn {
2995 Name,
2996 }
2997
2998 #[allow(dead_code)]
2999 impl SimpleColumn {
3000 fn as_str(&self) -> &str {
3001 match *self {
3002 SimpleColumn::Name => "Name",
3003 }
3004 }
3005 }
3006
3007 #[derive(Clone, Debug)]
3008 struct SimpleItem {
3009 name: String,
3010 }
3011
3012 impl TableViewItem<SimpleColumn> for SimpleItem {
3013 fn to_column(&self, column: SimpleColumn) -> String {
3014 match column {
3015 SimpleColumn::Name => self.name.to_string(),
3016 }
3017 }
3018
3019 fn cmp(&self, other: &Self, column: SimpleColumn) -> Ordering
3020 where
3021 Self: Sized,
3022 {
3023 match column {
3024 SimpleColumn::Name => self.name.cmp(&other.name),
3025 }
3026 }
3027 }
3028
3029 fn setup_test_table() -> TableView<SimpleItem, SimpleColumn> {
3030 TableView::<SimpleItem, SimpleColumn>::new()
3031 .column(SimpleColumn::Name, "Name", |c| c.width_percent(20))
3032 }
3033
3034 #[test]
3035 fn should_insert_into_existing_table() {
3036 let mut simple_table = setup_test_table();
3037
3038 let mut simple_items = Vec::new();
3039
3040 for i in 1..=10 {
3041 simple_items.push(SimpleItem {
3042 name: format!("{} - Name", i),
3043 });
3044 }
3045
3046 simple_table.set_items(simple_items);
3048
3049 simple_table.insert_item(SimpleItem {
3051 name: format!("{} Name", 11),
3052 });
3053
3054 assert_eq!(simple_table.len(), 11);
3055 }
3056
3057 #[test]
3058 fn should_insert_into_empty_table() {
3059 let mut simple_table = setup_test_table();
3060
3061 simple_table.insert_item(SimpleItem {
3063 name: format!("{} Name", 1),
3064 });
3065
3066 assert_eq!(simple_table.len(), 1);
3067 }
3068}