better_cursive_table/
lib.rs

1//! A basic table view implementation for [cursive](https://crates.io/crates/cursive).
2#![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
12// Crate Dependencies ---------------------------------------------------------
13use cursive_core as cursive;
14
15// STD Dependencies -----------------------------------------------------------
16use std::cmp::{self, Ordering};
17use std::collections::HashMap;
18use std::hash::Hash;
19use std::sync::Arc;
20
21// External Dependencies ------------------------------------------------------
22use 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
32/// A trait for displaying and sorting items inside a
33/// [`TableView`](struct.TableView.html).
34pub trait TableViewItem<H>: Clone + Sized
35where
36    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
37{
38    /// Method returning a string representation of the item for the
39    /// specified column from type `H`.
40    fn to_column(&self, column: H) -> String;
41
42    /// Method comparing two items via their specified column from type `H`.
43    fn cmp(&self, other: &Self, column: H) -> Ordering
44    where
45        Self: Sized;
46}
47
48/// Controls whether a table selection targets rows or individual cells.
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50pub enum SelectMode {
51    /// Selection is row-based.
52    Row,
53    /// Selection is cell-based.
54    Cell,
55}
56
57/// A trait for displaying items inside an
58/// [`ArrayView`](struct.ArrayView.html).
59pub trait ArrayViewItem<H>: Clone + Sized
60where
61    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
62{
63    /// Method returning a string representation of the item for the
64    /// specified column from type `H`.
65    fn to_column(&self, column: H) -> String;
66
67    /// Method returning a string representation of the item for the row
68    /// header.
69    fn to_row(&self) -> String;
70
71    /// Returns the column definitions used by [`ArrayViewItem::to_array_view`].
72    ///
73    /// The default implementation returns an empty list, creating an
74    /// `ArrayView` with no columns.
75    fn columns() -> Vec<(H, String)> {
76        Vec::new()
77    }
78
79    /// Creates an [`ArrayView`](struct.ArrayView.html) from the provided items.
80    ///
81    /// Columns are populated from [`ArrayViewItem::columns`].
82    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/// A simple row type for building array views from raw headers and data.
96///
97/// Row labels are stored separately and referenced by index.
98#[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
134/// A builder for creating `ArrayView` instances from headers and raw data.
135pub 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    /// Creates a new empty builder.
164    pub fn new() -> Self {
165        Self::default()
166    }
167
168    /// Sets the row header labels.
169    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    /// Sets the row header labels.
175    pub fn row_headers<S: Into<String>>(self, rows: Vec<S>) -> Self {
176        self.row_header(rows)
177    }
178
179    /// Sets the column header labels.
180    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    /// Sets the column header labels.
186    pub fn column_headers<S: Into<String>>(self, columns: Vec<S>) -> Self {
187        self.column_header(columns)
188    }
189
190    /// Sets the row header alignment.
191    pub fn row_header_orientation(mut self, alignment: HAlign) -> Self {
192        self.row_header_alignment = alignment;
193        self
194    }
195
196    /// Sets the column header alignment.
197    pub fn column_header_orientation(mut self, alignment: HAlign) -> Self {
198        self.column_header_alignment = alignment;
199        self
200    }
201
202    /// Sets the data cell alignment.
203    pub fn data_orientation(mut self, alignment: HAlign) -> Self {
204        self.data_alignment = alignment;
205        self
206    }
207
208    /// Sets the top-left array name.
209    pub fn array_name<S: Into<String>>(mut self, name: S) -> Self {
210        self.array_name = Some(name.into());
211        self
212    }
213
214    /// Sets the raw row data.
215    pub fn data(mut self, data: Vec<Vec<T>>) -> Self {
216        self.data = data;
217        self
218    }
219
220    /// Adds a column header.
221    pub fn add_column<S: Into<String>>(mut self, column: S) -> Self {
222        self.column_headers.push(column.into());
223        self
224    }
225
226    /// Adds a column header and appends a default value to each existing row.
227    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    /// Builds the array view using `usize` column keys.
236    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    /// Removes the last row, if any.
243    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    /// Removes the last column, if any.
251    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    /// Builds the array view using `usize` column keys.
261    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/// A simple row type for building table views from raw headers and data.
351#[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
388/// A builder for creating `TableView` instances from headers and raw data.
389pub 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    /// Creates a new empty builder.
414    pub fn new() -> Self {
415        Self::default()
416    }
417
418    /// Sets the column header labels.
419    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    /// Sets the column header labels.
425    pub fn column_headers<S: Into<String>>(self, columns: Vec<S>) -> Self {
426        self.column_header(columns)
427    }
428
429    /// Sets the column header alignment.
430    pub fn column_header_orientation(mut self, alignment: HAlign) -> Self {
431        self.column_header_alignment = alignment;
432        self
433    }
434
435    /// Sets the data cell alignment.
436    pub fn data_orientation(mut self, alignment: HAlign) -> Self {
437        self.data_alignment = alignment;
438        self
439    }
440
441    /// Enables or disables sorting.
442    pub fn sortable(mut self, sortable: bool) -> Self {
443        self.sortable = sortable;
444        self
445    }
446
447    /// Sets the raw row data.
448    pub fn data(mut self, data: Vec<Vec<T>>) -> Self {
449        self.data = data;
450        self
451    }
452
453    /// Adds a column header.
454    pub fn add_column<S: Into<String>>(mut self, column: S) -> Self {
455        self.column_headers.push(column.into());
456        self
457    }
458
459    /// Adds a column header and appends a default value to each existing row.
460    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    /// Adds a row of data.
469    pub fn add_row(mut self, data: Vec<T>) -> Self {
470        self.data.push(data);
471        self
472    }
473
474    /// Removes the last row, if any.
475    pub fn remove_row(mut self) -> Self {
476        self.data.pop();
477        self
478    }
479
480    /// Removes the last column, if any.
481    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    /// Builds the table view using `usize` column keys.
491    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
561/// Callback used when a column is sorted.
562///
563/// It takes the column and the ordering as input.
564///
565/// This is a private type to help readability.
566type OnSortCallback<H> = Arc<dyn Fn(&mut Cursive, H, Ordering) + Send + Sync>;
567
568/// Callback taking as argument the row and the index of an element.
569///
570/// This is a private type to help readability.
571type IndexCallback = Arc<dyn Fn(&mut Cursive, usize, usize) + Send + Sync>;
572
573/// Callback taking as argument the row and the column of an array cell.
574///
575/// This is a private type to help readability.
576type CellCallback<H> = Arc<dyn Fn(&mut Cursive, usize, H) + Send + Sync>;
577
578/// View to select an item among a list, supporting multiple columns for sorting.
579///
580/// # Examples
581///
582/// ```rust
583/// # extern crate cursive;
584/// # extern crate better_cursive_table;
585/// # use std::cmp::Ordering;
586/// # use better_cursive_table::{TableView, TableViewItem};
587/// # use cursive::align::HAlign;
588/// # fn main() {
589/// // Provide a type for the table's columns
590/// #[derive(Copy, Clone, PartialEq, Eq, Hash)]
591/// enum BasicColumn {
592///     Name,
593///     Count,
594///     Rate
595/// }
596///
597/// // Define the item type
598/// #[derive(Clone, Debug)]
599/// struct Foo {
600///     name: String,
601///     count: usize,
602///     rate: usize
603/// }
604///
605/// impl TableViewItem<BasicColumn> for Foo {
606///
607///     fn to_column(&self, column: BasicColumn) -> String {
608///         match column {
609///             BasicColumn::Name => self.name.to_string(),
610///             BasicColumn::Count => format!("{}", self.count),
611///             BasicColumn::Rate => format!("{}", self.rate)
612///         }
613///     }
614///
615///     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
616///         match column {
617///             BasicColumn::Name => self.name.cmp(&other.name),
618///             BasicColumn::Count => self.count.cmp(&other.count),
619///             BasicColumn::Rate => self.rate.cmp(&other.rate)
620///         }
621///     }
622///
623/// }
624///
625/// // Configure the actual table
626/// let table = TableView::<Foo, BasicColumn>::new()
627///                      .column(BasicColumn::Name, "Name", |c| c.width(20))
628///                      .column(BasicColumn::Count, "Count", |c| c.align(HAlign::Center))
629///                      .column(BasicColumn::Rate, "Rate", |c| {
630///                          c.ordering(Ordering::Greater).align(HAlign::Right).width(20)
631///                      })
632///                      .default_column(BasicColumn::Name);
633/// # }
634/// ```
635pub 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 enum -> columns vector index lookup.
644    column_indices: HashMap<H, usize>,
645
646    focus: usize,
647    focus_col: usize,
648    items: Vec<T>,
649    // Row index -> item index after sorting.
650    rows_to_items: Vec<usize>,
651
652    on_sort: Option<OnSortCallback<H>>,
653    // TODO Pass drawing offsets into the handlers so a popup menu
654    // can be created easily?
655    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    /// Creates a new empty `TableView` without any columns.
669    ///
670    /// See [`TableView::new()`].
671    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    /// Sets the contained items of the table.
682    ///
683    /// The currently active sort order is preserved and will be applied to all
684    /// items.
685    ///
686    /// Compared to `set_items`, the current selection will be preserved.
687    /// (But this is only available for `T: PartialEq`.)
688    pub fn set_items_stable(&mut self, items: Vec<T>) {
689        // Preserve selection
690        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    /// Creates a new empty `TableView` without any columns.
708    ///
709    /// A TableView should be accompanied by a enum of type `H` representing
710    /// the table columns.
711    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    /// Adds a column for the specified table column from type `H` along with
736    /// a title for its visual display.
737    ///
738    /// The provided callback can be used to further configure the
739    /// created [`TableColumn`](struct.TableColumn.html).
740    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    /// Adds a column for the specified table column from type `H` along with
751    /// a title for its visual display.
752    ///
753    /// The provided callback can be used to further configure the
754    /// created [`TableColumn`](struct.TableColumn.html).
755    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    /// Remove a column.
765    pub fn remove_column(&mut self, i: usize) {
766        // Update the existing indices
767        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    /// Adds a column for the specified table column from type `H` along with
782    /// a title for its visual display.
783    ///
784    /// The provided callback can be used to further configure the
785    /// created [`TableColumn`](struct.TableColumn.html).
786    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        // Update all existing indices
794        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        // Make the first column the default one
807        if self.columns.len() == 1 {
808            self.set_default_column(column);
809        }
810        self.needs_relayout = true;
811    }
812
813    /// Sets the initially active column of the table.
814    pub fn default_column(mut self, column: H) -> Self {
815        self.set_default_column(column);
816        self
817    }
818
819    /// Sets the initially active column of the table.
820    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    /// Sorts the table using the specified table `column` and the passed
837    /// `order`.
838    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                // Move selection back to the sorted column.
846                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    /// Sorts the table using the currently active column and its
856    /// ordering.
857    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    /// Returns the currently active column that is used for sorting
868    /// along with its ordering.
869    ///
870    /// Might return `None` if there are currently no items in the table
871    /// and it has not been sorted yet.
872    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    /// Disables this view.
882    ///
883    /// A disabled view cannot be selected.
884    pub fn disable(&mut self) {
885        self.enabled = false;
886    }
887
888    /// Re-enables this view.
889    pub fn enable(&mut self) {
890        self.enabled = true;
891    }
892
893    /// Enable or disable this view.
894    pub fn set_enabled(&mut self, enabled: bool) {
895        self.enabled = enabled;
896    }
897
898    /// Enable or disable sorting, header selection, and sort indicators.
899    pub fn set_sortable(&mut self, sortable: bool) {
900        self.sortable = sortable;
901        self.column_select &= sortable;
902    }
903
904    /// Sets the selection mode for this table.
905    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    /// Sets the selection mode for this table.
913    ///
914    /// Chainable variant.
915    pub fn selection_mode(mut self, mode: SelectMode) -> Self {
916        self.set_selection_mode(mode);
917        self
918    }
919
920    /// Enable or disable sorting, header selection, and sort indicators.
921    ///
922    /// Chainable variant.
923    pub fn sortable(self, sortable: bool) -> Self {
924        self.with(|t| t.set_sortable(sortable))
925    }
926
927    /// Returns `true` if sorting is enabled.
928    pub fn is_sortable(&self) -> bool {
929        self.sortable
930    }
931
932    /// Returns `true` if this view is enabled.
933    pub fn is_enabled(&self) -> bool {
934        self.enabled
935    }
936
937    /// Sets a callback to be used when a selected column is sorted by
938    /// pressing `<Enter>`.
939    ///
940    /// # Example
941    ///
942    /// ```no_run
943    /// # use std::cmp::Ordering;
944    /// # use cursive_core::Cursive;
945    /// # use better_cursive_table::{TableView, TableViewItem};
946    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
947    /// # enum BasicColumn { Name }
948    /// # #[derive(Clone)]
949    /// # struct Item { name: String }
950    /// # impl TableViewItem<BasicColumn> for Item {
951    /// #     fn to_column(&self, column: BasicColumn) -> String {
952    /// #         match column { BasicColumn::Name => self.name.clone() }
953    /// #     }
954    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
955    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
956    /// #     }
957    /// # }
958    /// # let mut table = TableView::<Item, BasicColumn>::new()
959    /// #     .column(BasicColumn::Name, "Name", |c| c);
960    /// table.set_on_sort(|_siv: &mut Cursive, _column: BasicColumn, _order: Ordering| {
961    /// });
962    /// ```
963    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    /// Sets a callback to be used when a selected column is sorted by
971    /// pressing `<Enter>`.
972    ///
973    /// Chainable variant.
974    ///
975    /// # Example
976    ///
977    /// ```no_run
978    /// # use std::cmp::Ordering;
979    /// # use cursive_core::Cursive;
980    /// # use better_cursive_table::{TableView, TableViewItem};
981    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
982    /// # enum BasicColumn { Name }
983    /// # #[derive(Clone)]
984    /// # struct Item { name: String }
985    /// # impl TableViewItem<BasicColumn> for Item {
986    /// #     fn to_column(&self, column: BasicColumn) -> String {
987    /// #         match column { BasicColumn::Name => self.name.clone() }
988    /// #     }
989    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
990    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
991    /// #     }
992    /// # }
993    /// # let table = TableView::<Item, BasicColumn>::new()
994    /// #     .column(BasicColumn::Name, "Name", |c| c);
995    /// let _table =
996    ///     table.on_sort(|_siv: &mut Cursive, _column: BasicColumn, _order: Ordering| {});
997    /// ```
998    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    /// Sets a callback to be used when `<Enter>` is pressed while an item
1006    /// is selected.
1007    ///
1008    /// Both the currently selected row and the index of the corresponding item
1009    /// within the underlying storage vector will be given to the callback.
1010    ///
1011    /// # Example
1012    ///
1013    /// ```no_run
1014    /// # use cursive_core::Cursive;
1015    /// # use std::cmp::Ordering;
1016    /// # use better_cursive_table::{TableView, TableViewItem};
1017    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1018    /// # enum BasicColumn { Name }
1019    /// # #[derive(Clone)]
1020    /// # struct Item { name: String }
1021    /// # impl TableViewItem<BasicColumn> for Item {
1022    /// #     fn to_column(&self, column: BasicColumn) -> String {
1023    /// #         match column { BasicColumn::Name => self.name.clone() }
1024    /// #     }
1025    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
1026    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
1027    /// #     }
1028    /// # }
1029    /// # let mut table = TableView::<Item, BasicColumn>::new()
1030    /// #     .column(BasicColumn::Name, "Name", |c| c);
1031    /// table.set_on_submit(|_siv: &mut Cursive, _row: usize, _index: usize| {
1032    /// });
1033    /// ```
1034    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    /// Sets a callback to be used when `<Enter>` is pressed while a cell
1042    /// is selected.
1043    ///
1044    /// The currently selected row and column will be given to the callback.
1045    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    /// Sets a callback to be used when `<Enter>` is pressed while an item
1053    /// is selected.
1054    ///
1055    /// Both the currently selected row and the index of the corresponding item
1056    /// within the underlying storage vector will be given to the callback.
1057    ///
1058    /// Chainable variant.
1059    ///
1060    /// # Example
1061    ///
1062    /// ```no_run
1063    /// # use cursive_core::Cursive;
1064    /// # use std::cmp::Ordering;
1065    /// # use better_cursive_table::{TableView, TableViewItem};
1066    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1067    /// # enum BasicColumn { Name }
1068    /// # #[derive(Clone)]
1069    /// # struct Item { name: String }
1070    /// # impl TableViewItem<BasicColumn> for Item {
1071    /// #     fn to_column(&self, column: BasicColumn) -> String {
1072    /// #         match column { BasicColumn::Name => self.name.clone() }
1073    /// #     }
1074    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
1075    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
1076    /// #     }
1077    /// # }
1078    /// # let table = TableView::<Item, BasicColumn>::new()
1079    /// #     .column(BasicColumn::Name, "Name", |c| c);
1080    /// let _table = table.on_submit(|_siv: &mut Cursive, _row: usize, _index: usize| {});
1081    /// ```
1082    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    /// Sets a callback to be used when `<Enter>` is pressed while a cell
1090    /// is selected.
1091    ///
1092    /// The currently selected row and column will be given to the callback.
1093    ///
1094    /// Chainable variant.
1095    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    /// Sets a callback to be used when an item is selected.
1103    ///
1104    /// Both the currently selected row and the index of the corresponding item
1105    /// within the underlying storage vector will be given to the callback.
1106    ///
1107    /// # Example
1108    ///
1109    /// ```no_run
1110    /// # use cursive_core::Cursive;
1111    /// # use std::cmp::Ordering;
1112    /// # use better_cursive_table::{TableView, TableViewItem};
1113    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1114    /// # enum BasicColumn { Name }
1115    /// # #[derive(Clone)]
1116    /// # struct Item { name: String }
1117    /// # impl TableViewItem<BasicColumn> for Item {
1118    /// #     fn to_column(&self, column: BasicColumn) -> String {
1119    /// #         match column { BasicColumn::Name => self.name.clone() }
1120    /// #     }
1121    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
1122    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
1123    /// #     }
1124    /// # }
1125    /// # let mut table = TableView::<Item, BasicColumn>::new()
1126    /// #     .column(BasicColumn::Name, "Name", |c| c);
1127    /// table.set_on_select(|_siv: &mut Cursive, _row: usize, _index: usize| {
1128    /// });
1129    /// ```
1130    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    /// Sets a callback to be used when an item is selected.
1138    ///
1139    /// Both the currently selected row and the index of the corresponding item
1140    /// within the underlying storage vector will be given to the callback.
1141    ///
1142    /// Chainable variant.
1143    ///
1144    /// # Example
1145    ///
1146    /// ```no_run
1147    /// # use cursive_core::Cursive;
1148    /// # use std::cmp::Ordering;
1149    /// # use better_cursive_table::{TableView, TableViewItem};
1150    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1151    /// # enum BasicColumn { Name }
1152    /// # #[derive(Clone)]
1153    /// # struct Item { name: String }
1154    /// # impl TableViewItem<BasicColumn> for Item {
1155    /// #     fn to_column(&self, column: BasicColumn) -> String {
1156    /// #         match column { BasicColumn::Name => self.name.clone() }
1157    /// #     }
1158    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
1159    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
1160    /// #     }
1161    /// # }
1162    /// # let table = TableView::<Item, BasicColumn>::new()
1163    /// #     .column(BasicColumn::Name, "Name", |c| c);
1164    /// let _table = table.on_select(|_siv: &mut Cursive, _row: usize, _index: usize| {});
1165    /// ```
1166    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    /// Removes all items from this view.
1174    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    /// Returns the number of items in this table.
1182    pub fn len(&self) -> usize {
1183        self.items.len()
1184    }
1185
1186    /// Returns `true` if this table has no items.
1187    pub fn is_empty(&self) -> bool {
1188        self.items.is_empty()
1189    }
1190
1191    /// Returns the index of the currently selected table row.
1192    pub fn row(&self) -> Option<usize> {
1193        if self.items.is_empty() {
1194            None
1195        } else {
1196            Some(self.focus)
1197        }
1198    }
1199
1200    /// Selects the row at the specified index.
1201    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    /// Selects the row at the specified index.
1207    ///
1208    /// Chainable variant.
1209    pub fn selected_row(self, row_index: usize) -> Self {
1210        self.with(|t| t.set_selected_row(row_index))
1211    }
1212
1213    /// Sets the contained items of the table.
1214    ///
1215    /// The currently active sort order is preserved and will be applied to all
1216    /// items.
1217    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            // Preserve the selected column if possible.
1231            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    /// Sets the contained items of the table.
1245    ///
1246    /// The order of the items will be preserved even when the table is sorted.
1247    ///
1248    /// Chainable variant.
1249    pub fn items(self, items: Vec<T>) -> Self {
1250        self.with(|t| t.set_items(items))
1251    }
1252
1253    /// Returns an immutable reference to the item at the specified index
1254    /// within the underlying storage vector.
1255    pub fn borrow_item(&self, index: usize) -> Option<&T> {
1256        self.items.get(index)
1257    }
1258
1259    /// Returns a mutable reference to the item at the specified index within
1260    /// the underlying storage vector.
1261    pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
1262        self.items.get_mut(index)
1263    }
1264
1265    /// Returns an immutable reference to the items contained within the table.
1266    pub fn borrow_items(&self) -> &[T] {
1267        &self.items
1268    }
1269
1270    /// Returns a mutable reference to the items contained within the table.
1271    ///
1272    /// Can be used to modify the items in place.
1273    pub fn borrow_items_mut(&mut self) -> &mut [T] {
1274        self.needs_relayout = true;
1275        &mut self.items
1276    }
1277
1278    /// Returns the index of the currently selected item within the underlying
1279    /// storage vector.
1280    pub fn item(&self) -> Option<usize> {
1281        self.rows_to_items.get(self.focus).copied()
1282    }
1283
1284    /// Selects the item at the specified index within the underlying storage
1285    /// vector.
1286    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    /// Selects the item at the specified index within the underlying storage
1302    /// vector.
1303    ///
1304    /// Chainable variant.
1305    pub fn selected_item(self, item_index: usize) -> Self {
1306        self.with(|t| t.set_selected_item(item_index))
1307    }
1308
1309    /// Inserts a new item into the table.
1310    ///
1311    /// The currently active sort order is preserved and will be applied to the
1312    /// newly inserted item.
1313    ///
1314    /// If no sort option is set, the item will be added to the end of the table.
1315    pub fn insert_item(&mut self, item: T) {
1316        self.insert_item_at(self.items.len(), item);
1317    }
1318
1319    /// Inserts a new item into the table.
1320    ///
1321    /// The currently active sort order is preserved and will be applied to the
1322    /// newly inserted item.
1323    ///
1324    /// If no sort option is set, the item will be inserted at the given index.
1325    ///
1326    /// # Panics
1327    ///
1328    /// If `index > self.len()`.
1329    pub fn insert_item_at(&mut self, index: usize, item: T) {
1330        self.items.push(item);
1331
1332        // Here we know self.items.len() > 0
1333        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    /// Removes the item at the specified index within the underlying storage
1342    /// vector and returns it.
1343    pub fn remove_item(&mut self, item_index: usize) -> Option<T> {
1344        if item_index < self.items.len() {
1345            // Move the selection if the currently selected item gets removed
1346            if let Some(selected_index) = self.item() {
1347                if selected_index == item_index {
1348                    self.focus_up(1);
1349                }
1350            }
1351
1352            // Remove the sorted reference to the item
1353            self.rows_to_items.retain(|i| *i != item_index);
1354
1355            // Adjust remaining references
1356            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            // Remove actual item from the underlying storage
1364            Some(self.items.remove(item_index))
1365        } else {
1366            None
1367        }
1368    }
1369
1370    /// Removes all items from the underlying storage and returns them.
1371    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        // Split up all columns into sized / unsized groups
1582        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        // Subtract one for the separators between our columns (that's column_count - 1)
1588        let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
1589
1590        // Calculate widths for all requested columns
1591        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        // Spread the remaining width across the unsized columns.
1604        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        // Extend the vertical bars to the end of the view.
1844        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
1911/// View to display a 2D array with row and column headers.
1912pub 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    /// Creates a new empty `ArrayView` without any columns.
1937    ///
1938    /// See [`ArrayView::new()`].
1939    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    /// Sets the contained items of the array.
1950    ///
1951    /// The current selection will be preserved when possible.
1952    /// (But this is only available for `T: PartialEq`.)
1953    pub fn set_items_stable(&mut self, items: Vec<T>) {
1954        // Preserve selection
1955        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    /// Creates a new empty `ArrayView` without any columns.
1973    ///
1974    /// An ArrayView should be accompanied by an enum of type `H` representing
1975    /// the array columns.
1976    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    /// Configures the row header column.
1996    ///
1997    /// Row header values are read from [`ArrayViewItem::to_row`].
1998    /// The top-left corner text is set via [`ArrayView::array_name`].
1999    ///
2000    /// Chainable variant.
2001    pub fn row_header<C: FnOnce(ArrayRowHeader) -> ArrayRowHeader>(mut self, callback: C) -> Self {
2002        self.set_row_header(callback);
2003        self
2004    }
2005
2006    /// Sets the text shown in the top-left corner.
2007    ///
2008    /// Chainable variant.
2009    pub fn array_name<S: Into<String>>(mut self, name: S) -> Self {
2010        self.set_array_name(name);
2011        self
2012    }
2013
2014    /// Sets the text shown in the top-left corner.
2015    pub fn set_array_name<S: Into<String>>(&mut self, name: S) {
2016        self.array_name = name.into();
2017    }
2018
2019    /// Configures the row header column.
2020    ///
2021    /// Row header values are read from [`ArrayViewItem::to_row`].
2022    /// The top-left corner text is set via [`ArrayView::array_name`].
2023    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    /// Adds a column for the specified array column from type `H` along with
2032    /// a title for its visual display.
2033    ///
2034    /// The provided callback can be used to further configure the
2035    /// created [`TableColumn`](struct.TableColumn.html).
2036    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    /// Adds a column for the specified array column from type `H` along with
2047    /// a title for its visual display.
2048    ///
2049    /// The provided callback can be used to further configure the
2050    /// created [`TableColumn`](struct.TableColumn.html).
2051    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    /// Remove a column.
2061    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    /// Adds a column for the specified array column from type `H` along with
2070    /// a title for its visual display.
2071    ///
2072    /// The provided callback can be used to further configure the
2073    /// created [`TableColumn`](struct.TableColumn.html).
2074    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    /// Disables this view.
2090    ///
2091    /// A disabled view cannot be selected.
2092    pub fn disable(&mut self) {
2093        self.enabled = false;
2094    }
2095
2096    /// Re-enables this view.
2097    pub fn enable(&mut self) {
2098        self.enabled = true;
2099    }
2100
2101    /// Enable or disable this view.
2102    pub fn set_enabled(&mut self, enabled: bool) {
2103        self.enabled = enabled;
2104    }
2105
2106    /// Returns `true` if this view is enabled.
2107    pub fn is_enabled(&self) -> bool {
2108        self.enabled
2109    }
2110
2111    /// Sets a callback to be used when `<Enter>` is pressed while a cell
2112    /// is selected.
2113    ///
2114    /// Both the currently selected row and column will be given to the callback.
2115    ///
2116    /// # Example
2117    ///
2118    /// ```no_run
2119    /// # use cursive_core::Cursive;
2120    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
2121    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
2122    /// # enum ArrayColumn { X }
2123    /// # #[derive(Clone)]
2124    /// # struct Item { label: String, value: String }
2125    /// # impl ArrayViewItem<ArrayColumn> for Item {
2126    /// #     fn to_column(&self, column: ArrayColumn) -> String {
2127    /// #         match column { ArrayColumn::X => self.value.clone() }
2128    /// #     }
2129    /// #     fn to_row(&self) -> String {
2130    /// #         self.label.clone()
2131    /// #     }
2132    /// # }
2133    /// # let mut array = ArrayView::<Item, ArrayColumn>::new()
2134    /// #     .row_header(|h| h.width(6))
2135    /// #     .column(ArrayColumn::X, "X", |c| c);
2136    /// array.set_on_submit(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {
2137    /// });
2138    /// ```
2139    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    /// Sets a callback to be used when `<Enter>` is pressed while a cell
2147    /// is selected.
2148    ///
2149    /// Chainable variant.
2150    ///
2151    /// # Example
2152    ///
2153    /// ```no_run
2154    /// # use cursive_core::Cursive;
2155    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
2156    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
2157    /// # enum ArrayColumn { X }
2158    /// # #[derive(Clone)]
2159    /// # struct Item { label: String, value: String }
2160    /// # impl ArrayViewItem<ArrayColumn> for Item {
2161    /// #     fn to_column(&self, column: ArrayColumn) -> String {
2162    /// #         match column { ArrayColumn::X => self.value.clone() }
2163    /// #     }
2164    /// #     fn to_row(&self) -> String {
2165    /// #         self.label.clone()
2166    /// #     }
2167    /// # }
2168    /// # let array = ArrayView::<Item, ArrayColumn>::new()
2169    /// #     .row_header(|h| h.width(6))
2170    /// #     .column(ArrayColumn::X, "X", |c| c);
2171    /// let _array = array.on_submit(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {});
2172    /// ```
2173    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    /// Sets a callback to be used when a cell is selected.
2181    ///
2182    /// Both the currently selected row and column will be given to the callback.
2183    ///
2184    /// # Example
2185    ///
2186    /// ```no_run
2187    /// # use cursive_core::Cursive;
2188    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
2189    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
2190    /// # enum ArrayColumn { X }
2191    /// # #[derive(Clone)]
2192    /// # struct Item { label: String, value: String }
2193    /// # impl ArrayViewItem<ArrayColumn> for Item {
2194    /// #     fn to_column(&self, column: ArrayColumn) -> String {
2195    /// #         match column { ArrayColumn::X => self.value.clone() }
2196    /// #     }
2197    /// #     fn to_row(&self) -> String {
2198    /// #         self.label.clone()
2199    /// #     }
2200    /// # }
2201    /// # let mut array = ArrayView::<Item, ArrayColumn>::new()
2202    /// #     .row_header(|h| h.width(6))
2203    /// #     .column(ArrayColumn::X, "X", |c| c);
2204    /// array.set_on_select(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {
2205    /// });
2206    /// ```
2207    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    /// Sets a callback to be used when a cell is selected.
2215    ///
2216    /// Chainable variant.
2217    ///
2218    /// # Example
2219    ///
2220    /// ```no_run
2221    /// # use cursive_core::Cursive;
2222    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
2223    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
2224    /// # enum ArrayColumn { X }
2225    /// # #[derive(Clone)]
2226    /// # struct Item { label: String, value: String }
2227    /// # impl ArrayViewItem<ArrayColumn> for Item {
2228    /// #     fn to_column(&self, column: ArrayColumn) -> String {
2229    /// #         match column { ArrayColumn::X => self.value.clone() }
2230    /// #     }
2231    /// #     fn to_row(&self) -> String {
2232    /// #         self.label.clone()
2233    /// #     }
2234    /// # }
2235    /// # let array = ArrayView::<Item, ArrayColumn>::new()
2236    /// #     .row_header(|h| h.width(6))
2237    /// #     .column(ArrayColumn::X, "X", |c| c);
2238    /// let _array = array.on_select(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {});
2239    /// ```
2240    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    /// Removes all items from this view.
2248    pub fn clear(&mut self) {
2249        self.items.clear();
2250        self.focus = 0;
2251        self.needs_relayout = true;
2252    }
2253
2254    /// Returns the number of items in this array.
2255    pub fn len(&self) -> usize {
2256        self.items.len()
2257    }
2258
2259    /// Returns `true` if this array has no items.
2260    pub fn is_empty(&self) -> bool {
2261        self.items.is_empty()
2262    }
2263
2264    /// Returns the index of the currently selected array row.
2265    pub fn row(&self) -> Option<usize> {
2266        if self.items.is_empty() {
2267            None
2268        } else {
2269            Some(self.focus)
2270        }
2271    }
2272
2273    /// Selects the row at the specified index.
2274    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    /// Selects the row at the specified index.
2280    ///
2281    /// Chainable variant.
2282    pub fn selected_row(self, row_index: usize) -> Self {
2283        self.with(|t| t.set_selected_row(row_index))
2284    }
2285
2286    /// Sets the contained items of the array.
2287    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    /// Sets the contained items of the array.
2298    ///
2299    /// Chainable variant.
2300    pub fn items(self, items: Vec<T>) -> Self {
2301        self.with(|t| t.set_items(items))
2302    }
2303
2304    /// Returns an immutable reference to the item at the specified index
2305    /// within the underlying storage vector.
2306    pub fn borrow_item(&self, index: usize) -> Option<&T> {
2307        self.items.get(index)
2308    }
2309
2310    /// Returns a mutable reference to the item at the specified index within
2311    /// the underlying storage vector.
2312    pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
2313        self.items.get_mut(index)
2314    }
2315
2316    /// Returns an immutable reference to the items contained within the array.
2317    pub fn borrow_items(&self) -> &[T] {
2318        &self.items
2319    }
2320
2321    /// Returns a mutable reference to the items contained within the array.
2322    ///
2323    /// Can be used to modify the items in place.
2324    pub fn borrow_items_mut(&mut self) -> &mut [T] {
2325        self.needs_relayout = true;
2326        &mut self.items
2327    }
2328
2329    /// Returns the index of the currently selected item within the underlying
2330    /// storage vector.
2331    pub fn item(&self) -> Option<usize> {
2332        if self.items.is_empty() {
2333            None
2334        } else {
2335            Some(self.focus)
2336        }
2337    }
2338
2339    /// Selects the item at the specified index within the underlying storage
2340    /// vector.
2341    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    /// Selects the item at the specified index within the underlying storage
2349    /// vector.
2350    ///
2351    /// Chainable variant.
2352    pub fn selected_item(self, item_index: usize) -> Self {
2353        self.with(|t| t.set_selected_item(item_index))
2354    }
2355
2356    /// Inserts a new item into the array.
2357    ///
2358    /// The item will be added to the end of the array.
2359    pub fn insert_item(&mut self, item: T) {
2360        self.insert_item_at(self.items.len(), item);
2361    }
2362
2363    /// Inserts a new item into the array.
2364    ///
2365    /// The item will be inserted at the given index.
2366    ///
2367    /// # Panics
2368    ///
2369    /// If `index > self.len()`.
2370    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    /// Removes the item at the specified index within the underlying storage
2376    /// vector and returns it.
2377    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    /// Removes all items from the underlying storage and returns them.
2393    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        // Split up all columns into sized / unsized groups
2537        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        // Subtract one for the separators between our columns (that's column_count - 1)
2543        let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
2544
2545        // Calculate widths for all requested columns
2546        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        // Spread the remaining width across the unsized columns.
2570        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        // Extend the vertical bars to the end of the view.
2773        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
2827/// A type used for the construction of the row header in an
2828/// [`ArrayView`](struct.ArrayView.html).
2829pub struct ArrayRowHeader {
2830    alignment: HAlign,
2831    width: usize,
2832    requested_width: Option<TableColumnWidth>,
2833}
2834
2835impl ArrayRowHeader {
2836    /// Sets the horizontal text alignment of the row header.
2837    pub fn align(mut self, alignment: HAlign) -> Self {
2838        self.alignment = alignment;
2839        self
2840    }
2841
2842    /// Sets how many characters of width this row header will try to occupy.
2843    pub fn width(mut self, width: usize) -> Self {
2844        self.requested_width = Some(TableColumnWidth::Absolute(width));
2845        self
2846    }
2847
2848    /// Sets what percentage of the width of the entire array this row header
2849    /// will try to occupy.
2850    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
2884/// A type used for the construction of columns in a
2885/// [`TableView`](struct.TableView.html).
2886pub 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    /// Sets the default ordering of the column.
2905    pub fn ordering(mut self, order: Ordering) -> Self {
2906        self.default_order = order;
2907        self
2908    }
2909
2910    /// Sets the horizontal text alignment of the column.
2911    pub fn align(mut self, alignment: HAlign) -> Self {
2912        self.alignment = alignment;
2913        self
2914    }
2915
2916    /// Sets the horizontal text alignment of the column header.
2917    pub fn header_align(mut self, alignment: HAlign) -> Self {
2918        self.header_alignment = Some(alignment);
2919        self
2920    }
2921
2922    /// Sets how many characters of width this column will try to occupy.
2923    pub fn width(mut self, width: usize) -> Self {
2924        self.requested_width = Some(TableColumnWidth::Absolute(width));
2925        self
2926    }
2927
2928    /// Sets what percentage of the width of the entire table this column will
2929    /// try to occupy.
2930    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        // Insert First Batch of Items
3047        simple_table.set_items(simple_items);
3048
3049        // Test for Additional item insertion
3050        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        // Test for First item insertion
3062        simple_table.insert_item(SimpleItem {
3063            name: format!("{} Name", 1),
3064        });
3065
3066        assert_eq!(simple_table.len(), 1);
3067    }
3068}