Add on_select callback for SelectView

And updated the select example to use that instead of an OK button.
This also removes the need for an ID selector.
This commit is contained in:
Alexandre Bury 2015-05-31 20:47:04 -07:00
parent a30a77f39b
commit b64a6b8c34
2 changed files with 35 additions and 17 deletions

View File

@ -4,7 +4,7 @@ use std::fs::File;
use std::io::{BufReader,BufRead}; use std::io::{BufReader,BufRead};
use cursive::Cursive; use cursive::Cursive;
use cursive::view::{Dialog,SelectView,TextView,Selector,BoxView}; use cursive::view::{Dialog,SelectView,TextView,BoxView};
fn main() { fn main() {
// To keep things simple, little error management is done here. // To keep things simple, little error management is done here.
@ -22,17 +22,15 @@ fn main() {
let mut siv = Cursive::new(); let mut siv = Cursive::new();
// Let's add a BoxView to keep the list at a reasonable size - it can scroll anyway. // Let's add a BoxView to keep the list at a reasonable size - it can scroll anyway.
siv.add_layer(Dialog::new(BoxView::new((20,10), select.with_id("city"))) siv.add_layer(Dialog::new(BoxView::new((20,10), select.on_select(|s,city| show_next_window(s,city))))
.title("Where are you from?") .title("Where are you from?"));
.button("Ok", |s| {
// Find the SelectView, gets its selection.
let city = s.find::<SelectView>(&Selector::Id("city")).unwrap().selection().to_string();
// And show the next window.
s.pop_layer();
s.add_layer(Dialog::new(TextView::new(&format!("{} is a great city!", city)))
.button("Quit", |s| s.quit()));
}));
siv.run(); siv.run();
} }
// Let's put the callback in a separate function to keep it clean, but it's not required.
fn show_next_window(siv: &mut Cursive, city: &str) {
siv.pop_layer();
siv.add_layer(Dialog::new(TextView::new(&format!("{} is a great city!", city)))
.button("Quit", |s| s.quit()));
}

View File

@ -1,6 +1,8 @@
use std::cmp::min; use std::cmp::min;
use std::rc::Rc;
use color; use color;
use ::Cursive;
use view::{View,IdView,SizeRequest,DimensionRequest}; use view::{View,IdView,SizeRequest,DimensionRequest};
use event::{Event,EventResult,Key}; use event::{Event,EventResult,Key};
use vec::Vec2; use vec::Vec2;
@ -9,14 +11,14 @@ use super::scroll::ScrollBase;
struct Item<T> { struct Item<T> {
label: String, label: String,
value: T, value: Rc<T>,
} }
impl <T> Item<T> { impl <T> Item<T> {
fn new(label: &str, value: T) -> Self { fn new(label: &str, value: T) -> Self {
Item { Item {
label: label.to_string(), label: label.to_string(),
value: value, value: Rc::new(value),
} }
} }
} }
@ -28,21 +30,32 @@ pub struct SelectView<T=String> {
items: Vec<Item<T>>, items: Vec<Item<T>>,
focus: usize, focus: usize,
scrollbase: ScrollBase, scrollbase: ScrollBase,
select_cb: Option<Rc<Box<Fn(&mut Cursive,&T)>>>,
} }
impl <T> SelectView<T> { impl <T: 'static> SelectView<T> {
/// Creates a new empty SelectView. /// Creates a new empty SelectView.
pub fn new() -> Self { pub fn new() -> Self {
SelectView { SelectView {
items: Vec::new(), items: Vec::new(),
focus: 0, focus: 0,
scrollbase: ScrollBase::new(), scrollbase: ScrollBase::new(),
select_cb: None,
} }
} }
/// Sets a function to be called when an item is selected (when ENTER is pressed).
pub fn on_select<F>(mut self, cb: F) -> Self
where F: Fn(&mut Cursive,&T) + 'static
{
self.select_cb = Some(Rc::new(Box::new(cb)));
self
}
/// Returns the value of the currently selected item. Panics if the list is empty. /// Returns the value of the currently selected item. Panics if the list is empty.
pub fn selection(&self) -> &T { pub fn selection(&self) -> Rc<T> {
&self.items[self.focus].value self.items[self.focus].value.clone()
} }
/// Adds a item to the list, with given label and value. /// Adds a item to the list, with given label and value.
@ -76,7 +89,7 @@ impl SelectView<String> {
} }
impl <T> View for SelectView<T> { impl <T: 'static> View for SelectView<T> {
fn draw(&mut self, printer: &Printer) { fn draw(&mut self, printer: &Printer) {
self.scrollbase.draw(printer, |printer,i| { self.scrollbase.draw(printer, |printer,i| {
@ -115,6 +128,13 @@ impl <T> View for SelectView<T> {
Event::KeyEvent(Key::PageDown) => self.focus = min(self.focus+10,self.items.len()-1), Event::KeyEvent(Key::PageDown) => self.focus = min(self.focus+10,self.items.len()-1),
Event::KeyEvent(Key::Home) => self.focus = 0, Event::KeyEvent(Key::Home) => self.focus = 0,
Event::KeyEvent(Key::End) => self.focus = self.items.len()-1, Event::KeyEvent(Key::End) => self.focus = self.items.len()-1,
Event::KeyEvent(Key::Enter) if self.select_cb.is_some() => {
if let Some(ref cb) = self.select_cb {
let cb = cb.clone();
let v = self.selection();
return EventResult::Consumed(Some(Rc::new(Box::new(move |s| cb(s, &*v)))));
}
},
Event::CharEvent(c) => { Event::CharEvent(c) => {
// Starting from the current focus, find the first item that match the char. // Starting from the current focus, find the first item that match the char.
// Cycle back to the beginning of the list when we reach the end. // Cycle back to the beginning of the list when we reach the end.