Add a sort method to SelectView, to easily sort all contained items lexicographically by their label. (#329)

* Add a sort method to SelectView, to easily sort all contained items lexicographically by their label.

* Add more sort methods to SelectView, bringing the API up to parity with Vec (except SelectView does not expose _unstable_ sorting).

* Rename SelectView::sort to sort_by_label to make it clear that it does not sort by the ordering of the items.

* The Ord implementation for select_view::Item is not necessary.

* Implement a SelectView::sort method for when the item values are orderable.
This commit is contained in:
Chris Vest 2019-03-30 18:22:08 +01:00 committed by Alexandre Bury
parent ebfeef06d9
commit 88037717e6

View File

@ -15,7 +15,7 @@ use crate::Printer;
use crate::With;
use std::borrow::Borrow;
use std::cell::Cell;
use std::cmp::min;
use std::cmp::{min, Ordering};
use std::rc::Rc;
/// View to select an item among a list.
@ -384,6 +384,35 @@ impl<T: 'static> SelectView<T> {
fn focus(&self) -> usize {
self.focus.get()
}
/// Sort the current items lexicographically by their label.
/// Note that this does not change the current focus index, which means that the current
/// selection will likely be changed by the sorting.
/// This sort is stable: items with identical label will not be reordered.
pub fn sort_by_label(&mut self) {
self.items.sort_by(|a, b| a.label.source().cmp(b.label.source()));
}
/// Sort the current items with the given comparator function.
/// Note that this does not change the current focus index, which means that the current
/// selection will likely be changed by the sorting.
/// The given comparator function must define a total order for the items.
/// If the comparator function does not define a total order, then the order after the sort is
/// unspecified.
/// This sort is stable: equal items will not be reordered.
pub fn sort_by<F>(&mut self, mut compare: F)
where F: FnMut(&T, &T) -> Ordering {
self.items.sort_by(|a, b| compare(&a.value, &b.value));
}
/// Sort the current items with the given key extraction function.
/// Note that this does not change the current focus index, which means that the current
/// selection will likely be changed by the sorting.
/// This sort is stable: items with equal keys will not be reordered.
pub fn sort_by_key<K, F>(&mut self, mut key_of: F)
where F: FnMut(&T) -> K, K: Ord {
self.items.sort_by_key(|item| key_of(&item.value));
}
/// Moves the selection to the given position.
///
@ -671,6 +700,16 @@ impl SelectView<String> {
}
}
impl<T: 'static> SelectView<T> where T: Ord {
/// Sort the current items by their natural ordering.
/// Note that this does not change the current focus index, which means that the current
/// selection will likely be changed by the sorting.
/// This sort is stable: items that are equal will not be reordered.
pub fn sort(&mut self) {
self.items.sort_by(|a, b| a.value.cmp(&b.value));
}
}
impl<T: 'static> View for SelectView<T> {
fn draw(&self, printer: &Printer<'_, '_>) {
self.last_offset.set(printer.offset);
@ -784,3 +823,101 @@ impl<T> Item<T> {
Item { label, value }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn select_view_sorting() {
// We add items in no particular order, from going by their label.
let mut view = SelectView::new();
view.add_item_str("Y");
view.add_item_str("Z");
view.add_item_str("X");
// Then sorting the list...
view.sort_by_label();
// ... should observe the items in sorted order.
// And focus is NOT changed by the sorting, so the first item is "X".
assert_eq!(view.selection(), Some(Rc::new(String::from("X"))));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(String::from("Y"))));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(String::from("Z"))));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(String::from("Z"))));
}
#[test]
fn select_view_sorting_with_comparator() {
// We add items in no particular order, from going by their value.
let mut view = SelectView::new();
view.add_item("Y", 2);
view.add_item("Z", 1);
view.add_item("X", 3);
// Then sorting the list...
view.sort_by(|a, b| a.cmp(b));
// ... should observe the items in sorted order.
// And focus is NOT changed by the sorting, so the first item is "X".
assert_eq!(view.selection(), Some(Rc::new(1)));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(2)));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(3)));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(3)));
}
#[test]
fn select_view_sorting_by_key() {
// We add items in no particular order, from going by their key value.
#[derive(Eq, PartialEq, Debug)]
struct MyStruct {
key: i32
}
let mut view = SelectView::new();
view.add_item("Y", MyStruct{ key: 2 });
view.add_item("Z", MyStruct{ key: 1 });
view.add_item("X", MyStruct{ key: 3 });
// Then sorting the list...
view.sort_by_key(|s| s.key);
// ... should observe the items in sorted order.
// And focus is NOT changed by the sorting, so the first item is "X".
assert_eq!(view.selection(), Some(Rc::new(MyStruct { key: 1 })));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(MyStruct { key: 2 })));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(MyStruct { key: 3 })));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(MyStruct { key: 3 })));
}
#[test]
fn select_view_sorting_orderable_items() {
// We add items in no particular order, from going by their value.
let mut view = SelectView::new();
view.add_item("Y", 2);
view.add_item("Z", 1);
view.add_item("X", 3);
// Then sorting the list...
view.sort();
// ... should observe the items in sorted order.
// And focus is NOT changed by the sorting, so the first item is "X".
assert_eq!(view.selection(), Some(Rc::new(1)));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(2)));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(3)));
view.on_event(Event::Key(Key::Down));
assert_eq!(view.selection(), Some(Rc::new(3)));
}
}