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 ---------------------------------------------------------
13extern crate 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/// A trait for displaying items inside an
49/// [`ArrayView`](struct.ArrayView.html).
50pub trait ArrayViewItem<H>: Clone + Sized
51where
52    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
53{
54    /// Method returning a string representation of the item for the
55    /// specified column from type `H`.
56    fn to_column(&self, column: H) -> String;
57
58    /// Method returning a string representation of the item for the row
59    /// header.
60    fn to_row(&self) -> String;
61}
62
63/// Callback used when a column is sorted.
64///
65/// It takes the column and the ordering as input.
66///
67/// This is a private type to help readability.
68type OnSortCallback<H> = Arc<dyn Fn(&mut Cursive, H, Ordering) + Send + Sync>;
69
70/// Callback taking as argument the row and the index of an element.
71///
72/// This is a private type to help readability.
73type IndexCallback = Arc<dyn Fn(&mut Cursive, usize, usize) + Send + Sync>;
74
75/// Callback taking as argument the row and the column of an array cell.
76///
77/// This is a private type to help readability.
78type CellCallback<H> = Arc<dyn Fn(&mut Cursive, usize, H) + Send + Sync>;
79
80/// View to select an item among a list, supporting multiple columns for sorting.
81///
82/// # Examples
83///
84/// ```rust
85/// # extern crate cursive;
86/// # extern crate better_cursive_table;
87/// # use std::cmp::Ordering;
88/// # use better_cursive_table::{TableView, TableViewItem};
89/// # use cursive::align::HAlign;
90/// # fn main() {
91/// // Provide a type for the table's columns
92/// #[derive(Copy, Clone, PartialEq, Eq, Hash)]
93/// enum BasicColumn {
94///     Name,
95///     Count,
96///     Rate
97/// }
98///
99/// // Define the item type
100/// #[derive(Clone, Debug)]
101/// struct Foo {
102///     name: String,
103///     count: usize,
104///     rate: usize
105/// }
106///
107/// impl TableViewItem<BasicColumn> for Foo {
108///
109///     fn to_column(&self, column: BasicColumn) -> String {
110///         match column {
111///             BasicColumn::Name => self.name.to_string(),
112///             BasicColumn::Count => format!("{}", self.count),
113///             BasicColumn::Rate => format!("{}", self.rate)
114///         }
115///     }
116///
117///     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
118///         match column {
119///             BasicColumn::Name => self.name.cmp(&other.name),
120///             BasicColumn::Count => self.count.cmp(&other.count),
121///             BasicColumn::Rate => self.rate.cmp(&other.rate)
122///         }
123///     }
124///
125/// }
126///
127/// // Configure the actual table
128/// let table = TableView::<Foo, BasicColumn>::new()
129///                      .column(BasicColumn::Name, "Name", |c| c.width(20))
130///                      .column(BasicColumn::Count, "Count", |c| c.align(HAlign::Center))
131///                      .column(BasicColumn::Rate, "Rate", |c| {
132///                          c.ordering(Ordering::Greater).align(HAlign::Right).width(20)
133///                      })
134///                      .default_column(BasicColumn::Name);
135/// # }
136/// ```
137pub struct TableView<T, H> {
138    enabled: bool,
139    sortable: bool,
140    scroll_core: scroll::Core,
141    needs_relayout: bool,
142
143    column_select: bool,
144    columns: Vec<TableColumn<H>>,
145    // Column enum -> columns vector index lookup.
146    column_indices: HashMap<H, usize>,
147
148    focus: usize,
149    items: Vec<T>,
150    // Row index -> item index after sorting.
151    rows_to_items: Vec<usize>,
152
153    on_sort: Option<OnSortCallback<H>>,
154    // TODO Pass drawing offsets into the handlers so a popup menu
155    // can be created easily?
156    on_submit: Option<IndexCallback>,
157    on_select: Option<IndexCallback>,
158}
159
160cursive::impl_scroller!(TableView < T, H > ::scroll_core);
161
162impl<T, H> Default for TableView<T, H>
163where
164    T: TableViewItem<H> + PartialEq,
165    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
166{
167    /// Creates a new empty `TableView` without any columns.
168    ///
169    /// See [`TableView::new()`].
170    fn default() -> Self {
171        Self::new()
172    }
173}
174
175impl<T, H> TableView<T, H>
176where
177    T: TableViewItem<H> + PartialEq,
178    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
179{
180    /// Sets the contained items of the table.
181    ///
182    /// The currently active sort order is preserved and will be applied to all
183    /// items.
184    ///
185    /// Compared to `set_items`, the current selection will be preserved.
186    /// (But this is only available for `T: PartialEq`.)
187    pub fn set_items_stable(&mut self, items: Vec<T>) {
188        // Preserve selection
189        let new_location = self
190            .item()
191            .and_then(|old_item| {
192                let old_item = &self.items[old_item];
193                items.iter().position(|new| new == old_item)
194            })
195            .unwrap_or(0);
196
197        self.set_items_and_focus(items, new_location);
198    }
199}
200
201impl<T, H> TableView<T, H>
202where
203    T: TableViewItem<H>,
204    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
205{
206    /// Creates a new empty `TableView` without any columns.
207    ///
208    /// A TableView should be accompanied by a enum of type `H` representing
209    /// the table columns.
210    pub fn new() -> Self {
211        Self {
212            enabled: true,
213            sortable: true,
214            scroll_core: scroll::Core::new(),
215            needs_relayout: true,
216
217            column_select: false,
218            columns: Vec::new(),
219            column_indices: HashMap::new(),
220
221            focus: 0,
222            items: Vec::new(),
223            rows_to_items: Vec::new(),
224
225            on_sort: None,
226            on_submit: None,
227            on_select: None,
228        }
229    }
230
231    /// Adds a column for the specified table column from type `H` along with
232    /// a title for its visual display.
233    ///
234    /// The provided callback can be used to further configure the
235    /// created [`TableColumn`](struct.TableColumn.html).
236    pub fn column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
237        mut self,
238        column: H,
239        title: S,
240        callback: C,
241    ) -> Self {
242        self.add_column(column, title, callback);
243        self
244    }
245
246    /// Adds a column for the specified table column from type `H` along with
247    /// a title for its visual display.
248    ///
249    /// The provided callback can be used to further configure the
250    /// created [`TableColumn`](struct.TableColumn.html).
251    pub fn add_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
252        &mut self,
253        column: H,
254        title: S,
255        callback: C,
256    ) {
257        self.insert_column(self.columns.len(), column, title, callback);
258    }
259
260    /// Remove a column.
261    pub fn remove_column(&mut self, i: usize) {
262        // Update the existing indices
263        for column in &self.columns[i + 1..] {
264            *self.column_indices.get_mut(&column.column).unwrap() -= 1;
265        }
266
267        let column = self.columns.remove(i);
268        self.column_indices.remove(&column.column);
269        self.needs_relayout = true;
270    }
271
272    /// Adds a column for the specified table column from type `H` along with
273    /// a title for its visual display.
274    ///
275    /// The provided callback can be used to further configure the
276    /// created [`TableColumn`](struct.TableColumn.html).
277    pub fn insert_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
278        &mut self,
279        i: usize,
280        column: H,
281        title: S,
282        callback: C,
283    ) {
284        // Update all existing indices
285        for column in &self.columns[i..] {
286            *self.column_indices.get_mut(&column.column).unwrap() += 1;
287        }
288
289        self.column_indices.insert(column, i);
290        self.columns
291            .insert(i, callback(TableColumn::new(column, title.into())));
292
293        // Make the first column the default one
294        if self.columns.len() == 1 {
295            self.set_default_column(column);
296        }
297        self.needs_relayout = true;
298    }
299
300    /// Sets the initially active column of the table.
301    pub fn default_column(mut self, column: H) -> Self {
302        self.set_default_column(column);
303        self
304    }
305
306    /// Sets the initially active column of the table.
307    pub fn set_default_column(&mut self, column: H) {
308        if self.column_indices.contains_key(&column) {
309            for c in &mut self.columns {
310                c.selected = c.column == column;
311                if c.selected {
312                    c.order = c.default_order;
313                } else {
314                    c.order = Ordering::Equal;
315                }
316            }
317        }
318    }
319
320    /// Sorts the table using the specified table `column` and the passed
321    /// `order`.
322    pub fn sort_by(&mut self, column: H, order: Ordering) {
323        if !self.sortable {
324            return;
325        }
326
327        if self.column_indices.contains_key(&column) {
328            for c in &mut self.columns {
329                // Move selection back to the sorted column.
330                let selected = c.column == column;
331                c.selected = selected;
332                c.order = if selected { order } else { Ordering::Equal };
333            }
334        }
335
336        self.sort_items(column, order);
337    }
338
339    /// Sorts the table using the currently active column and its
340    /// ordering.
341    pub fn sort(&mut self) {
342        if !self.sortable || self.items.len() <= 1 {
343            return;
344        }
345
346        if let Some((column, order)) = self.order() {
347            self.sort_items(column, order);
348        }
349    }
350
351    /// Returns the currently active column that is used for sorting
352    /// along with its ordering.
353    ///
354    /// Might return `None` if there are currently no items in the table
355    /// and it has not been sorted yet.
356    pub fn order(&self) -> Option<(H, Ordering)> {
357        for c in &self.columns {
358            if c.order != Ordering::Equal {
359                return Some((c.column, c.order));
360            }
361        }
362        None
363    }
364
365    /// Disables this view.
366    ///
367    /// A disabled view cannot be selected.
368    pub fn disable(&mut self) {
369        self.enabled = false;
370    }
371
372    /// Re-enables this view.
373    pub fn enable(&mut self) {
374        self.enabled = true;
375    }
376
377    /// Enable or disable this view.
378    pub fn set_enabled(&mut self, enabled: bool) {
379        self.enabled = enabled;
380    }
381
382    /// Enable or disable sorting, header selection, and sort indicators.
383    pub fn set_sortable(&mut self, sortable: bool) {
384        self.sortable = sortable;
385        self.column_select &= sortable;
386    }
387
388    /// Enable or disable sorting, header selection, and sort indicators.
389    ///
390    /// Chainable variant.
391    pub fn sortable(self, sortable: bool) -> Self {
392        self.with(|t| t.set_sortable(sortable))
393    }
394
395    /// Returns `true` if sorting is enabled.
396    pub fn is_sortable(&self) -> bool {
397        self.sortable
398    }
399
400    /// Returns `true` if this view is enabled.
401    pub fn is_enabled(&self) -> bool {
402        self.enabled
403    }
404
405    /// Sets a callback to be used when a selected column is sorted by
406    /// pressing `<Enter>`.
407    ///
408    /// # Example
409    ///
410    /// ```no_run
411    /// # use std::cmp::Ordering;
412    /// # use cursive_core::Cursive;
413    /// # use better_cursive_table::{TableView, TableViewItem};
414    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
415    /// # enum BasicColumn { Name }
416    /// # #[derive(Clone)]
417    /// # struct Item { name: String }
418    /// # impl TableViewItem<BasicColumn> for Item {
419    /// #     fn to_column(&self, column: BasicColumn) -> String {
420    /// #         match column { BasicColumn::Name => self.name.clone() }
421    /// #     }
422    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
423    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
424    /// #     }
425    /// # }
426    /// # let mut table = TableView::<Item, BasicColumn>::new()
427    /// #     .column(BasicColumn::Name, "Name", |c| c);
428    /// table.set_on_sort(|_siv: &mut Cursive, _column: BasicColumn, _order: Ordering| {
429    /// });
430    /// ```
431    pub fn set_on_sort<F>(&mut self, cb: F)
432    where
433        F: Fn(&mut Cursive, H, Ordering) + Send + Sync + 'static,
434    {
435        self.on_sort = Some(Arc::new(move |s, h, o| cb(s, h, o)));
436    }
437
438    /// Sets a callback to be used when a selected column is sorted by
439    /// pressing `<Enter>`.
440    ///
441    /// Chainable variant.
442    ///
443    /// # Example
444    ///
445    /// ```no_run
446    /// # use std::cmp::Ordering;
447    /// # use cursive_core::Cursive;
448    /// # use better_cursive_table::{TableView, TableViewItem};
449    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
450    /// # enum BasicColumn { Name }
451    /// # #[derive(Clone)]
452    /// # struct Item { name: String }
453    /// # impl TableViewItem<BasicColumn> for Item {
454    /// #     fn to_column(&self, column: BasicColumn) -> String {
455    /// #         match column { BasicColumn::Name => self.name.clone() }
456    /// #     }
457    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
458    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
459    /// #     }
460    /// # }
461    /// # let table = TableView::<Item, BasicColumn>::new()
462    /// #     .column(BasicColumn::Name, "Name", |c| c);
463    /// let _table =
464    ///     table.on_sort(|_siv: &mut Cursive, _column: BasicColumn, _order: Ordering| {});
465    /// ```
466    pub fn on_sort<F>(self, cb: F) -> Self
467    where
468        F: Fn(&mut Cursive, H, Ordering) + Send + Sync + 'static,
469    {
470        self.with(|t| t.set_on_sort(cb))
471    }
472
473    /// Sets a callback to be used when `<Enter>` is pressed while an item
474    /// is selected.
475    ///
476    /// Both the currently selected row and the index of the corresponding item
477    /// within the underlying storage vector will be given to the callback.
478    ///
479    /// # Example
480    ///
481    /// ```no_run
482    /// # use cursive_core::Cursive;
483    /// # use std::cmp::Ordering;
484    /// # use better_cursive_table::{TableView, TableViewItem};
485    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
486    /// # enum BasicColumn { Name }
487    /// # #[derive(Clone)]
488    /// # struct Item { name: String }
489    /// # impl TableViewItem<BasicColumn> for Item {
490    /// #     fn to_column(&self, column: BasicColumn) -> String {
491    /// #         match column { BasicColumn::Name => self.name.clone() }
492    /// #     }
493    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
494    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
495    /// #     }
496    /// # }
497    /// # let mut table = TableView::<Item, BasicColumn>::new()
498    /// #     .column(BasicColumn::Name, "Name", |c| c);
499    /// table.set_on_submit(|_siv: &mut Cursive, _row: usize, _index: usize| {
500    /// });
501    /// ```
502    pub fn set_on_submit<F>(&mut self, cb: F)
503    where
504        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
505    {
506        self.on_submit = Some(Arc::new(move |s, row, index| cb(s, row, index)));
507    }
508
509    /// Sets a callback to be used when `<Enter>` is pressed while an item
510    /// is selected.
511    ///
512    /// Both the currently selected row and the index of the corresponding item
513    /// within the underlying storage vector will be given to the callback.
514    ///
515    /// Chainable variant.
516    ///
517    /// # Example
518    ///
519    /// ```no_run
520    /// # use cursive_core::Cursive;
521    /// # use std::cmp::Ordering;
522    /// # use better_cursive_table::{TableView, TableViewItem};
523    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
524    /// # enum BasicColumn { Name }
525    /// # #[derive(Clone)]
526    /// # struct Item { name: String }
527    /// # impl TableViewItem<BasicColumn> for Item {
528    /// #     fn to_column(&self, column: BasicColumn) -> String {
529    /// #         match column { BasicColumn::Name => self.name.clone() }
530    /// #     }
531    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
532    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
533    /// #     }
534    /// # }
535    /// # let table = TableView::<Item, BasicColumn>::new()
536    /// #     .column(BasicColumn::Name, "Name", |c| c);
537    /// let _table = table.on_submit(|_siv: &mut Cursive, _row: usize, _index: usize| {});
538    /// ```
539    pub fn on_submit<F>(self, cb: F) -> Self
540    where
541        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
542    {
543        self.with(|t| t.set_on_submit(cb))
544    }
545
546    /// Sets a callback to be used when an item is selected.
547    ///
548    /// Both the currently selected row and the index of the corresponding item
549    /// within the underlying storage vector will be given to the callback.
550    ///
551    /// # Example
552    ///
553    /// ```no_run
554    /// # use cursive_core::Cursive;
555    /// # use std::cmp::Ordering;
556    /// # use better_cursive_table::{TableView, TableViewItem};
557    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
558    /// # enum BasicColumn { Name }
559    /// # #[derive(Clone)]
560    /// # struct Item { name: String }
561    /// # impl TableViewItem<BasicColumn> for Item {
562    /// #     fn to_column(&self, column: BasicColumn) -> String {
563    /// #         match column { BasicColumn::Name => self.name.clone() }
564    /// #     }
565    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
566    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
567    /// #     }
568    /// # }
569    /// # let mut table = TableView::<Item, BasicColumn>::new()
570    /// #     .column(BasicColumn::Name, "Name", |c| c);
571    /// table.set_on_select(|_siv: &mut Cursive, _row: usize, _index: usize| {
572    /// });
573    /// ```
574    pub fn set_on_select<F>(&mut self, cb: F)
575    where
576        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
577    {
578        self.on_select = Some(Arc::new(move |s, row, index| cb(s, row, index)));
579    }
580
581    /// Sets a callback to be used when an item is selected.
582    ///
583    /// Both the currently selected row and the index of the corresponding item
584    /// within the underlying storage vector will be given to the callback.
585    ///
586    /// Chainable variant.
587    ///
588    /// # Example
589    ///
590    /// ```no_run
591    /// # use cursive_core::Cursive;
592    /// # use std::cmp::Ordering;
593    /// # use better_cursive_table::{TableView, TableViewItem};
594    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
595    /// # enum BasicColumn { Name }
596    /// # #[derive(Clone)]
597    /// # struct Item { name: String }
598    /// # impl TableViewItem<BasicColumn> for Item {
599    /// #     fn to_column(&self, column: BasicColumn) -> String {
600    /// #         match column { BasicColumn::Name => self.name.clone() }
601    /// #     }
602    /// #     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
603    /// #         match column { BasicColumn::Name => self.name.cmp(&other.name) }
604    /// #     }
605    /// # }
606    /// # let table = TableView::<Item, BasicColumn>::new()
607    /// #     .column(BasicColumn::Name, "Name", |c| c);
608    /// let _table = table.on_select(|_siv: &mut Cursive, _row: usize, _index: usize| {});
609    /// ```
610    pub fn on_select<F>(self, cb: F) -> Self
611    where
612        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
613    {
614        self.with(|t| t.set_on_select(cb))
615    }
616
617    /// Removes all items from this view.
618    pub fn clear(&mut self) {
619        self.items.clear();
620        self.rows_to_items.clear();
621        self.focus = 0;
622        self.needs_relayout = true;
623    }
624
625    /// Returns the number of items in this table.
626    pub fn len(&self) -> usize {
627        self.items.len()
628    }
629
630    /// Returns `true` if this table has no items.
631    pub fn is_empty(&self) -> bool {
632        self.items.is_empty()
633    }
634
635    /// Returns the index of the currently selected table row.
636    pub fn row(&self) -> Option<usize> {
637        if self.items.is_empty() {
638            None
639        } else {
640            Some(self.focus)
641        }
642    }
643
644    /// Selects the row at the specified index.
645    pub fn set_selected_row(&mut self, row_index: usize) {
646        self.focus = row_index;
647        self.scroll_core.scroll_to_y(row_index);
648    }
649
650    /// Selects the row at the specified index.
651    ///
652    /// Chainable variant.
653    pub fn selected_row(self, row_index: usize) -> Self {
654        self.with(|t| t.set_selected_row(row_index))
655    }
656
657    /// Sets the contained items of the table.
658    ///
659    /// The currently active sort order is preserved and will be applied to all
660    /// items.
661    pub fn set_items(&mut self, items: Vec<T>) {
662        self.set_items_and_focus(items, 0);
663    }
664
665    fn set_items_and_focus(&mut self, items: Vec<T>, new_location: usize) {
666        self.items = items;
667        self.rows_to_items = Vec::with_capacity(self.items.len());
668
669        for i in 0..self.items.len() {
670            self.rows_to_items.push(i);
671        }
672
673        if let Some((column, order)) = self.order() {
674            // Preserve the selected column if possible.
675            let selected_column = self.columns.iter().find(|c| c.selected).map(|c| c.column);
676            self.sort_by(column, order);
677            if let Some(column) = selected_column {
678                for c in &mut self.columns {
679                    c.selected = c.column == column;
680                }
681            }
682        }
683
684        self.set_selected_item(new_location);
685        self.needs_relayout = true;
686    }
687
688    /// Sets the contained items of the table.
689    ///
690    /// The order of the items will be preserved even when the table is sorted.
691    ///
692    /// Chainable variant.
693    pub fn items(self, items: Vec<T>) -> Self {
694        self.with(|t| t.set_items(items))
695    }
696
697    /// Returns an immutable reference to the item at the specified index
698    /// within the underlying storage vector.
699    pub fn borrow_item(&self, index: usize) -> Option<&T> {
700        self.items.get(index)
701    }
702
703    /// Returns a mutable reference to the item at the specified index within
704    /// the underlying storage vector.
705    pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
706        self.items.get_mut(index)
707    }
708
709    /// Returns an immutable reference to the items contained within the table.
710    pub fn borrow_items(&self) -> &[T] {
711        &self.items
712    }
713
714    /// Returns a mutable reference to the items contained within the table.
715    ///
716    /// Can be used to modify the items in place.
717    pub fn borrow_items_mut(&mut self) -> &mut [T] {
718        self.needs_relayout = true;
719        &mut self.items
720    }
721
722    /// Returns the index of the currently selected item within the underlying
723    /// storage vector.
724    pub fn item(&self) -> Option<usize> {
725        self.rows_to_items.get(self.focus).copied()
726    }
727
728    /// Selects the item at the specified index within the underlying storage
729    /// vector.
730    pub fn set_selected_item(&mut self, item_index: usize) {
731        if item_index >= self.items.len() {
732            return;
733        }
734
735        if let Some(row) = self
736            .rows_to_items
737            .iter()
738            .position(|&item| item == item_index)
739        {
740            self.focus = row;
741            self.scroll_core.scroll_to_y(row);
742        }
743    }
744
745    /// Selects the item at the specified index within the underlying storage
746    /// vector.
747    ///
748    /// Chainable variant.
749    pub fn selected_item(self, item_index: usize) -> Self {
750        self.with(|t| t.set_selected_item(item_index))
751    }
752
753    /// Inserts a new item into the table.
754    ///
755    /// The currently active sort order is preserved and will be applied to the
756    /// newly inserted item.
757    ///
758    /// If no sort option is set, the item will be added to the end of the table.
759    pub fn insert_item(&mut self, item: T) {
760        self.insert_item_at(self.items.len(), item);
761    }
762
763    /// Inserts a new item into the table.
764    ///
765    /// The currently active sort order is preserved and will be applied to the
766    /// newly inserted item.
767    ///
768    /// If no sort option is set, the item will be inserted at the given index.
769    ///
770    /// # Panics
771    ///
772    /// If `index > self.len()`.
773    pub fn insert_item_at(&mut self, index: usize, item: T) {
774        self.items.push(item);
775
776        // Here we know self.items.len() > 0
777        self.rows_to_items.insert(index, self.items.len() - 1);
778
779        if let Some((column, order)) = self.order() {
780            self.sort_by(column, order);
781        }
782        self.needs_relayout = true;
783    }
784
785    /// Removes the item at the specified index within the underlying storage
786    /// vector and returns it.
787    pub fn remove_item(&mut self, item_index: usize) -> Option<T> {
788        if item_index < self.items.len() {
789            // Move the selection if the currently selected item gets removed
790            if let Some(selected_index) = self.item() {
791                if selected_index == item_index {
792                    self.focus_up(1);
793                }
794            }
795
796            // Remove the sorted reference to the item
797            self.rows_to_items.retain(|i| *i != item_index);
798
799            // Adjust remaining references
800            for ref_index in &mut self.rows_to_items {
801                if *ref_index > item_index {
802                    *ref_index -= 1;
803                }
804            }
805            self.needs_relayout = true;
806
807            // Remove actual item from the underlying storage
808            Some(self.items.remove(item_index))
809        } else {
810            None
811        }
812    }
813
814    /// Removes all items from the underlying storage and returns them.
815    pub fn take_items(&mut self) -> Vec<T> {
816        self.set_selected_row(0);
817        self.rows_to_items.clear();
818        self.needs_relayout = true;
819        self.items.drain(0..).collect()
820    }
821}
822
823impl<T, H> TableView<T, H>
824where
825    T: TableViewItem<H>,
826    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
827{
828    fn draw_columns<C: Fn(&Printer, &TableColumn<H>)>(
829        &self,
830        printer: &Printer,
831        sep: &str,
832        callback: C,
833    ) {
834        let mut column_offset = 0;
835        let column_count = self.columns.len();
836        for (index, column) in self.columns.iter().enumerate() {
837            let printer = &printer.offset((column_offset, 0)).focused(true);
838
839            callback(printer, column);
840
841            if 1 + index < column_count {
842                printer.print((column.width + 1, 0), sep);
843            }
844
845            column_offset += column.width + 3;
846        }
847    }
848
849    fn sort_items(&mut self, column: H, order: Ordering) {
850        if !self.is_empty() {
851            let old_item = self.item();
852
853            let items = &self.items;
854            let rows_to_items = &mut self.rows_to_items;
855            let ascending = order == Ordering::Less;
856            rows_to_items.sort_by(|a, b| {
857                if ascending {
858                    items[*a].cmp(&items[*b], column)
859                } else {
860                    items[*b].cmp(&items[*a], column)
861                }
862            });
863
864            if let Some(old_item) = old_item {
865                self.set_selected_item(old_item);
866            }
867        }
868    }
869
870    fn draw_item(&self, printer: &Printer, i: usize) {
871        self.draw_columns(printer, "┆ ", |printer, column| {
872            let value = self.items[self.rows_to_items[i]].to_column(column.column);
873            column.draw_row(printer, value.as_str());
874        });
875    }
876
877    fn on_focus_change(&self) -> EventResult {
878        let row = self.row().unwrap();
879        let index = self.item().unwrap();
880        EventResult::Consumed(
881            self.on_select
882                .clone()
883                .map(|cb| Callback::from_fn(move |s| cb(s, row, index))),
884        )
885    }
886
887    fn focus_up(&mut self, n: usize) {
888        self.focus -= cmp::min(self.focus, n);
889    }
890
891    fn focus_down(&mut self, n: usize) {
892        self.focus = cmp::min(self.focus + n, self.items.len().saturating_sub(1));
893    }
894
895    fn active_column(&self) -> usize {
896        self.columns.iter().position(|c| c.selected).unwrap_or(0)
897    }
898
899    fn column_cancel(&mut self) {
900        self.column_select = false;
901        for column in &mut self.columns {
902            column.selected = column.order != Ordering::Equal;
903        }
904    }
905
906    fn column_next(&mut self) -> bool {
907        let column = self.active_column();
908        if 1 + column < self.columns.len() {
909            self.columns[column].selected = false;
910            self.columns[column + 1].selected = true;
911            true
912        } else {
913            false
914        }
915    }
916
917    fn column_prev(&mut self) -> bool {
918        let column = self.active_column();
919        if column > 0 {
920            self.columns[column].selected = false;
921            self.columns[column - 1].selected = true;
922            true
923        } else {
924            false
925        }
926    }
927
928    fn column_select(&mut self) -> EventResult {
929        if !self.sortable {
930            self.column_cancel();
931            return EventResult::Ignored;
932        }
933
934        let next = self.active_column();
935        let column = self.columns[next].column;
936        let current = self
937            .columns
938            .iter()
939            .position(|c| c.order != Ordering::Equal)
940            .unwrap_or(0);
941
942        let order = if current != next {
943            self.columns[next].default_order
944        } else if self.columns[current].order == Ordering::Less {
945            Ordering::Greater
946        } else {
947            Ordering::Less
948        };
949
950        self.sort_by(column, order);
951
952        if self.on_sort.is_some() {
953            let c = &self.columns[self.active_column()];
954            let column = c.column;
955            let order = c.order;
956
957            let cb = self.on_sort.clone().unwrap();
958            EventResult::with_cb(move |s| cb(s, column, order))
959        } else {
960            EventResult::Consumed(None)
961        }
962    }
963
964    fn column_for_x(&self, mut x: usize) -> Option<usize> {
965        for (i, col) in self.columns.iter().enumerate() {
966            x = match x.checked_sub(col.width) {
967                None => return Some(i),
968                Some(x) => x.checked_sub(3)?,
969            };
970        }
971
972        None
973    }
974
975    fn draw_content(&self, printer: &Printer) {
976        for i in 0..self.rows_to_items.len() {
977            let printer = printer.offset((0, i));
978            let color = if i == self.focus && self.enabled {
979                if !self.column_select && self.enabled && printer.focused {
980                    theme::ColorStyle::highlight()
981                } else {
982                    theme::ColorStyle::highlight_inactive()
983                }
984            } else {
985                theme::ColorStyle::primary()
986            };
987
988            if i < self.items.len() {
989                printer.with_color(color, |printer| {
990                    self.draw_item(printer, i);
991                });
992            }
993        }
994    }
995
996    fn layout_content(&mut self, size: Vec2) {
997        let column_count = self.columns.len();
998
999        // Split up all columns into sized / unsized groups
1000        let (mut sized, mut flexible): (Vec<&mut TableColumn<H>>, Vec<&mut TableColumn<H>>) = self
1001            .columns
1002            .iter_mut()
1003            .partition(|c| c.requested_width.is_some());
1004
1005        // Subtract one for the separators between our columns (that's column_count - 1)
1006        let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
1007
1008        // Calculate widths for all requested columns
1009        let mut remaining_width = available_width;
1010        for column in &mut sized {
1011            column.width = match *column.requested_width.as_ref().unwrap() {
1012                TableColumnWidth::Percent(width) => cmp::min(
1013                    (size.x as f32 / 100.0 * width as f32).ceil() as usize,
1014                    remaining_width,
1015                ),
1016                TableColumnWidth::Absolute(width) => width,
1017            };
1018            remaining_width = remaining_width.saturating_sub(column.width);
1019        }
1020
1021        // Spread the remaining width across the unsized columns.
1022        let remaining_columns = flexible.len();
1023        let base_width = if remaining_columns > 0 {
1024            remaining_width / remaining_columns
1025        } else {
1026            0
1027        };
1028        for column in &mut flexible {
1029            column.width = base_width;
1030        }
1031
1032        self.needs_relayout = false;
1033    }
1034
1035    fn content_required_size(&mut self, req: Vec2) -> Vec2 {
1036        Vec2::new(req.x, self.rows_to_items.len())
1037    }
1038
1039    fn on_inner_event(&mut self, event: Event) -> EventResult {
1040        let last_focus = self.focus;
1041        match event {
1042            Event::Key(Key::Right) => {
1043                if !self.sortable {
1044                    return EventResult::Ignored;
1045                }
1046
1047                if self.column_select {
1048                    if !self.column_next() {
1049                        return EventResult::Ignored;
1050                    }
1051                } else {
1052                    self.column_select = true;
1053                }
1054            }
1055            Event::Key(Key::Left) => {
1056                if !self.sortable {
1057                    return EventResult::Ignored;
1058                }
1059
1060                if self.column_select {
1061                    if !self.column_prev() {
1062                        return EventResult::Ignored;
1063                    }
1064                } else {
1065                    self.column_select = true;
1066                }
1067            }
1068            Event::Key(Key::Up) if self.focus > 0 || self.column_select => {
1069                if self.column_select {
1070                    self.column_cancel();
1071                } else {
1072                    self.focus_up(1);
1073                }
1074            }
1075            Event::Key(Key::Down) if self.focus + 1 < self.items.len() || self.column_select => {
1076                if self.column_select {
1077                    self.column_cancel();
1078                } else {
1079                    self.focus_down(1);
1080                }
1081            }
1082            Event::Key(Key::PageUp) => {
1083                self.column_cancel();
1084                self.focus_up(10);
1085            }
1086            Event::Key(Key::PageDown) => {
1087                self.column_cancel();
1088                self.focus_down(10);
1089            }
1090            Event::Key(Key::Home) => {
1091                self.column_cancel();
1092                self.focus = 0;
1093            }
1094            Event::Key(Key::End) => {
1095                self.column_cancel();
1096                self.focus = self.items.len().saturating_sub(1);
1097            }
1098            Event::Key(Key::Enter) => {
1099                if self.column_select && self.sortable {
1100                    return self.column_select();
1101                } else if !self.is_empty() && self.on_submit.is_some() {
1102                    return self.on_submit_event();
1103                }
1104            }
1105            Event::Mouse {
1106                position,
1107                offset,
1108                event: MouseEvent::Press(MouseButton::Left),
1109            } if !self.is_empty()
1110                && position
1111                    .checked_sub(offset)
1112                    .map_or(false, |p| p.y == self.focus) =>
1113            {
1114                self.column_cancel();
1115                return self.on_submit_event();
1116            }
1117            Event::Mouse {
1118                position,
1119                offset,
1120                event: MouseEvent::Press(_),
1121            } if !self.is_empty() => match position.checked_sub(offset) {
1122                Some(position) if position.y < self.rows_to_items.len() => {
1123                    self.column_cancel();
1124                    self.focus = position.y;
1125                }
1126                _ => return EventResult::Ignored,
1127            },
1128            _ => return EventResult::Ignored,
1129        }
1130
1131        let focus = self.focus;
1132
1133        if self.column_select {
1134            EventResult::Consumed(None)
1135        } else if !self.is_empty() && last_focus != focus {
1136            self.on_focus_change()
1137        } else {
1138            EventResult::Ignored
1139        }
1140    }
1141
1142    fn inner_important_area(&self, size: Vec2) -> Rect {
1143        Rect::from_size((0, self.focus), (size.x, 1))
1144    }
1145
1146    fn on_submit_event(&mut self) -> EventResult {
1147        if let Some(cb) = &self.on_submit {
1148            let cb = Arc::clone(cb);
1149            let row = self.row().unwrap();
1150            let index = self.item().unwrap();
1151            return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row, index))));
1152        }
1153        EventResult::Ignored
1154    }
1155}
1156
1157impl<T, H> View for TableView<T, H>
1158where
1159    T: TableViewItem<H> + Send + Sync + 'static,
1160    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1161{
1162    fn draw(&self, printer: &Printer) {
1163        self.draw_columns(printer, "╷ ", |printer, column| {
1164            let color = if self.enabled
1165                && self.sortable
1166                && (column.order != Ordering::Equal || column.selected)
1167            {
1168                if self.column_select && column.selected && printer.focused {
1169                    theme::ColorStyle::highlight()
1170                } else {
1171                    theme::ColorStyle::highlight_inactive()
1172                }
1173            } else {
1174                theme::ColorStyle::primary()
1175            };
1176
1177            printer.with_color(color, |printer| {
1178                column.draw_header(printer, self.sortable);
1179            });
1180        });
1181
1182        self.draw_columns(
1183            &printer.offset((0, 1)).focused(true),
1184            "┴─",
1185            |printer, column| {
1186                printer.print_hline((0, 0), column.width + 1, "─");
1187            },
1188        );
1189
1190        // Extend the vertical bars to the end of the view.
1191        let available_height = printer.size.y.saturating_sub(2);
1192        let filled_rows = self.rows_to_items.len().min(available_height);
1193        for y in 2 + filled_rows..printer.size.y {
1194            self.draw_columns(&printer.offset((0, y)), "┆ ", |_, _| ());
1195        }
1196
1197        let printer = &printer.offset((0, 2)).focused(true);
1198        scroll::draw(self, printer, Self::draw_content);
1199    }
1200
1201    fn layout(&mut self, size: Vec2) {
1202        scroll::layout(
1203            self,
1204            size.saturating_sub((0, 2)),
1205            self.needs_relayout,
1206            Self::layout_content,
1207            Self::content_required_size,
1208        );
1209    }
1210
1211    fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
1212        self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
1213    }
1214
1215    fn on_event(&mut self, event: Event) -> EventResult {
1216        if !self.enabled {
1217            return EventResult::Ignored;
1218        }
1219
1220        match event {
1221            Event::Mouse {
1222                position,
1223                offset,
1224                event: MouseEvent::Press(MouseButton::Left),
1225            } if position.checked_sub(offset).map_or(false, |p| p.y == 0) => {
1226                if !self.sortable {
1227                    return EventResult::Ignored;
1228                }
1229
1230                if let Some(position) = position.checked_sub(offset) {
1231                    if let Some(col) = self.column_for_x(position.x) {
1232                        if self.column_select && self.columns[col].selected {
1233                            return self.column_select();
1234                        } else {
1235                            let active = self.active_column();
1236                            self.columns[active].selected = false;
1237                            self.columns[col].selected = true;
1238                            self.column_select = true;
1239                        }
1240                    }
1241                }
1242                EventResult::Ignored
1243            }
1244            event => scroll::on_event(
1245                self,
1246                event.relativized((0, 2)),
1247                Self::on_inner_event,
1248                Self::inner_important_area,
1249            ),
1250        }
1251    }
1252
1253    fn important_area(&self, size: Vec2) -> Rect {
1254        self.inner_important_area(size.saturating_sub((0, 2))) + (0, 2)
1255    }
1256}
1257
1258/// View to display a 2D array with row and column headers.
1259pub struct ArrayView<T, H> {
1260    enabled: bool,
1261    scroll_core: scroll::Core,
1262    needs_relayout: bool,
1263
1264    columns: Vec<TableColumn<H>>,
1265    row_header: ArrayRowHeader,
1266    array_name: String,
1267
1268    focus: usize,
1269    focus_col: usize,
1270    items: Vec<T>,
1271
1272    on_submit: Option<CellCallback<H>>,
1273    on_select: Option<CellCallback<H>>,
1274}
1275
1276cursive::impl_scroller!(ArrayView < T, H > ::scroll_core);
1277
1278impl<T, H> Default for ArrayView<T, H>
1279where
1280    T: ArrayViewItem<H>,
1281    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1282{
1283    /// Creates a new empty `ArrayView` without any columns.
1284    ///
1285    /// See [`ArrayView::new()`].
1286    fn default() -> Self {
1287        Self::new()
1288    }
1289}
1290
1291impl<T, H> ArrayView<T, H>
1292where
1293    T: ArrayViewItem<H> + PartialEq,
1294    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1295{
1296    /// Sets the contained items of the array.
1297    ///
1298    /// The current selection will be preserved when possible.
1299    /// (But this is only available for `T: PartialEq`.)
1300    pub fn set_items_stable(&mut self, items: Vec<T>) {
1301        // Preserve selection
1302        let new_location = self
1303            .item()
1304            .and_then(|old_item| {
1305                let old_item = &self.items[old_item];
1306                items.iter().position(|new| new == old_item)
1307            })
1308            .unwrap_or(0);
1309
1310        self.set_items_and_focus(items, new_location);
1311    }
1312}
1313
1314impl<T, H> ArrayView<T, H>
1315where
1316    T: ArrayViewItem<H>,
1317    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1318{
1319    /// Creates a new empty `ArrayView` without any columns.
1320    ///
1321    /// An ArrayView should be accompanied by an enum of type `H` representing
1322    /// the array columns.
1323    pub fn new() -> Self {
1324        Self {
1325            enabled: true,
1326            scroll_core: scroll::Core::new(),
1327            needs_relayout: true,
1328
1329            columns: Vec::new(),
1330            row_header: ArrayRowHeader::new(),
1331            array_name: String::new(),
1332
1333            focus: 0,
1334            focus_col: 0,
1335            items: Vec::new(),
1336
1337            on_submit: None,
1338            on_select: None,
1339        }
1340    }
1341
1342    /// Configures the row header column.
1343    ///
1344    /// Row header values are read from [`ArrayViewItem::to_row`].
1345    /// The top-left corner text is set via [`ArrayView::array_name`].
1346    ///
1347    /// Chainable variant.
1348    pub fn row_header<C: FnOnce(ArrayRowHeader) -> ArrayRowHeader>(mut self, callback: C) -> Self {
1349        self.set_row_header(callback);
1350        self
1351    }
1352
1353    /// Sets the text shown in the top-left corner.
1354    ///
1355    /// Chainable variant.
1356    pub fn array_name<S: Into<String>>(mut self, name: S) -> Self {
1357        self.set_array_name(name);
1358        self
1359    }
1360
1361    /// Sets the text shown in the top-left corner.
1362    pub fn set_array_name<S: Into<String>>(&mut self, name: S) {
1363        self.array_name = name.into();
1364    }
1365
1366    /// Configures the row header column.
1367    ///
1368    /// Row header values are read from [`ArrayViewItem::to_row`].
1369    /// The top-left corner text is set via [`ArrayView::array_name`].
1370    pub fn set_row_header<C: FnOnce(ArrayRowHeader) -> ArrayRowHeader>(
1371        &mut self,
1372        callback: C,
1373    ) {
1374        self.row_header = callback(ArrayRowHeader::new());
1375        self.needs_relayout = true;
1376    }
1377
1378    /// Adds a column for the specified array column from type `H` along with
1379    /// a title for its visual display.
1380    ///
1381    /// The provided callback can be used to further configure the
1382    /// created [`TableColumn`](struct.TableColumn.html).
1383    pub fn column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
1384        mut self,
1385        column: H,
1386        title: S,
1387        callback: C,
1388    ) -> Self {
1389        self.add_column(column, title, callback);
1390        self
1391    }
1392
1393    /// Adds a column for the specified array column from type `H` along with
1394    /// a title for its visual display.
1395    ///
1396    /// The provided callback can be used to further configure the
1397    /// created [`TableColumn`](struct.TableColumn.html).
1398    pub fn add_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
1399        &mut self,
1400        column: H,
1401        title: S,
1402        callback: C,
1403    ) {
1404        self.insert_column(self.columns.len(), column, title, callback);
1405    }
1406
1407    /// Remove a column.
1408    pub fn remove_column(&mut self, i: usize) {
1409        self.columns.remove(i);
1410        if self.focus_col >= self.columns.len() {
1411            self.focus_col = self.columns.len().saturating_sub(1);
1412        }
1413        self.needs_relayout = true;
1414    }
1415
1416    /// Adds a column for the specified array column from type `H` along with
1417    /// a title for its visual display.
1418    ///
1419    /// The provided callback can be used to further configure the
1420    /// created [`TableColumn`](struct.TableColumn.html).
1421    pub fn insert_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
1422        &mut self,
1423        i: usize,
1424        column: H,
1425        title: S,
1426        callback: C,
1427    ) {
1428        self.columns
1429            .insert(i, callback(TableColumn::new(column, title.into())));
1430        if self.focus_col >= self.columns.len() {
1431            self.focus_col = self.columns.len().saturating_sub(1);
1432        }
1433        self.needs_relayout = true;
1434    }
1435
1436    /// Disables this view.
1437    ///
1438    /// A disabled view cannot be selected.
1439    pub fn disable(&mut self) {
1440        self.enabled = false;
1441    }
1442
1443    /// Re-enables this view.
1444    pub fn enable(&mut self) {
1445        self.enabled = true;
1446    }
1447
1448    /// Enable or disable this view.
1449    pub fn set_enabled(&mut self, enabled: bool) {
1450        self.enabled = enabled;
1451    }
1452
1453    /// Returns `true` if this view is enabled.
1454    pub fn is_enabled(&self) -> bool {
1455        self.enabled
1456    }
1457
1458    /// Sets a callback to be used when `<Enter>` is pressed while a cell
1459    /// is selected.
1460    ///
1461    /// Both the currently selected row and column will be given to the callback.
1462    ///
1463    /// # Example
1464    ///
1465    /// ```no_run
1466    /// # use cursive_core::Cursive;
1467    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
1468    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1469    /// # enum ArrayColumn { X }
1470    /// # #[derive(Clone)]
1471    /// # struct Item { label: String, value: String }
1472    /// # impl ArrayViewItem<ArrayColumn> for Item {
1473    /// #     fn to_column(&self, column: ArrayColumn) -> String {
1474    /// #         match column { ArrayColumn::X => self.value.clone() }
1475    /// #     }
1476    /// #     fn to_row(&self) -> String {
1477    /// #         self.label.clone()
1478    /// #     }
1479    /// # }
1480    /// # let mut array = ArrayView::<Item, ArrayColumn>::new()
1481    /// #     .row_header(|h| h.width(6))
1482    /// #     .column(ArrayColumn::X, "X", |c| c);
1483    /// array.set_on_submit(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {
1484    /// });
1485    /// ```
1486    pub fn set_on_submit<F>(&mut self, cb: F)
1487    where
1488        F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
1489    {
1490        self.on_submit = Some(Arc::new(move |s, row, column| cb(s, row, column)));
1491    }
1492
1493    /// Sets a callback to be used when `<Enter>` is pressed while a cell
1494    /// is selected.
1495    ///
1496    /// Chainable variant.
1497    ///
1498    /// # Example
1499    ///
1500    /// ```no_run
1501    /// # use cursive_core::Cursive;
1502    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
1503    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1504    /// # enum ArrayColumn { X }
1505    /// # #[derive(Clone)]
1506    /// # struct Item { label: String, value: String }
1507    /// # impl ArrayViewItem<ArrayColumn> for Item {
1508    /// #     fn to_column(&self, column: ArrayColumn) -> String {
1509    /// #         match column { ArrayColumn::X => self.value.clone() }
1510    /// #     }
1511    /// #     fn to_row(&self) -> String {
1512    /// #         self.label.clone()
1513    /// #     }
1514    /// # }
1515    /// # let array = ArrayView::<Item, ArrayColumn>::new()
1516    /// #     .row_header(|h| h.width(6))
1517    /// #     .column(ArrayColumn::X, "X", |c| c);
1518    /// let _array = array.on_submit(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {});
1519    /// ```
1520    pub fn on_submit<F>(self, cb: F) -> Self
1521    where
1522        F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
1523    {
1524        self.with(|t| t.set_on_submit(cb))
1525    }
1526
1527    /// Sets a callback to be used when a cell is selected.
1528    ///
1529    /// Both the currently selected row and column will be given to the callback.
1530    ///
1531    /// # Example
1532    ///
1533    /// ```no_run
1534    /// # use cursive_core::Cursive;
1535    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
1536    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1537    /// # enum ArrayColumn { X }
1538    /// # #[derive(Clone)]
1539    /// # struct Item { label: String, value: String }
1540    /// # impl ArrayViewItem<ArrayColumn> for Item {
1541    /// #     fn to_column(&self, column: ArrayColumn) -> String {
1542    /// #         match column { ArrayColumn::X => self.value.clone() }
1543    /// #     }
1544    /// #     fn to_row(&self) -> String {
1545    /// #         self.label.clone()
1546    /// #     }
1547    /// # }
1548    /// # let mut array = ArrayView::<Item, ArrayColumn>::new()
1549    /// #     .row_header(|h| h.width(6))
1550    /// #     .column(ArrayColumn::X, "X", |c| c);
1551    /// array.set_on_select(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {
1552    /// });
1553    /// ```
1554    pub fn set_on_select<F>(&mut self, cb: F)
1555    where
1556        F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
1557    {
1558        self.on_select = Some(Arc::new(move |s, row, column| cb(s, row, column)));
1559    }
1560
1561    /// Sets a callback to be used when a cell is selected.
1562    ///
1563    /// Chainable variant.
1564    ///
1565    /// # Example
1566    ///
1567    /// ```no_run
1568    /// # use cursive_core::Cursive;
1569    /// # use better_cursive_table::{ArrayView, ArrayViewItem};
1570    /// # #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1571    /// # enum ArrayColumn { X }
1572    /// # #[derive(Clone)]
1573    /// # struct Item { label: String, value: String }
1574    /// # impl ArrayViewItem<ArrayColumn> for Item {
1575    /// #     fn to_column(&self, column: ArrayColumn) -> String {
1576    /// #         match column { ArrayColumn::X => self.value.clone() }
1577    /// #     }
1578    /// #     fn to_row(&self) -> String {
1579    /// #         self.label.clone()
1580    /// #     }
1581    /// # }
1582    /// # let array = ArrayView::<Item, ArrayColumn>::new()
1583    /// #     .row_header(|h| h.width(6))
1584    /// #     .column(ArrayColumn::X, "X", |c| c);
1585    /// let _array = array.on_select(|_siv: &mut Cursive, _row: usize, _column: ArrayColumn| {});
1586    /// ```
1587    pub fn on_select<F>(self, cb: F) -> Self
1588    where
1589        F: Fn(&mut Cursive, usize, H) + Send + Sync + 'static,
1590    {
1591        self.with(|t| t.set_on_select(cb))
1592    }
1593
1594    /// Removes all items from this view.
1595    pub fn clear(&mut self) {
1596        self.items.clear();
1597        self.focus = 0;
1598        self.needs_relayout = true;
1599    }
1600
1601    /// Returns the number of items in this array.
1602    pub fn len(&self) -> usize {
1603        self.items.len()
1604    }
1605
1606    /// Returns `true` if this array has no items.
1607    pub fn is_empty(&self) -> bool {
1608        self.items.is_empty()
1609    }
1610
1611    /// Returns the index of the currently selected array row.
1612    pub fn row(&self) -> Option<usize> {
1613        if self.items.is_empty() {
1614            None
1615        } else {
1616            Some(self.focus)
1617        }
1618    }
1619
1620    /// Selects the row at the specified index.
1621    pub fn set_selected_row(&mut self, row_index: usize) {
1622        self.focus = row_index;
1623        self.scroll_core.scroll_to_y(self.row_y(row_index));
1624    }
1625
1626    /// Selects the row at the specified index.
1627    ///
1628    /// Chainable variant.
1629    pub fn selected_row(self, row_index: usize) -> Self {
1630        self.with(|t| t.set_selected_row(row_index))
1631    }
1632
1633    /// Sets the contained items of the array.
1634    pub fn set_items(&mut self, items: Vec<T>) {
1635        self.set_items_and_focus(items, 0);
1636    }
1637
1638    fn set_items_and_focus(&mut self, items: Vec<T>, new_location: usize) {
1639        self.items = items;
1640        self.set_selected_item(new_location);
1641        self.needs_relayout = true;
1642    }
1643
1644    /// Sets the contained items of the array.
1645    ///
1646    /// Chainable variant.
1647    pub fn items(self, items: Vec<T>) -> Self {
1648        self.with(|t| t.set_items(items))
1649    }
1650
1651    /// Returns an immutable reference to the item at the specified index
1652    /// within the underlying storage vector.
1653    pub fn borrow_item(&self, index: usize) -> Option<&T> {
1654        self.items.get(index)
1655    }
1656
1657    /// Returns a mutable reference to the item at the specified index within
1658    /// the underlying storage vector.
1659    pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
1660        self.items.get_mut(index)
1661    }
1662
1663    /// Returns an immutable reference to the items contained within the array.
1664    pub fn borrow_items(&self) -> &[T] {
1665        &self.items
1666    }
1667
1668    /// Returns a mutable reference to the items contained within the array.
1669    ///
1670    /// Can be used to modify the items in place.
1671    pub fn borrow_items_mut(&mut self) -> &mut [T] {
1672        self.needs_relayout = true;
1673        &mut self.items
1674    }
1675
1676    /// Returns the index of the currently selected item within the underlying
1677    /// storage vector.
1678    pub fn item(&self) -> Option<usize> {
1679        if self.items.is_empty() {
1680            None
1681        } else {
1682            Some(self.focus)
1683        }
1684    }
1685
1686    /// Selects the item at the specified index within the underlying storage
1687    /// vector.
1688    pub fn set_selected_item(&mut self, item_index: usize) {
1689        if item_index < self.items.len() {
1690            self.focus = item_index;
1691            self.scroll_core.scroll_to_y(self.row_y(item_index));
1692        }
1693    }
1694
1695    /// Selects the item at the specified index within the underlying storage
1696    /// vector.
1697    ///
1698    /// Chainable variant.
1699    pub fn selected_item(self, item_index: usize) -> Self {
1700        self.with(|t| t.set_selected_item(item_index))
1701    }
1702
1703    /// Inserts a new item into the array.
1704    ///
1705    /// The item will be added to the end of the array.
1706    pub fn insert_item(&mut self, item: T) {
1707        self.insert_item_at(self.items.len(), item);
1708    }
1709
1710    /// Inserts a new item into the array.
1711    ///
1712    /// The item will be inserted at the given index.
1713    ///
1714    /// # Panics
1715    ///
1716    /// If `index > self.len()`.
1717    pub fn insert_item_at(&mut self, index: usize, item: T) {
1718        self.items.insert(index, item);
1719        self.needs_relayout = true;
1720    }
1721
1722    /// Removes the item at the specified index within the underlying storage
1723    /// vector and returns it.
1724    pub fn remove_item(&mut self, item_index: usize) -> Option<T> {
1725        if item_index < self.items.len() {
1726            if let Some(selected_index) = self.item() {
1727                if selected_index == item_index {
1728                    self.focus_up(1);
1729                }
1730            }
1731
1732            self.needs_relayout = true;
1733            Some(self.items.remove(item_index))
1734        } else {
1735            None
1736        }
1737    }
1738
1739    /// Removes all items from the underlying storage and returns them.
1740    pub fn take_items(&mut self) -> Vec<T> {
1741        self.set_selected_row(0);
1742        self.needs_relayout = true;
1743        self.items.drain(0..).collect()
1744    }
1745}
1746
1747impl<T, H> ArrayView<T, H>
1748where
1749    T: ArrayViewItem<H>,
1750    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
1751{
1752    fn row_y(&self, row_index: usize) -> usize {
1753        row_index.saturating_mul(2)
1754    }
1755
1756    fn row_for_y(&self, y: usize) -> Option<usize> {
1757        if y % 2 == 0 {
1758            Some(y / 2)
1759        } else {
1760            None
1761        }
1762    }
1763
1764    fn column_for_x(&self, mut x: usize) -> Option<usize> {
1765        x = match x.checked_sub(self.row_header.width) {
1766            None => return None,
1767            Some(x) => x,
1768        };
1769        x = x.checked_sub(3)?;
1770
1771        for (i, col) in self.columns.iter().enumerate() {
1772            x = match x.checked_sub(col.width) {
1773                None => return Some(i),
1774                Some(x) => x.checked_sub(3)?,
1775            };
1776        }
1777
1778        None
1779    }
1780
1781    fn draw_columns<C, R>(&self, printer: &Printer, sep: &str, row_cb: R, mut col_cb: C)
1782    where
1783        C: FnMut(&Printer, &TableColumn<H>, usize),
1784        R: Fn(&Printer, &ArrayRowHeader),
1785    {
1786        let row_header = &self.row_header;
1787        let printer = &printer.offset((0, 0)).focused(true);
1788        row_cb(printer, row_header);
1789
1790        if !self.columns.is_empty() {
1791            printer.print((row_header.width + 1, 0), sep);
1792        }
1793
1794        let mut column_offset = row_header.width + 3;
1795
1796        let column_count = self.columns.len();
1797        for (index, column) in self.columns.iter().enumerate() {
1798            let printer = &printer.offset((column_offset, 0)).focused(true);
1799
1800            col_cb(printer, column, index);
1801
1802            if 1 + index < column_count {
1803                printer.print((column.width + 1, 0), sep);
1804            }
1805
1806            column_offset += column.width + 3;
1807        }
1808    }
1809
1810    fn draw_item(&self, printer: &Printer, i: usize) {
1811        let item = &self.items[i];
1812        let focused_row = i == self.focus && self.enabled;
1813        let focused_col = self.focus_col;
1814        self.draw_columns(
1815            printer,
1816            "┆ ",
1817            |printer, row_header| {
1818                let value = item.to_row();
1819                row_header.draw_row(printer, value.as_str());
1820            },
1821            |printer, column, index| {
1822                let color = if focused_row && index == focused_col {
1823                    if printer.focused {
1824                        theme::ColorStyle::highlight()
1825                    } else {
1826                        theme::ColorStyle::highlight_inactive()
1827                    }
1828                } else {
1829                    theme::ColorStyle::primary()
1830                };
1831                let value = item.to_column(column.column);
1832                printer.with_color(color, |printer| {
1833                    column.draw_row(printer, value.as_str());
1834                });
1835            },
1836        );
1837    }
1838
1839    fn on_focus_change(&self) -> EventResult {
1840        let row = self.row().unwrap();
1841        let column = match self.columns.get(self.focus_col) {
1842            Some(column) => column.column,
1843            None => return EventResult::Ignored,
1844        };
1845        EventResult::Consumed(
1846            self.on_select
1847                .clone()
1848                .map(|cb| Callback::from_fn(move |s| cb(s, row, column))),
1849        )
1850    }
1851
1852    fn focus_up(&mut self, n: usize) {
1853        self.focus -= cmp::min(self.focus, n);
1854    }
1855
1856    fn focus_down(&mut self, n: usize) {
1857        self.focus = cmp::min(self.focus + n, self.items.len().saturating_sub(1));
1858    }
1859
1860    fn draw_content(&self, printer: &Printer) {
1861        let row_count = self.items.len();
1862        for i in 0..row_count {
1863            let row_y = self.row_y(i);
1864            let printer = printer.offset((0, row_y));
1865            self.draw_item(&printer, i);
1866
1867            self.draw_columns(
1868                &printer.offset((0, 1)).focused(true),
1869                "┼─",
1870                |printer, row_header| {
1871                    printer.print_hline((0, 0), row_header.width + 1, "─");
1872                },
1873                |printer, column, _| {
1874                    printer.print_hline((0, 0), column.width + 1, "─");
1875                },
1876            );
1877        }
1878    }
1879
1880    fn layout_content(&mut self, size: Vec2) {
1881        let column_count = self.columns.len() + 1;
1882
1883        // Split up all columns into sized / unsized groups
1884        let (mut sized, mut flexible): (Vec<&mut TableColumn<H>>, Vec<&mut TableColumn<H>>) = self
1885            .columns
1886            .iter_mut()
1887            .partition(|c| c.requested_width.is_some());
1888
1889        // Subtract one for the separators between our columns (that's column_count - 1)
1890        let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
1891
1892        // Calculate widths for all requested columns
1893        let mut remaining_width = available_width;
1894        if let Some(requested_width) = self.row_header.requested_width.as_ref() {
1895            self.row_header.width = match *requested_width {
1896                TableColumnWidth::Percent(width) => cmp::min(
1897                    (size.x as f32 / 100.0 * width as f32).ceil() as usize,
1898                    remaining_width,
1899                ),
1900                TableColumnWidth::Absolute(width) => width,
1901            };
1902            remaining_width = remaining_width.saturating_sub(self.row_header.width);
1903        }
1904
1905        for column in &mut sized {
1906            column.width = match *column.requested_width.as_ref().unwrap() {
1907                TableColumnWidth::Percent(width) => cmp::min(
1908                    (size.x as f32 / 100.0 * width as f32).ceil() as usize,
1909                    remaining_width,
1910                ),
1911                TableColumnWidth::Absolute(width) => width,
1912            };
1913            remaining_width = remaining_width.saturating_sub(column.width);
1914        }
1915
1916        // Spread the remaining width across the unsized columns.
1917        let mut remaining_columns = flexible.len();
1918        if self.row_header.requested_width.is_none() {
1919            remaining_columns += 1;
1920        }
1921
1922        let base_width = if remaining_columns > 0 {
1923            remaining_width / remaining_columns
1924        } else {
1925            0
1926        };
1927
1928        if self.row_header.requested_width.is_none() {
1929            self.row_header.width = base_width;
1930        }
1931
1932        for column in &mut flexible {
1933            column.width = base_width;
1934        }
1935
1936        self.needs_relayout = false;
1937    }
1938
1939    fn content_required_size(&mut self, req: Vec2) -> Vec2 {
1940        let rows = self.items.len();
1941        let height = rows.saturating_mul(2);
1942        Vec2::new(req.x, height)
1943    }
1944
1945    fn on_inner_event(&mut self, event: Event) -> EventResult {
1946        let last_focus = (self.focus, self.focus_col);
1947        match event {
1948            Event::Key(Key::Up) if self.focus > 0 => {
1949                self.focus_up(1);
1950            }
1951            Event::Key(Key::Down) if self.focus + 1 < self.items.len() => {
1952                self.focus_down(1);
1953            }
1954            Event::Key(Key::Left) if self.focus_col > 0 => {
1955                self.focus_col -= 1;
1956            }
1957            Event::Key(Key::Right) if self.focus_col + 1 < self.columns.len() => {
1958                self.focus_col += 1;
1959            }
1960            Event::Key(Key::PageUp) => {
1961                self.focus_up(10);
1962            }
1963            Event::Key(Key::PageDown) => {
1964                self.focus_down(10);
1965            }
1966            Event::Key(Key::Home) => {
1967                self.focus = 0;
1968            }
1969            Event::Key(Key::End) => {
1970                self.focus = self.items.len().saturating_sub(1);
1971            }
1972            Event::Key(Key::Enter) => {
1973                if !self.is_empty() && self.on_submit.is_some() {
1974                    return self.on_submit_event();
1975                }
1976            }
1977            Event::Mouse {
1978                position,
1979                offset,
1980                event: MouseEvent::Press(MouseButton::Left),
1981            } if !self.is_empty() => {
1982                if let Some(position) = position.checked_sub(offset) {
1983                    let row = self.row_for_y(position.y);
1984                    let column = self.column_for_x(position.x);
1985                    if let (Some(row), Some(column)) = (row, column) {
1986                        if row == self.focus && column == self.focus_col {
1987                            return self.on_submit_event();
1988                        }
1989                        if row < self.items.len() {
1990                            self.focus = row;
1991                            self.focus_col = column;
1992                        }
1993                    } else {
1994                        return EventResult::Ignored;
1995                    }
1996                } else {
1997                    return EventResult::Ignored;
1998                }
1999            }
2000            Event::Mouse {
2001                position,
2002                offset,
2003                event: MouseEvent::Press(_),
2004            } if !self.is_empty() => {
2005                if let Some(position) = position.checked_sub(offset) {
2006                    let row = self.row_for_y(position.y);
2007                    let column = self.column_for_x(position.x);
2008                    if let (Some(row), Some(column)) = (row, column) {
2009                        if row < self.items.len() {
2010                            self.focus = row;
2011                            self.focus_col = column;
2012                        }
2013                    } else {
2014                        return EventResult::Ignored;
2015                    }
2016                } else {
2017                    return EventResult::Ignored;
2018                }
2019            }
2020            _ => return EventResult::Ignored,
2021        }
2022
2023        let focus = (self.focus, self.focus_col);
2024
2025        if !self.is_empty() && last_focus != focus {
2026            self.on_focus_change()
2027        } else {
2028            EventResult::Ignored
2029        }
2030    }
2031
2032    fn inner_important_area(&self, size: Vec2) -> Rect {
2033        Rect::from_size((0, self.row_y(self.focus)), (size.x, 1))
2034    }
2035
2036    fn on_submit_event(&mut self) -> EventResult {
2037        if let Some(cb) = &self.on_submit {
2038            let cb = Arc::clone(cb);
2039            let row = self.row().unwrap();
2040            let column = match self.columns.get(self.focus_col) {
2041                Some(column) => column.column,
2042                None => return EventResult::Ignored,
2043            };
2044            return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row, column))));
2045        }
2046        EventResult::Ignored
2047    }
2048}
2049
2050impl<T, H> View for ArrayView<T, H>
2051where
2052    T: ArrayViewItem<H> + Send + Sync + 'static,
2053    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
2054{
2055    fn draw(&self, printer: &Printer) {
2056        self.draw_columns(
2057            printer,
2058            "╷ ",
2059            |printer, row_header| {
2060                let title = self.array_name.as_str();
2061                printer.with_color(theme::ColorStyle::primary(), |printer| {
2062                    row_header.draw_header(printer, title);
2063                });
2064            },
2065            |printer, column, _| {
2066                printer.with_color(theme::ColorStyle::primary(), |printer| {
2067                    column.draw_header(printer, false);
2068                });
2069            },
2070        );
2071
2072        self.draw_columns(
2073            &printer.offset((0, 1)).focused(true),
2074            "┴─",
2075            |printer, row_header| {
2076                printer.print_hline((0, 0), row_header.width + 1, "─");
2077            },
2078            |printer, column, _| {
2079                printer.print_hline((0, 0), column.width + 1, "─");
2080            },
2081        );
2082
2083        // Extend the vertical bars to the end of the view.
2084        let available_height = printer.size.y.saturating_sub(2);
2085        let filled_rows = self.items.len().saturating_mul(2).min(available_height);
2086        for y in 2 + filled_rows..printer.size.y {
2087            self.draw_columns(
2088                &printer.offset((0, y)),
2089                "┆ ",
2090                |_, _| (),
2091                |_, _, _| (),
2092            );
2093        }
2094
2095        let printer = &printer.offset((0, 2)).focused(true);
2096        scroll::draw(self, printer, Self::draw_content);
2097    }
2098
2099    fn layout(&mut self, size: Vec2) {
2100        scroll::layout(
2101            self,
2102            size.saturating_sub((0, 2)),
2103            self.needs_relayout,
2104            Self::layout_content,
2105            Self::content_required_size,
2106        );
2107    }
2108
2109    fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
2110        self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
2111    }
2112
2113    fn on_event(&mut self, event: Event) -> EventResult {
2114        if !self.enabled {
2115            return EventResult::Ignored;
2116        }
2117
2118        match event {
2119            Event::Mouse {
2120                position,
2121                offset,
2122                event: MouseEvent::Press(_),
2123            } if position.checked_sub(offset).map_or(false, |p| p.y < 2) => EventResult::Ignored,
2124            event => scroll::on_event(
2125                self,
2126                event.relativized((0, 2)),
2127                Self::on_inner_event,
2128                Self::inner_important_area,
2129            ),
2130        }
2131    }
2132
2133    fn important_area(&self, size: Vec2) -> Rect {
2134        self.inner_important_area(size.saturating_sub((0, 2))) + (0, 2)
2135    }
2136}
2137
2138/// A type used for the construction of the row header in an
2139/// [`ArrayView`](struct.ArrayView.html).
2140pub struct ArrayRowHeader {
2141    alignment: HAlign,
2142    width: usize,
2143    requested_width: Option<TableColumnWidth>,
2144}
2145
2146impl ArrayRowHeader {
2147    /// Sets the horizontal text alignment of the row header.
2148    pub fn align(mut self, alignment: HAlign) -> Self {
2149        self.alignment = alignment;
2150        self
2151    }
2152
2153    /// Sets how many characters of width this row header will try to occupy.
2154    pub fn width(mut self, width: usize) -> Self {
2155        self.requested_width = Some(TableColumnWidth::Absolute(width));
2156        self
2157    }
2158
2159    /// Sets what percentage of the width of the entire array this row header
2160    /// will try to occupy.
2161    pub fn width_percent(mut self, width: usize) -> Self {
2162        self.requested_width = Some(TableColumnWidth::Percent(width));
2163        self
2164    }
2165
2166    fn new() -> Self {
2167        Self {
2168            alignment: HAlign::Left,
2169            width: 0,
2170            requested_width: None,
2171        }
2172    }
2173
2174    fn draw_header(&self, printer: &Printer, title: &str) {
2175        let header = match self.alignment {
2176            HAlign::Left => format!("{:<width$}", title, width = self.width),
2177            HAlign::Right => format!("{:>width$}", title, width = self.width),
2178            HAlign::Center => format!("{:^width$}", title, width = self.width),
2179        };
2180
2181        printer.print((0, 0), header.as_str());
2182    }
2183
2184    fn draw_row(&self, printer: &Printer, value: &str) {
2185        let value = match self.alignment {
2186            HAlign::Left => format!("{:<width$} ", value, width = self.width),
2187            HAlign::Right => format!("{:>width$} ", value, width = self.width),
2188            HAlign::Center => format!("{:^width$} ", value, width = self.width),
2189        };
2190
2191        printer.print((0, 0), value.as_str());
2192    }
2193}
2194
2195/// A type used for the construction of columns in a
2196/// [`TableView`](struct.TableView.html).
2197pub struct TableColumn<H> {
2198    column: H,
2199    title: String,
2200    selected: bool,
2201    alignment: HAlign,
2202    order: Ordering,
2203    width: usize,
2204    default_order: Ordering,
2205    requested_width: Option<TableColumnWidth>,
2206}
2207
2208enum TableColumnWidth {
2209    Percent(usize),
2210    Absolute(usize),
2211}
2212
2213impl<H: Copy + Clone + 'static> TableColumn<H> {
2214    /// Sets the default ordering of the column.
2215    pub fn ordering(mut self, order: Ordering) -> Self {
2216        self.default_order = order;
2217        self
2218    }
2219
2220    /// Sets the horizontal text alignment of the column.
2221    pub fn align(mut self, alignment: HAlign) -> Self {
2222        self.alignment = alignment;
2223        self
2224    }
2225
2226    /// Sets how many characters of width this column will try to occupy.
2227    pub fn width(mut self, width: usize) -> Self {
2228        self.requested_width = Some(TableColumnWidth::Absolute(width));
2229        self
2230    }
2231
2232    /// Sets what percentage of the width of the entire table this column will
2233    /// try to occupy.
2234    pub fn width_percent(mut self, width: usize) -> Self {
2235        self.requested_width = Some(TableColumnWidth::Percent(width));
2236        self
2237    }
2238
2239    fn new(column: H, title: String) -> Self {
2240        Self {
2241            column,
2242            title,
2243            selected: false,
2244            alignment: HAlign::Left,
2245            order: Ordering::Equal,
2246            width: 0,
2247            default_order: Ordering::Less,
2248            requested_width: None,
2249        }
2250    }
2251
2252    fn draw_header(&self, printer: &Printer, sortable: bool) {
2253        let title_width = if sortable {
2254            self.width.saturating_sub(4)
2255        } else {
2256            self.width
2257        };
2258        let title = self.title.as_str();
2259
2260        let mut header = match self.alignment {
2261            HAlign::Left => format!("{:<width$}", title, width = title_width),
2262            HAlign::Right => format!("{:>width$}", title, width = title_width),
2263            HAlign::Center => format!("{:^width$}", title, width = title_width),
2264        };
2265
2266        if sortable {
2267            let order = match self.order {
2268                Ordering::Less => "^",
2269                Ordering::Greater => "v",
2270                Ordering::Equal => " ",
2271            };
2272            header.push_str(" [");
2273            header.push_str(order);
2274            header.push(']');
2275        }
2276
2277        printer.print((0, 0), header.as_str());
2278    }
2279
2280    fn draw_row(&self, printer: &Printer, value: &str) {
2281        let value = match self.alignment {
2282            HAlign::Left => format!("{:<width$} ", value, width = self.width),
2283            HAlign::Right => format!("{:>width$} ", value, width = self.width),
2284            HAlign::Center => format!("{:^width$} ", value, width = self.width),
2285        };
2286
2287        printer.print((0, 0), value.as_str());
2288    }
2289}
2290
2291#[cfg(test)]
2292mod tests {
2293    use super::*;
2294
2295    #[derive(Copy, Clone, PartialEq, Eq, Hash)]
2296    enum SimpleColumn {
2297        Name,
2298    }
2299
2300    #[allow(dead_code)]
2301    impl SimpleColumn {
2302        fn as_str(&self) -> &str {
2303            match *self {
2304                SimpleColumn::Name => "Name",
2305            }
2306        }
2307    }
2308
2309    #[derive(Clone, Debug)]
2310    struct SimpleItem {
2311        name: String,
2312    }
2313
2314    impl TableViewItem<SimpleColumn> for SimpleItem {
2315        fn to_column(&self, column: SimpleColumn) -> String {
2316            match column {
2317                SimpleColumn::Name => self.name.to_string(),
2318            }
2319        }
2320
2321        fn cmp(&self, other: &Self, column: SimpleColumn) -> Ordering
2322        where
2323            Self: Sized,
2324        {
2325            match column {
2326                SimpleColumn::Name => self.name.cmp(&other.name),
2327            }
2328        }
2329    }
2330
2331    fn setup_test_table() -> TableView<SimpleItem, SimpleColumn> {
2332        TableView::<SimpleItem, SimpleColumn>::new()
2333            .column(SimpleColumn::Name, "Name", |c| c.width_percent(20))
2334    }
2335
2336    #[test]
2337    fn should_insert_into_existing_table() {
2338        let mut simple_table = setup_test_table();
2339
2340        let mut simple_items = Vec::new();
2341
2342        for i in 1..=10 {
2343            simple_items.push(SimpleItem {
2344                name: format!("{} - Name", i),
2345            });
2346        }
2347
2348        // Insert First Batch of Items
2349        simple_table.set_items(simple_items);
2350
2351        // Test for Additional item insertion
2352        simple_table.insert_item(SimpleItem {
2353            name: format!("{} Name", 11),
2354        });
2355
2356        assert!(simple_table.len() == 11);
2357    }
2358
2359    #[test]
2360    fn should_insert_into_empty_table() {
2361        let mut simple_table = setup_test_table();
2362
2363        // Test for First item insertion
2364        simple_table.insert_item(SimpleItem {
2365            name: format!("{} Name", 1),
2366        });
2367
2368        assert!(simple_table.len() == 1);
2369    }
2370}