cursive_table_view/
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/// Callback used when a column is sorted.
49///
50/// It takes the column and the ordering as input.
51///
52/// This is a private type to help readability.
53type OnSortCallback<H> = Arc<dyn Fn(&mut Cursive, H, Ordering) + Send + Sync>;
54
55/// Callback taking as argument the row and the index of an element.
56///
57/// This is a private type to help readability.
58type IndexCallback = Arc<dyn Fn(&mut Cursive, usize, usize) + Send + Sync>;
59
60/// View to select an item among a list, supporting multiple columns for sorting.
61///
62/// # Examples
63///
64/// ```rust
65/// # extern crate cursive;
66/// # extern crate cursive_table_view;
67/// # use std::cmp::Ordering;
68/// # use cursive_table_view::{TableView, TableViewItem};
69/// # use cursive::align::HAlign;
70/// # fn main() {
71/// // Provide a type for the table's columns
72/// #[derive(Copy, Clone, PartialEq, Eq, Hash)]
73/// enum BasicColumn {
74///     Name,
75///     Count,
76///     Rate
77/// }
78///
79/// // Define the item type
80/// #[derive(Clone, Debug)]
81/// struct Foo {
82///     name: String,
83///     count: usize,
84///     rate: usize
85/// }
86///
87/// impl TableViewItem<BasicColumn> for Foo {
88///
89///     fn to_column(&self, column: BasicColumn) -> String {
90///         match column {
91///             BasicColumn::Name => self.name.to_string(),
92///             BasicColumn::Count => format!("{}", self.count),
93///             BasicColumn::Rate => format!("{}", self.rate)
94///         }
95///     }
96///
97///     fn cmp(&self, other: &Self, column: BasicColumn) -> Ordering where Self: Sized {
98///         match column {
99///             BasicColumn::Name => self.name.cmp(&other.name),
100///             BasicColumn::Count => self.count.cmp(&other.count),
101///             BasicColumn::Rate => self.rate.cmp(&other.rate)
102///         }
103///     }
104///
105/// }
106///
107/// // Configure the actual table
108/// let table = TableView::<Foo, BasicColumn>::new()
109///                      .column(BasicColumn::Name, "Name", |c| c.width(20))
110///                      .column(BasicColumn::Count, "Count", |c| c.align(HAlign::Center))
111///                      .column(BasicColumn::Rate, "Rate", |c| {
112///                          c.ordering(Ordering::Greater).align(HAlign::Right).width(20)
113///                      })
114///                      .default_column(BasicColumn::Name);
115/// # }
116/// ```
117pub struct TableView<T, H> {
118    enabled: bool,
119    scroll_core: scroll::Core,
120    needs_relayout: bool,
121
122    column_select: bool,
123    columns: Vec<TableColumn<H>>,
124    column_indicies: HashMap<H, usize>,
125
126    focus: usize,
127    items: Vec<T>,
128    rows_to_items: Vec<usize>,
129
130    on_sort: Option<OnSortCallback<H>>,
131    // TODO Pass drawing offsets into the handlers so a popup menu
132    // can be created easily?
133    on_submit: Option<IndexCallback>,
134    on_select: Option<IndexCallback>,
135}
136
137cursive::impl_scroller!(TableView < T, H > ::scroll_core);
138
139impl<T, H> Default for TableView<T, H>
140where
141    T: TableViewItem<H> + PartialEq,
142    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
143{
144    /// Creates a new empty `TableView` without any columns.
145    ///
146    /// See [`TableView::new()`].
147    fn default() -> Self {
148        Self::new()
149    }
150}
151
152impl<T, H> TableView<T, H>
153where
154    T: TableViewItem<H> + PartialEq,
155    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
156{
157    /// Sets the contained items of the table.
158    ///
159    /// The currently active sort order is preserved and will be applied to all
160    /// items.
161    ///
162    /// Compared to `set_items`, the current selection will be preserved.
163    /// (But this is only available for `T: PartialEq`.)
164    pub fn set_items_stable(&mut self, items: Vec<T>) {
165        // Preserve selection
166        let new_location = self
167            .item()
168            .and_then(|old_item| {
169                let old_item = &self.items[old_item];
170                items.iter().position(|new| new == old_item)
171            })
172            .unwrap_or(0);
173
174        self.set_items_and_focus(items, new_location);
175    }
176}
177
178impl<T, H> TableView<T, H>
179where
180    T: TableViewItem<H>,
181    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
182{
183    /// Creates a new empty `TableView` without any columns.
184    ///
185    /// A TableView should be accompanied by a enum of type `H` representing
186    /// the table columns.
187    pub fn new() -> Self {
188        Self {
189            enabled: true,
190            scroll_core: scroll::Core::new(),
191            needs_relayout: true,
192
193            column_select: false,
194            columns: Vec::new(),
195            column_indicies: HashMap::new(),
196
197            focus: 0,
198            items: Vec::new(),
199            rows_to_items: Vec::new(),
200
201            on_sort: None,
202            on_submit: None,
203            on_select: None,
204        }
205    }
206
207    /// Adds a column for the specified table colum from type `H` along with
208    /// a title for its visual display.
209    ///
210    /// The provided callback can be used to further configure the
211    /// created [`TableColumn`](struct.TableColumn.html).
212    pub fn column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
213        mut self,
214        column: H,
215        title: S,
216        callback: C,
217    ) -> Self {
218        self.add_column(column, title, callback);
219        self
220    }
221
222    /// Adds a column for the specified table colum from type `H` along with
223    /// a title for its visual display.
224    ///
225    /// The provided callback can be used to further configure the
226    /// created [`TableColumn`](struct.TableColumn.html).
227    pub fn add_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
228        &mut self,
229        column: H,
230        title: S,
231        callback: C,
232    ) {
233        self.insert_column(self.columns.len(), column, title, callback);
234    }
235
236    /// Remove a column.
237    pub fn remove_column(&mut self, i: usize) {
238        // Update the existing indices
239        for column in &self.columns[i + 1..] {
240            *self.column_indicies.get_mut(&column.column).unwrap() -= 1;
241        }
242
243        let column = self.columns.remove(i);
244        self.column_indicies.remove(&column.column);
245        self.needs_relayout = true;
246    }
247
248    /// Adds a column for the specified table colum from type `H` along with
249    /// a title for its visual display.
250    ///
251    /// The provided callback can be used to further configure the
252    /// created [`TableColumn`](struct.TableColumn.html).
253    pub fn insert_column<S: Into<String>, C: FnOnce(TableColumn<H>) -> TableColumn<H>>(
254        &mut self,
255        i: usize,
256        column: H,
257        title: S,
258        callback: C,
259    ) {
260        // Update all existing indices
261        for column in &self.columns[i..] {
262            *self.column_indicies.get_mut(&column.column).unwrap() += 1;
263        }
264
265        self.column_indicies.insert(column, i);
266        self.columns
267            .insert(i, callback(TableColumn::new(column, title.into())));
268
269        // Make the first colum the default one
270        if self.columns.len() == 1 {
271            self.set_default_column(column);
272        }
273        self.needs_relayout = true;
274    }
275
276    /// Sets the initially active column of the table.
277    pub fn default_column(mut self, column: H) -> Self {
278        self.set_default_column(column);
279        self
280    }
281
282    /// Sets the initially active column of the table.
283    pub fn set_default_column(&mut self, column: H) {
284        if self.column_indicies.contains_key(&column) {
285            for c in &mut self.columns {
286                c.selected = c.column == column;
287                if c.selected {
288                    c.order = c.default_order;
289                } else {
290                    c.order = Ordering::Equal;
291                }
292            }
293        }
294    }
295
296    /// Sorts the table using the specified table `column` and the passed
297    /// `order`.
298    pub fn sort_by(&mut self, column: H, order: Ordering) {
299        if self.column_indicies.contains_key(&column) {
300            for c in &mut self.columns {
301                // Move selection back to the sorted column.
302                c.selected = c.column == column;
303                if c.selected {
304                    c.order = order;
305                } else {
306                    c.order = Ordering::Equal;
307                }
308            }
309        }
310
311        self.sort_items(column, order);
312    }
313
314    /// Sorts the table using the currently active column and its
315    /// ordering.
316    pub fn sort(&mut self) {
317        if let Some((column, order)) = self.order() {
318            self.sort_items(column, order);
319        }
320    }
321
322    /// Returns the currently active column that is used for sorting
323    /// along with its ordering.
324    ///
325    /// Might return `None` if there are currently no items in the table
326    /// and it has not been sorted yet.
327    pub fn order(&self) -> Option<(H, Ordering)> {
328        for c in &self.columns {
329            if c.order != Ordering::Equal {
330                return Some((c.column, c.order));
331            }
332        }
333        None
334    }
335
336    /// Disables this view.
337    ///
338    /// A disabled view cannot be selected.
339    pub fn disable(&mut self) {
340        self.enabled = false;
341    }
342
343    /// Re-enables this view.
344    pub fn enable(&mut self) {
345        self.enabled = true;
346    }
347
348    /// Enable or disable this view.
349    pub fn set_enabled(&mut self, enabled: bool) {
350        self.enabled = enabled;
351    }
352
353    /// Returns `true` if this view is enabled.
354    pub fn is_enabled(&self) -> bool {
355        self.enabled
356    }
357
358    /// Sets a callback to be used when a selected column is sorted by
359    /// pressing `<Enter>`.
360    ///
361    /// # Example
362    ///
363    /// ```ignore
364    /// table.set_on_sort(|siv: &mut Cursive, column: BasicColumn, order: Ordering| {
365    ///
366    /// });
367    /// ```
368    pub fn set_on_sort<F>(&mut self, cb: F)
369    where
370        F: Fn(&mut Cursive, H, Ordering) + Send + Sync + 'static,
371    {
372        self.on_sort = Some(Arc::new(move |s, h, o| cb(s, h, o)));
373    }
374
375    /// Sets a callback to be used when a selected column is sorted by
376    /// pressing `<Enter>`.
377    ///
378    /// Chainable variant.
379    ///
380    /// # Example
381    ///
382    /// ```ignore
383    /// table.on_sort(|siv: &mut Cursive, column: BasicColumn, order: Ordering| {
384    ///
385    /// });
386    /// ```
387    pub fn on_sort<F>(self, cb: F) -> Self
388    where
389        F: Fn(&mut Cursive, H, Ordering) + Send + Sync + 'static,
390    {
391        self.with(|t| t.set_on_sort(cb))
392    }
393
394    /// Sets a callback to be used when `<Enter>` is pressed while an item
395    /// is selected.
396    ///
397    /// Both the currently selected row and the index of the corresponding item
398    /// within the underlying storage vector will be given to the callback.
399    ///
400    /// # Example
401    ///
402    /// ```ignore
403    /// table.set_on_submit(|siv: &mut Cursive, row: usize, index: usize| {
404    ///
405    /// });
406    /// ```
407    pub fn set_on_submit<F>(&mut self, cb: F)
408    where
409        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
410    {
411        self.on_submit = Some(Arc::new(move |s, row, index| cb(s, row, index)));
412    }
413
414    /// Sets a callback to be used when `<Enter>` is pressed while an item
415    /// is selected.
416    ///
417    /// Both the currently selected row and the index of the corresponding item
418    /// within the underlying storage vector will be given to the callback.
419    ///
420    /// Chainable variant.
421    ///
422    /// # Example
423    ///
424    /// ```ignore
425    /// table.on_submit(|siv: &mut Cursive, row: usize, index: usize| {
426    ///
427    /// });
428    /// ```
429    pub fn on_submit<F>(self, cb: F) -> Self
430    where
431        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
432    {
433        self.with(|t| t.set_on_submit(cb))
434    }
435
436    /// Sets a callback to be used when an item is selected.
437    ///
438    /// Both the currently selected row and the index of the corresponding item
439    /// within the underlying storage vector will be given to the callback.
440    ///
441    /// # Example
442    ///
443    /// ```ignore
444    /// table.set_on_select(|siv: &mut Cursive, row: usize, index: usize| {
445    ///
446    /// });
447    /// ```
448    pub fn set_on_select<F>(&mut self, cb: F)
449    where
450        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
451    {
452        self.on_select = Some(Arc::new(move |s, row, index| cb(s, row, index)));
453    }
454
455    /// Sets a callback to be used when an item is selected.
456    ///
457    /// Both the currently selected row and the index of the corresponding item
458    /// within the underlying storage vector will be given to the callback.
459    ///
460    /// Chainable variant.
461    ///
462    /// # Example
463    ///
464    /// ```ignore
465    /// table.on_select(|siv: &mut Cursive, row: usize, index: usize| {
466    ///
467    /// });
468    /// ```
469    pub fn on_select<F>(self, cb: F) -> Self
470    where
471        F: Fn(&mut Cursive, usize, usize) + Send + Sync + 'static,
472    {
473        self.with(|t| t.set_on_select(cb))
474    }
475
476    /// Removes all items from this view.
477    pub fn clear(&mut self) {
478        self.items.clear();
479        self.rows_to_items.clear();
480        self.focus = 0;
481        self.needs_relayout = true;
482    }
483
484    /// Returns the number of items in this table.
485    pub fn len(&self) -> usize {
486        self.items.len()
487    }
488
489    /// Returns `true` if this table has no items.
490    pub fn is_empty(&self) -> bool {
491        self.items.is_empty()
492    }
493
494    /// Returns the index of the currently selected table row.
495    pub fn row(&self) -> Option<usize> {
496        if self.items.is_empty() {
497            None
498        } else {
499            Some(self.focus)
500        }
501    }
502
503    /// Selects the row at the specified index.
504    pub fn set_selected_row(&mut self, row_index: usize) {
505        self.focus = row_index;
506        self.scroll_core.scroll_to_y(row_index);
507    }
508
509    /// Selects the row at the specified index.
510    ///
511    /// Chainable variant.
512    pub fn selected_row(self, row_index: usize) -> Self {
513        self.with(|t| t.set_selected_row(row_index))
514    }
515
516    /// Sets the contained items of the table.
517    ///
518    /// The currently active sort order is preserved and will be applied to all
519    /// items.
520    pub fn set_items(&mut self, items: Vec<T>) {
521        self.set_items_and_focus(items, 0);
522    }
523
524    fn set_items_and_focus(&mut self, items: Vec<T>, new_location: usize) {
525        self.items = items;
526        self.rows_to_items = Vec::with_capacity(self.items.len());
527
528        for i in 0..self.items.len() {
529            self.rows_to_items.push(i);
530        }
531
532        if let Some((column, order)) = self.order() {
533            // Preserve the selected column if possible.
534            let selected_column = self.columns.iter().find(|c| c.selected).map(|c| c.column);
535            self.sort_by(column, order);
536            if let Some(column) = selected_column {
537                for c in &mut self.columns {
538                    c.selected = c.column == column;
539                }
540            }
541        }
542
543        self.set_selected_item(new_location);
544        self.needs_relayout = true;
545    }
546
547    /// Sets the contained items of the table.
548    ///
549    /// The order of the items will be preserved even when the table is sorted.
550    ///
551    /// Chainable variant.
552    pub fn items(self, items: Vec<T>) -> Self {
553        self.with(|t| t.set_items(items))
554    }
555
556    /// Returns a immmutable reference to the item at the specified index
557    /// within the underlying storage vector.
558    pub fn borrow_item(&self, index: usize) -> Option<&T> {
559        self.items.get(index)
560    }
561
562    /// Returns a mutable reference to the item at the specified index within
563    /// the underlying storage vector.
564    pub fn borrow_item_mut(&mut self, index: usize) -> Option<&mut T> {
565        self.items.get_mut(index)
566    }
567
568    /// Returns a immmutable reference to the items contained within the table.
569    pub fn borrow_items(&mut self) -> &[T] {
570        &self.items
571    }
572
573    /// Returns a mutable reference to the items contained within the table.
574    ///
575    /// Can be used to modify the items in place.
576    pub fn borrow_items_mut(&mut self) -> &mut [T] {
577        self.needs_relayout = true;
578        &mut self.items
579    }
580
581    /// Returns the index of the currently selected item within the underlying
582    /// storage vector.
583    pub fn item(&self) -> Option<usize> {
584        self.rows_to_items.get(self.focus).copied()
585    }
586
587    /// Selects the item at the specified index within the underlying storage
588    /// vector.
589    pub fn set_selected_item(&mut self, item_index: usize) {
590        // TODO optimize the performance for very large item lists
591        if item_index < self.items.len() {
592            for (row, item) in self.rows_to_items.iter().enumerate() {
593                if *item == item_index {
594                    self.focus = row;
595                    self.scroll_core.scroll_to_y(row);
596                    break;
597                }
598            }
599        }
600    }
601
602    /// Selects the item at the specified index within the underlying storage
603    /// vector.
604    ///
605    /// Chainable variant.
606    pub fn selected_item(self, item_index: usize) -> Self {
607        self.with(|t| t.set_selected_item(item_index))
608    }
609
610    /// Inserts a new item into the table.
611    ///
612    /// The currently active sort order is preserved and will be applied to the
613    /// newly inserted item.
614    ///
615    /// If no sort option is set, the item will be added to the end of the table.
616    pub fn insert_item(&mut self, item: T) {
617        self.insert_item_at(self.items.len(), item);
618    }
619
620    /// Inserts a new item into the table.
621    ///
622    /// The currently active sort order is preserved and will be applied to the
623    /// newly inserted item.
624    ///
625    /// If no sort option is set, the item will be inserted at the given index.
626    ///
627    /// # Panics
628    ///
629    /// If `index > self.len()`.
630    pub fn insert_item_at(&mut self, index: usize, item: T) {
631        self.items.push(item);
632
633        // Here we know self.items.len() > 0
634        self.rows_to_items.insert(index, self.items.len() - 1);
635
636        if let Some((column, order)) = self.order() {
637            self.sort_by(column, order);
638        }
639        self.needs_relayout = true;
640    }
641
642    /// Removes the item at the specified index within the underlying storage
643    /// vector and returns it.
644    pub fn remove_item(&mut self, item_index: usize) -> Option<T> {
645        if item_index < self.items.len() {
646            // Move the selection if the currently selected item gets removed
647            if let Some(selected_index) = self.item() {
648                if selected_index == item_index {
649                    self.focus_up(1);
650                }
651            }
652
653            // Remove the sorted reference to the item
654            self.rows_to_items.retain(|i| *i != item_index);
655
656            // Adjust remaining references
657            for ref_index in &mut self.rows_to_items {
658                if *ref_index > item_index {
659                    *ref_index -= 1;
660                }
661            }
662            self.needs_relayout = true;
663
664            // Remove actual item from the underlying storage
665            Some(self.items.remove(item_index))
666        } else {
667            None
668        }
669    }
670
671    /// Removes all items from the underlying storage and returns them.
672    pub fn take_items(&mut self) -> Vec<T> {
673        self.set_selected_row(0);
674        self.rows_to_items.clear();
675        self.needs_relayout = true;
676        self.items.drain(0..).collect()
677    }
678}
679
680impl<T, H> TableView<T, H>
681where
682    T: TableViewItem<H>,
683    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
684{
685    fn draw_columns<C: Fn(&Printer, &TableColumn<H>)>(
686        &self,
687        printer: &Printer,
688        sep: &str,
689        callback: C,
690    ) {
691        let mut column_offset = 0;
692        let column_count = self.columns.len();
693        for (index, column) in self.columns.iter().enumerate() {
694            let printer = &printer.offset((column_offset, 0)).focused(true);
695
696            callback(printer, column);
697
698            if 1 + index < column_count {
699                printer.print((column.width + 1, 0), sep);
700            }
701
702            column_offset += column.width + 3;
703        }
704    }
705
706    fn sort_items(&mut self, column: H, order: Ordering) {
707        if !self.is_empty() {
708            let old_item = self.item();
709
710            let mut rows_to_items = self.rows_to_items.clone();
711            rows_to_items.sort_by(|a, b| {
712                if order == Ordering::Less {
713                    self.items[*a].cmp(&self.items[*b], column)
714                } else {
715                    self.items[*b].cmp(&self.items[*a], column)
716                }
717            });
718            self.rows_to_items = rows_to_items;
719
720            if let Some(old_item) = old_item {
721                self.set_selected_item(old_item);
722            }
723        }
724    }
725
726    fn draw_item(&self, printer: &Printer, i: usize) {
727        self.draw_columns(printer, "┆ ", |printer, column| {
728            let value = self.items[self.rows_to_items[i]].to_column(column.column);
729            column.draw_row(printer, value.as_str());
730        });
731    }
732
733    fn on_focus_change(&self) -> EventResult {
734        let row = self.row().unwrap();
735        let index = self.item().unwrap();
736        EventResult::Consumed(
737            self.on_select
738                .clone()
739                .map(|cb| Callback::from_fn(move |s| cb(s, row, index))),
740        )
741    }
742
743    fn focus_up(&mut self, n: usize) {
744        self.focus -= cmp::min(self.focus, n);
745    }
746
747    fn focus_down(&mut self, n: usize) {
748        self.focus = cmp::min(self.focus + n, self.items.len().saturating_sub(1));
749    }
750
751    fn active_column(&self) -> usize {
752        self.columns.iter().position(|c| c.selected).unwrap_or(0)
753    }
754
755    fn column_cancel(&mut self) {
756        self.column_select = false;
757        for column in &mut self.columns {
758            column.selected = column.order != Ordering::Equal;
759        }
760    }
761
762    fn column_next(&mut self) -> bool {
763        let column = self.active_column();
764        if 1 + column < self.columns.len() {
765            self.columns[column].selected = false;
766            self.columns[column + 1].selected = true;
767            true
768        } else {
769            false
770        }
771    }
772
773    fn column_prev(&mut self) -> bool {
774        let column = self.active_column();
775        if column > 0 {
776            self.columns[column].selected = false;
777            self.columns[column - 1].selected = true;
778            true
779        } else {
780            false
781        }
782    }
783
784    fn column_select(&mut self) -> EventResult {
785        let next = self.active_column();
786        let column = self.columns[next].column;
787        let current = self
788            .columns
789            .iter()
790            .position(|c| c.order != Ordering::Equal)
791            .unwrap_or(0);
792
793        let order = if current != next {
794            self.columns[next].default_order
795        } else if self.columns[current].order == Ordering::Less {
796            Ordering::Greater
797        } else {
798            Ordering::Less
799        };
800
801        self.sort_by(column, order);
802
803        if self.on_sort.is_some() {
804            let c = &self.columns[self.active_column()];
805            let column = c.column;
806            let order = c.order;
807
808            let cb = self.on_sort.clone().unwrap();
809            EventResult::with_cb(move |s| cb(s, column, order))
810        } else {
811            EventResult::Consumed(None)
812        }
813    }
814
815    fn column_for_x(&self, mut x: usize) -> Option<usize> {
816        for (i, col) in self.columns.iter().enumerate() {
817            x = match x.checked_sub(col.width) {
818                None => return Some(i),
819                Some(x) => x.checked_sub(3)?,
820            };
821        }
822
823        None
824    }
825
826    fn draw_content(&self, printer: &Printer) {
827        for i in 0..self.rows_to_items.len() {
828            let printer = printer.offset((0, i));
829            let color = if i == self.focus && self.enabled {
830                if !self.column_select && self.enabled && printer.focused {
831                    theme::ColorStyle::highlight()
832                } else {
833                    theme::ColorStyle::highlight_inactive()
834                }
835            } else {
836                theme::ColorStyle::primary()
837            };
838
839            if i < self.items.len() {
840                printer.with_color(color, |printer| {
841                    self.draw_item(printer, i);
842                });
843            }
844        }
845    }
846
847    fn layout_content(&mut self, size: Vec2) {
848        let column_count = self.columns.len();
849
850        // Split up all columns into sized / unsized groups
851        let (mut sized, mut usized): (Vec<&mut TableColumn<H>>, Vec<&mut TableColumn<H>>) = self
852            .columns
853            .iter_mut()
854            .partition(|c| c.requested_width.is_some());
855
856        // Subtract one for the seperators between our columns (that's column_count - 1)
857        let available_width = size.x.saturating_sub(column_count.saturating_sub(1) * 3);
858
859        // Calculate widths for all requested columns
860        let mut remaining_width = available_width;
861        for column in &mut sized {
862            column.width = match *column.requested_width.as_ref().unwrap() {
863                TableColumnWidth::Percent(width) => cmp::min(
864                    (size.x as f32 / 100.0 * width as f32).ceil() as usize,
865                    remaining_width,
866                ),
867                TableColumnWidth::Absolute(width) => width,
868            };
869            remaining_width = remaining_width.saturating_sub(column.width);
870        }
871
872        // Spread the remaining with across the unsized columns
873        let remaining_columns = usized.len();
874        for column in &mut usized {
875            column.width = (remaining_width as f32 / remaining_columns as f32).floor() as usize;
876        }
877
878        self.needs_relayout = false;
879    }
880
881    fn content_required_size(&mut self, req: Vec2) -> Vec2 {
882        Vec2::new(req.x, self.rows_to_items.len())
883    }
884
885    fn on_inner_event(&mut self, event: Event) -> EventResult {
886        let last_focus = self.focus;
887        match event {
888            Event::Key(Key::Right) => {
889                if self.column_select {
890                    if !self.column_next() {
891                        return EventResult::Ignored;
892                    }
893                } else {
894                    self.column_select = true;
895                }
896            }
897            Event::Key(Key::Left) => {
898                if self.column_select {
899                    if !self.column_prev() {
900                        return EventResult::Ignored;
901                    }
902                } else {
903                    self.column_select = true;
904                }
905            }
906            Event::Key(Key::Up) if self.focus > 0 || self.column_select => {
907                if self.column_select {
908                    self.column_cancel();
909                } else {
910                    self.focus_up(1);
911                }
912            }
913            Event::Key(Key::Down) if self.focus + 1 < self.items.len() || self.column_select => {
914                if self.column_select {
915                    self.column_cancel();
916                } else {
917                    self.focus_down(1);
918                }
919            }
920            Event::Key(Key::PageUp) => {
921                self.column_cancel();
922                self.focus_up(10);
923            }
924            Event::Key(Key::PageDown) => {
925                self.column_cancel();
926                self.focus_down(10);
927            }
928            Event::Key(Key::Home) => {
929                self.column_cancel();
930                self.focus = 0;
931            }
932            Event::Key(Key::End) => {
933                self.column_cancel();
934                self.focus = self.items.len().saturating_sub(1);
935            }
936            Event::Key(Key::Enter) => {
937                if self.column_select {
938                    return self.column_select();
939                } else if !self.is_empty() && self.on_submit.is_some() {
940                    return self.on_submit_event();
941                }
942            }
943            Event::Mouse {
944                position,
945                offset,
946                event: MouseEvent::Press(MouseButton::Left),
947            } if !self.is_empty()
948                && position
949                    .checked_sub(offset)
950                    .map_or(false, |p| p.y == self.focus) =>
951            {
952                self.column_cancel();
953                return self.on_submit_event();
954            }
955            Event::Mouse {
956                position,
957                offset,
958                event: MouseEvent::Press(_),
959            } if !self.is_empty() => match position.checked_sub(offset) {
960                Some(position) if position.y < self.rows_to_items.len() => {
961                    self.column_cancel();
962                    self.focus = position.y;
963                }
964                _ => return EventResult::Ignored,
965            },
966            _ => return EventResult::Ignored,
967        }
968
969        let focus = self.focus;
970
971        if self.column_select {
972            EventResult::Consumed(None)
973        } else if !self.is_empty() && last_focus != focus {
974            self.on_focus_change()
975        } else {
976            EventResult::Ignored
977        }
978    }
979
980    fn inner_important_area(&self, size: Vec2) -> Rect {
981        Rect::from_size((0, self.focus), (size.x, 1))
982    }
983
984    fn on_submit_event(&mut self) -> EventResult {
985        if let Some(ref cb) = &self.on_submit {
986            let cb = Arc::clone(cb);
987            let row = self.row().unwrap();
988            let index = self.item().unwrap();
989            return EventResult::Consumed(Some(Callback::from_fn(move |s| cb(s, row, index))));
990        }
991        EventResult::Ignored
992    }
993}
994
995impl<T, H> View for TableView<T, H>
996where
997    T: TableViewItem<H> + Send + Sync + 'static,
998    H: Eq + Hash + Copy + Clone + Send + Sync + 'static,
999{
1000    fn draw(&self, printer: &Printer) {
1001        self.draw_columns(printer, "╷ ", |printer, column| {
1002            let color = if self.enabled && (column.order != Ordering::Equal || column.selected) {
1003                if self.column_select && column.selected && self.enabled && printer.focused {
1004                    theme::ColorStyle::highlight()
1005                } else {
1006                    theme::ColorStyle::highlight_inactive()
1007                }
1008            } else {
1009                theme::ColorStyle::primary()
1010            };
1011
1012            printer.with_color(color, |printer| {
1013                column.draw_header(printer);
1014            });
1015        });
1016
1017        self.draw_columns(
1018            &printer.offset((0, 1)).focused(true),
1019            "┴─",
1020            |printer, column| {
1021                printer.print_hline((0, 0), column.width + 1, "─");
1022            },
1023        );
1024
1025        // Extend the vertical bars to the end of the view
1026        for y in 2..printer.size.y {
1027            self.draw_columns(&printer.offset((0, y)), "┆ ", |_, _| ());
1028        }
1029
1030        let printer = &printer.offset((0, 2)).focused(true);
1031        scroll::draw(self, printer, Self::draw_content);
1032    }
1033
1034    fn layout(&mut self, size: Vec2) {
1035        scroll::layout(
1036            self,
1037            size.saturating_sub((0, 2)),
1038            self.needs_relayout,
1039            Self::layout_content,
1040            Self::content_required_size,
1041        );
1042    }
1043
1044    fn take_focus(&mut self, _: Direction) -> Result<EventResult, CannotFocus> {
1045        self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
1046    }
1047
1048    fn on_event(&mut self, event: Event) -> EventResult {
1049        if !self.enabled {
1050            return EventResult::Ignored;
1051        }
1052
1053        match event {
1054            Event::Mouse {
1055                position,
1056                offset,
1057                event: MouseEvent::Press(MouseButton::Left),
1058            } if position.checked_sub(offset).map_or(false, |p| p.y == 0) => {
1059                if let Some(position) = position.checked_sub(offset) {
1060                    if let Some(col) = self.column_for_x(position.x) {
1061                        if self.column_select && self.columns[col].selected {
1062                            return self.column_select();
1063                        } else {
1064                            let active = self.active_column();
1065                            self.columns[active].selected = false;
1066                            self.columns[col].selected = true;
1067                            self.column_select = true;
1068                        }
1069                    }
1070                }
1071                EventResult::Ignored
1072            }
1073            event => scroll::on_event(
1074                self,
1075                event.relativized((0, 2)),
1076                Self::on_inner_event,
1077                Self::inner_important_area,
1078            ),
1079        }
1080    }
1081
1082    fn important_area(&self, size: Vec2) -> Rect {
1083        self.inner_important_area(size.saturating_sub((0, 2))) + (0, 2)
1084    }
1085}
1086
1087/// A type used for the construction of columns in a
1088/// [`TableView`](struct.TableView.html).
1089pub struct TableColumn<H> {
1090    column: H,
1091    title: String,
1092    selected: bool,
1093    alignment: HAlign,
1094    order: Ordering,
1095    width: usize,
1096    default_order: Ordering,
1097    requested_width: Option<TableColumnWidth>,
1098}
1099
1100enum TableColumnWidth {
1101    Percent(usize),
1102    Absolute(usize),
1103}
1104
1105impl<H: Copy + Clone + 'static> TableColumn<H> {
1106    /// Sets the default ordering of the column.
1107    pub fn ordering(mut self, order: Ordering) -> Self {
1108        self.default_order = order;
1109        self
1110    }
1111
1112    /// Sets the horizontal text alignment of the column.
1113    pub fn align(mut self, alignment: HAlign) -> Self {
1114        self.alignment = alignment;
1115        self
1116    }
1117
1118    /// Sets how many characters of width this column will try to occupy.
1119    pub fn width(mut self, width: usize) -> Self {
1120        self.requested_width = Some(TableColumnWidth::Absolute(width));
1121        self
1122    }
1123
1124    /// Sets what percentage of the width of the entire table this column will
1125    /// try to occupy.
1126    pub fn width_percent(mut self, width: usize) -> Self {
1127        self.requested_width = Some(TableColumnWidth::Percent(width));
1128        self
1129    }
1130
1131    fn new(column: H, title: String) -> Self {
1132        Self {
1133            column,
1134            title,
1135            selected: false,
1136            alignment: HAlign::Left,
1137            order: Ordering::Equal,
1138            width: 0,
1139            default_order: Ordering::Less,
1140            requested_width: None,
1141        }
1142    }
1143
1144    fn draw_header(&self, printer: &Printer) {
1145        let order = match self.order {
1146            Ordering::Less => "^",
1147            Ordering::Greater => "v",
1148            Ordering::Equal => " ",
1149        };
1150
1151        let header = match self.alignment {
1152            HAlign::Left => format!(
1153                "{:<width$} [{}]",
1154                self.title,
1155                order,
1156                width = self.width.saturating_sub(4)
1157            ),
1158            HAlign::Right => format!(
1159                "{:>width$} [{}]",
1160                self.title,
1161                order,
1162                width = self.width.saturating_sub(4)
1163            ),
1164            HAlign::Center => format!(
1165                "{:^width$} [{}]",
1166                self.title,
1167                order,
1168                width = self.width.saturating_sub(4)
1169            ),
1170        };
1171
1172        printer.print((0, 0), header.as_str());
1173    }
1174
1175    fn draw_row(&self, printer: &Printer, value: &str) {
1176        let value = match self.alignment {
1177            HAlign::Left => format!("{:<width$} ", value, width = self.width),
1178            HAlign::Right => format!("{:>width$} ", value, width = self.width),
1179            HAlign::Center => format!("{:^width$} ", value, width = self.width),
1180        };
1181
1182        printer.print((0, 0), value.as_str());
1183    }
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188    use super::*;
1189
1190    #[derive(Copy, Clone, PartialEq, Eq, Hash)]
1191    enum SimpleColumn {
1192        Name,
1193    }
1194
1195    #[allow(dead_code)]
1196    impl SimpleColumn {
1197        fn as_str(&self) -> &str {
1198            match *self {
1199                SimpleColumn::Name => "Name",
1200            }
1201        }
1202    }
1203
1204    #[derive(Clone, Debug)]
1205    struct SimpleItem {
1206        name: String,
1207    }
1208
1209    impl TableViewItem<SimpleColumn> for SimpleItem {
1210        fn to_column(&self, column: SimpleColumn) -> String {
1211            match column {
1212                SimpleColumn::Name => self.name.to_string(),
1213            }
1214        }
1215
1216        fn cmp(&self, other: &Self, column: SimpleColumn) -> Ordering
1217        where
1218            Self: Sized,
1219        {
1220            match column {
1221                SimpleColumn::Name => self.name.cmp(&other.name),
1222            }
1223        }
1224    }
1225
1226    fn setup_test_table() -> TableView<SimpleItem, SimpleColumn> {
1227        TableView::<SimpleItem, SimpleColumn>::new()
1228            .column(SimpleColumn::Name, "Name", |c| c.width_percent(20))
1229    }
1230
1231    #[test]
1232    fn should_insert_into_existing_table() {
1233        let mut simple_table = setup_test_table();
1234
1235        let mut simple_items = Vec::new();
1236
1237        for i in 1..=10 {
1238            simple_items.push(SimpleItem {
1239                name: format!("{} - Name", i),
1240            });
1241        }
1242
1243        // Insert First Batch of Items
1244        simple_table.set_items(simple_items);
1245
1246        // Test for Additional item insertion
1247        simple_table.insert_item(SimpleItem {
1248            name: format!("{} Name", 11),
1249        });
1250
1251        assert!(simple_table.len() == 11);
1252    }
1253
1254    #[test]
1255    fn should_insert_into_empty_table() {
1256        let mut simple_table = setup_test_table();
1257
1258        // Test for First item insertion
1259        simple_table.insert_item(SimpleItem {
1260            name: format!("{} Name", 1),
1261        });
1262
1263        assert!(simple_table.len() == 1);
1264    }
1265}