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