mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
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:
parent
ebfeef06d9
commit
88037717e6
@ -15,7 +15,7 @@ use crate::Printer;
|
|||||||
use crate::With;
|
use crate::With;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cmp::min;
|
use std::cmp::{min, Ordering};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// View to select an item among a list.
|
/// View to select an item among a list.
|
||||||
@ -384,6 +384,35 @@ impl<T: 'static> SelectView<T> {
|
|||||||
fn focus(&self) -> usize {
|
fn focus(&self) -> usize {
|
||||||
self.focus.get()
|
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.
|
/// 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> {
|
impl<T: 'static> View for SelectView<T> {
|
||||||
fn draw(&self, printer: &Printer<'_, '_>) {
|
fn draw(&self, printer: &Printer<'_, '_>) {
|
||||||
self.last_offset.set(printer.offset);
|
self.last_offset.set(printer.offset);
|
||||||
@ -784,3 +823,101 @@ impl<T> Item<T> {
|
|||||||
Item { label, value }
|
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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user