Merge remote-tracking branch 'origin/master' into custom_color

This commit is contained in:
Alexandre Bury 2017-06-14 00:44:04 -07:00
commit e4464cb44d
12 changed files with 215 additions and 133 deletions

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "cursive" name = "cursive"
readme = "Readme.md" readme = "Readme.md"
repository = "https://github.com/gyscos/Cursive" repository = "https://github.com/gyscos/Cursive"
version = "0.5.0" version = "0.5.1"
[badges.travis-ci] [badges.travis-ci]
repository = "gyscos/Cursive" repository = "gyscos/Cursive"

View File

@ -47,7 +47,7 @@ fn main() {
} }
``` ```
![Cursive dialog example](https://raw.githubusercontent.com/gyscos/Cursive/master/doc/cursive_example.png) [![Cursive dialog example](https://raw.githubusercontent.com/gyscos/Cursive/master/doc/cursive_example.png)](examples/dialog.rs)
Check out the other [examples](https://github.com/gyscos/Cursive/tree/master/examples) to get these results, and more: Check out the other [examples](https://github.com/gyscos/Cursive/tree/master/examples) to get these results, and more:
@ -76,6 +76,7 @@ Here are a few crates implementing new views for you to use:
* [cursive_table_view](https://github.com/BonsaiDen/cursive_table_view) * [cursive_table_view](https://github.com/BonsaiDen/cursive_table_view)
* [cursive_calendar_view](https://github.com/BonsaiDen/cursive_calendar_view) * [cursive_calendar_view](https://github.com/BonsaiDen/cursive_calendar_view)
* [cursive_tree_view](https://github.com/BonsaiDen/cursive_tree_view)
## Goals ## Goals

View File

@ -1,7 +1,7 @@
extern crate cursive; extern crate cursive;
use cursive::Cursive; use cursive::Cursive;
use cursive::views::{Dialog, KeyEventView, TextView}; use cursive::views::{Dialog, OnEventView, TextView};
use cursive::view::{Offset, Position}; use cursive::view::{Offset, Position};
use cursive::traits::*; use cursive::traits::*;
@ -33,8 +33,8 @@ fn main() {
// Let's wrap the view to give it a recognizable ID, so we can look for it. // Let's wrap the view to give it a recognizable ID, so we can look for it.
// We add the P callback on the textview only (and not globally), // We add the P callback on the textview only (and not globally),
// so that we can't call it when the popup is already visible. // so that we can't call it when the popup is already visible.
siv.add_layer(KeyEventView::new(TextView::new(content).with_id("text")) siv.add_layer(OnEventView::new(TextView::new(content).with_id("text"))
.register('p', |s| show_popup(s))); .on_event('p', |s| show_popup(s)));
siv.run(); siv.run();

View File

@ -66,8 +66,8 @@ impl backend::Backend for Concrete {
// The delay is the time ncurses wait after pressing ESC // The delay is the time ncurses wait after pressing ESC
// to see if it's an escape sequence. // to see if it's an escape sequence.
// Default delay is way too long. 25 is imperceptible yet works fine. // Default delay is way too long. 25 is imperceptible yet works fine.
::std::env::set_var("ESCDELAY", "25");
ncurses::setlocale(ncurses::LcCategory::all, ""); ncurses::setlocale(ncurses::LcCategory::all, "");
::std::env::set_var("ESCDELAY", "25");
ncurses::initscr(); ncurses::initscr();
ncurses::keypad(ncurses::stdscr(), true); ncurses::keypad(ncurses::stdscr(), true);
ncurses::noecho(); ncurses::noecho();

View File

@ -66,8 +66,8 @@ impl Concrete {
impl backend::Backend for Concrete { impl backend::Backend for Concrete {
fn init() -> Self { fn init() -> Self {
::std::env::set_var("ESCDELAY", "25");
let window = pancurses::initscr(); let window = pancurses::initscr();
::std::env::set_var("ESCDELAY", "25");
window.keypad(true); window.keypad(true);
pancurses::noecho(); pancurses::noecho();
pancurses::cbreak(); pancurses::cbreak();

View File

@ -71,6 +71,7 @@ extern crate owning_ref;
extern crate chan; extern crate chan;
#[allow(unused)]
macro_rules! println_stderr( macro_rules! println_stderr(
($($arg:tt)*) => { { ($($arg:tt)*) => { {
use ::std::io::Write; use ::std::io::Write;
@ -578,9 +579,13 @@ impl Cursive {
/// ///
/// Calls [`step(&mut self)`] until [`quit(&mut self)`] is called. /// Calls [`step(&mut self)`] until [`quit(&mut self)`] is called.
/// ///
/// After this function returns, you can call
/// it again and it will start a new loop.
///
/// [`step(&mut self)`]: #method.step /// [`step(&mut self)`]: #method.step
/// [`quit(&mut self)`]: #method.quit /// [`quit(&mut self)`]: #method.quit
pub fn run(&mut self) { pub fn run(&mut self) {
self.running = true;
// And the big event loop begins! // And the big event loop begins!
while self.running { while self.running {

View File

@ -37,6 +37,15 @@ impl Button {
} }
} }
/// Sets the function to be called when the button is pressed.
///
/// Replaces the previous callback.
pub fn set_callback<F>(&mut self, cb: F)
where F: Fn(&mut Cursive) + 'static
{
self.callback = Callback::from_fn(cb);
}
/// Disables this view. /// Disables this view.
/// ///
/// A disabled view cannot be selected. /// A disabled view cannot be selected.

View File

@ -13,22 +13,27 @@ use view::ScrollBase;
use view::Selector; use view::Selector;
use view::View; use view::View;
enum Child { /// Represents a child from a [`ListView`].
///
/// [`ListView`]: struct.ListView.html
pub enum ListChild {
/// A single row, with a label and a view.
Row(String, Box<View>), Row(String, Box<View>),
/// A delimiter between groups.
Delimiter, Delimiter,
} }
impl Child { impl ListChild {
fn label(&self) -> &str { fn label(&self) -> &str {
match *self { match *self {
Child::Row(ref label, _) => label, ListChild::Row(ref label, _) => label,
_ => "", _ => "",
} }
} }
fn view(&mut self) -> Option<&mut Box<View>> { fn view(&mut self) -> Option<&mut Box<View>> {
match *self { match *self {
Child::Row(_, ref mut view) => Some(view), ListChild::Row(_, ref mut view) => Some(view),
_ => None, _ => None,
} }
} }
@ -36,7 +41,7 @@ impl Child {
/// Displays a scrollable list of elements. /// Displays a scrollable list of elements.
pub struct ListView { pub struct ListView {
children: Vec<Child>, children: Vec<ListChild>,
scrollbase: ScrollBase, scrollbase: ScrollBase,
focus: usize, focus: usize,
// This callback is called when the selection is changed. // This callback is called when the selection is changed.
@ -56,10 +61,42 @@ impl ListView {
} }
} }
/// Returns the number of children, including delimiters.
pub fn len(&self) -> usize {
self.children.len()
}
/// Returns `true` if this view contains no children.
///
/// Returns `false` if at least a delimiter or a view is present.
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
/// Returns a reference to the children
pub fn children(&self) -> &[ListChild] {
&self.children[..]
}
/// Returns a reference to the child at the given position.
pub fn get_row(&self, id: usize) -> &ListChild {
&self.children[id]
}
/// Gives mutable access to the child at the given position.
///
/// # Panics
///
/// Panics if `id >= self.len()`.
pub fn row_mut(&mut self, id: usize) -> &mut ListChild {
&mut self.children[id]
}
/// Adds a view to the end of the list. /// Adds a view to the end of the list.
pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) { pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
view.take_focus(direction::Direction::none()); view.take_focus(direction::Direction::none());
self.children.push(Child::Row(label.to_string(), Box::new(view))); self.children.push(ListChild::Row(label.to_string(), Box::new(view)));
} }
/// Removes all children from this view. /// Removes all children from this view.
@ -77,7 +114,7 @@ impl ListView {
/// Adds a delimiter to the end of the list. /// Adds a delimiter to the end of the list.
pub fn add_delimiter(&mut self) { pub fn add_delimiter(&mut self) {
self.children.push(Child::Delimiter); self.children.push(ListChild::Delimiter);
} }
/// Adds a delimiter to the end of the list. /// Adds a delimiter to the end of the list.
@ -112,7 +149,7 @@ impl ListView {
fn iter_mut<'a>(&'a mut self, from_focus: bool, fn iter_mut<'a>(&'a mut self, from_focus: bool,
source: direction::Relative) source: direction::Relative)
-> Box<Iterator<Item = (usize, &mut Child)> + 'a> { -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
match source { match source {
direction::Relative::Front => { direction::Relative::Front => {
@ -162,11 +199,11 @@ impl ListView {
} }
} }
fn try_focus((i, child): (usize, &mut Child), source: direction::Direction) fn try_focus((i, child): (usize, &mut ListChild), source: direction::Direction)
-> Option<usize> { -> Option<usize> {
match *child { match *child {
Child::Delimiter => None, ListChild::Delimiter => None,
Child::Row(_, ref mut view) => { ListChild::Row(_, ref mut view) => {
if view.take_focus(source) { if view.take_focus(source) {
Some(i) Some(i)
} else { } else {
@ -185,18 +222,18 @@ impl View for ListView {
let offset = self.children let offset = self.children
.iter() .iter()
.map(Child::label) .map(ListChild::label)
.map(UnicodeWidthStr::width) .map(UnicodeWidthStr::width)
.max() .max()
.unwrap_or(0) + 1; .unwrap_or(0) + 1;
// println_stderr!("Offset: {}", offset); // println_stderr!("Offset: {}", offset);
self.scrollbase.draw(printer, |printer, i| match self.children[i] { self.scrollbase.draw(printer, |printer, i| match self.children[i] {
Child::Row(ref label, ref view) => { ListChild::Row(ref label, ref view) => {
printer.print((0, 0), label); printer.print((0, 0), label);
view.draw(&printer.offset((offset, 0), i == self.focus)); view.draw(&printer.offset((offset, 0), i == self.focus));
} }
Child::Delimiter => (), ListChild::Delimiter => (),
}); });
} }
@ -204,14 +241,14 @@ impl View for ListView {
// We'll show 2 columns: the labels, and the views. // We'll show 2 columns: the labels, and the views.
let label_width = self.children let label_width = self.children
.iter() .iter()
.map(Child::label) .map(ListChild::label)
.map(UnicodeWidthStr::width) .map(UnicodeWidthStr::width)
.max() .max()
.unwrap_or(0); .unwrap_or(0);
let view_size = self.children let view_size = self.children
.iter_mut() .iter_mut()
.filter_map(Child::view) .filter_map(ListChild::view)
.map(|v| v.required_size(req).x) .map(|v| v.required_size(req).x)
.max() .max()
.unwrap_or(0); .unwrap_or(0);
@ -229,7 +266,7 @@ impl View for ListView {
// We'll show 2 columns: the labels, and the views. // We'll show 2 columns: the labels, and the views.
let label_width = self.children let label_width = self.children
.iter() .iter()
.map(Child::label) .map(ListChild::label)
.map(UnicodeWidthStr::width) .map(UnicodeWidthStr::width)
.max() .max()
.unwrap_or(0); .unwrap_or(0);
@ -246,7 +283,7 @@ impl View for ListView {
// println_stderr!("Available: {}", available); // println_stderr!("Available: {}", available);
for child in self.children.iter_mut().filter_map(Child::view) { for child in self.children.iter_mut().filter_map(ListChild::view) {
child.layout(Vec2::new(available, 1)); child.layout(Vec2::new(available, 1));
} }
} }
@ -256,7 +293,7 @@ impl View for ListView {
return EventResult::Ignored; return EventResult::Ignored;
} }
if let Child::Row(_, ref mut view) = self.children[self.focus] { if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
let result = view.on_event(event.clone()); let result = view.on_event(event.clone());
if result.is_consumed() { if result.is_consumed() {
return result; return result;
@ -317,7 +354,7 @@ impl View for ListView {
mut callback: Box<FnMut(&mut Any) + 'a>) { mut callback: Box<FnMut(&mut Any) + 'a>) {
for view in self.children for view in self.children
.iter_mut() .iter_mut()
.filter_map(Child::view) { .filter_map(ListChild::view) {
view.call_on_any(selector, Box::new(|any| callback(any))); view.call_on_any(selector, Box::new(|any| callback(any)));
} }
} }

View File

@ -12,7 +12,7 @@ use std::rc::Rc;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use vec::Vec2; use vec::Vec2;
use view::{Position, ScrollBase, View}; use view::{Position, ScrollBase, View};
use views::KeyEventView; use views::OnEventView;
/// Popup that shows a list of items. /// Popup that shows a list of items.
pub struct MenuPopup { pub struct MenuPopup {
@ -90,18 +90,31 @@ impl MenuPopup {
/// Sets the alignment for this view. /// Sets the alignment for this view.
pub fn align(mut self, align: Align) -> Self { ///
self.align = align; /// Chainable variant.
pub fn align(self, align: Align) -> Self {
self.with(|s| s.set_align(align))
}
self /// Sets the alignment for this view.
pub fn set_align(&mut self, align: Align) {
self.align = align;
} }
/// Sets a callback to be used when this view is actively dismissed. /// Sets a callback to be used when this view is actively dismissed.
/// ///
/// (When the user hits <ESC>) /// (When the user hits <ESC>)
pub fn on_dismiss<F: 'static + Fn(&mut Cursive)>(mut self, f: F) -> Self { ///
/// Chainable variant.
pub fn on_dismiss<F: 'static + Fn(&mut Cursive)>(self, f: F) -> Self {
self.with(|s| s.set_on_dismiss(f))
}
/// Sets a callback to be used when this view is actively dismissed.
///
/// (When the user hits <ESC>)
pub fn set_on_dismiss<F: 'static + Fn(&mut Cursive)>(&mut self, f: F) {
self.on_dismiss = Some(Callback::from_fn(f)); self.on_dismiss = Some(Callback::from_fn(f));
self
} }
/// Sets a callback to be used when a leaf is activated. /// Sets a callback to be used when a leaf is activated.
@ -109,20 +122,30 @@ impl MenuPopup {
/// Will also be called if a leaf from a subtree is activated. /// Will also be called if a leaf from a subtree is activated.
/// ///
/// Usually used to hide the parent view. /// Usually used to hide the parent view.
pub fn on_action<F: 'static + Fn(&mut Cursive)>(mut self, f: F) -> Self { ///
/// Chainable variant.
pub fn on_action<F: 'static + Fn(&mut Cursive)>(self, f: F) -> Self {
self.with(|s| s.set_on_action(f))
}
/// Sets a callback to be used when a leaf is activated.
///
/// Will also be called if a leaf from a subtree is activated.
///
/// Usually used to hide the parent view.
pub fn set_on_action<F: 'static + Fn(&mut Cursive)>(&mut self, f: F) {
self.on_action = Some(Callback::from_fn(f)); self.on_action = Some(Callback::from_fn(f));
self
} }
fn make_subtree_cb(&self, tree: &Rc<MenuTree>) -> EventResult { fn make_subtree_cb(&self, tree: &Rc<MenuTree>) -> EventResult {
let tree = tree.clone(); let tree = tree.clone();
let max_width = 4 + let max_width = 4 +
self.menu self.menu
.children .children
.iter() .iter()
.map(Self::item_width) .map(Self::item_width)
.max() .max()
.unwrap_or(1); .unwrap_or(1);
let offset = Vec2::new(max_width, self.focus); let offset = Vec2::new(max_width, self.focus);
let action_cb = self.on_action.clone(); let action_cb = self.on_action.clone();
@ -130,7 +153,7 @@ impl MenuPopup {
let action_cb = action_cb.clone(); let action_cb = action_cb.clone();
s.screen_mut() s.screen_mut()
.add_layer_at(Position::parent(offset), .add_layer_at(Position::parent(offset),
KeyEventView::new(MenuPopup::new(tree.clone()) OnEventView::new(MenuPopup::new(tree.clone())
.on_action(move |s| { .on_action(move |s| {
// This will happen when the subtree popup // This will happen when the subtree popup
// activates something; // activates something;
@ -140,7 +163,7 @@ impl MenuPopup {
action_cb.clone()(s); action_cb.clone()(s);
} }
})) }))
.register(Key::Left, |s| s.pop_layer())); .on_event(Key::Left, |s| s.pop_layer()));
}) })
} }
} }
@ -196,11 +219,11 @@ impl View for MenuPopup {
// We can't really shrink our items here, so it's not flexible. // We can't really shrink our items here, so it's not flexible.
let w = 4 + let w = 4 +
self.menu self.menu
.children .children
.iter() .iter()
.map(Self::item_width) .map(Self::item_width)
.max() .max()
.unwrap_or(1); .unwrap_or(1);
let h = 2 + self.menu.children.len(); let h = 2 + self.menu.children.len();
@ -231,38 +254,38 @@ impl View for MenuPopup {
Event::Key(Key::End) => self.focus = self.menu.children.len() - 1, Event::Key(Key::End) => self.focus = self.menu.children.len() - 1,
Event::Key(Key::Right) if self.menu.children[self.focus] Event::Key(Key::Right) if self.menu.children[self.focus]
.is_subtree() => { .is_subtree() => {
return match self.menu.children[self.focus] { return match self.menu.children[self.focus] {
MenuItem::Subtree(_, ref tree) => { MenuItem::Subtree(_, ref tree) => {
self.make_subtree_cb(tree) self.make_subtree_cb(tree)
} }
_ => panic!("Not a subtree???"), _ => panic!("Not a subtree???"),
}; };
} }
Event::Key(Key::Enter) if !self.menu.children[self.focus] Event::Key(Key::Enter) if !self.menu.children[self.focus]
.is_delimiter() => { .is_delimiter() => {
return match self.menu.children[self.focus] { return match self.menu.children[self.focus] {
MenuItem::Leaf(_, ref cb) => { MenuItem::Leaf(_, ref cb) => {
let cb = cb.clone(); let cb = cb.clone();
let action_cb = self.on_action.clone(); let action_cb = self.on_action.clone();
EventResult::with_cb(move |s| { EventResult::with_cb(move |s| {
// Remove ourselves from the face of the earth // Remove ourselves from the face of the earth
s.pop_layer(); s.pop_layer();
// If we had prior orders, do it now. // If we had prior orders, do it now.
if let Some(ref action_cb) = action_cb { if let Some(ref action_cb) = action_cb {
action_cb.clone()(s); action_cb.clone()(s);
} }
// And transmit his last words. // And transmit his last words.
cb.clone()(s); cb.clone()(s);
}) })
} }
MenuItem::Subtree(_, ref tree) => { MenuItem::Subtree(_, ref tree) => {
self.make_subtree_cb(tree) self.make_subtree_cb(tree)
} }
_ => panic!("No delimiter here"), _ => panic!("No delimiter here"),
}; };
} }
_ => return EventResult::Ignored, _ => return EventResult::Ignored,
@ -274,7 +297,6 @@ impl View for MenuPopup {
} }
fn layout(&mut self, size: Vec2) { fn layout(&mut self, size: Vec2) {
self.scrollbase self.scrollbase.set_heights(size.y - 2, self.menu.children.len());
.set_heights(size.y - 2, self.menu.children.len());
} }
} }

View File

@ -10,7 +10,7 @@ use theme::ColorStyle;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use vec::Vec2; use vec::Vec2;
use view::{Position, View}; use view::{Position, View};
use views::{KeyEventView, MenuPopup}; use views::{OnEventView, MenuPopup};
/// Current state of the menubar /// Current state of the menubar
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
@ -108,9 +108,7 @@ impl Menubar {
/// ///
/// Returns `None` if `i > self.len()` /// Returns `None` if `i > self.len()`
pub fn get_subtree(&mut self, i: usize) -> Option<&mut MenuTree> { pub fn get_subtree(&mut self, i: usize) -> Option<&mut MenuTree> {
self.menus self.menus.get_mut(i).map(|&mut (_, ref mut tree)| Rc::make_mut(tree))
.get_mut(i)
.map(|&mut (_, ref mut tree)| Rc::make_mut(tree))
} }
/// Looks for an item with the given label. /// Looks for an item with the given label.
@ -129,9 +127,7 @@ impl Menubar {
/// ///
/// Returns `None` if no such label was found. /// Returns `None` if no such label was found.
pub fn find_position(&mut self, label: &str) -> Option<usize> { pub fn find_position(&mut self, label: &str) -> Option<usize> {
self.menus self.menus.iter().position(|&(ref l, _)| l == label)
.iter()
.position(|&(ref l, _)| l == label)
} }
/// Remove the item at the given position. /// Remove the item at the given position.
@ -145,37 +141,38 @@ fn show_child(s: &mut Cursive, offset: (usize, usize), menu: Rc<MenuTree>) {
// Also adds two key callbacks on this new view, to handle `left` and // Also adds two key callbacks on this new view, to handle `left` and
// `right` key presses. // `right` key presses.
// (If the view itself listens for a `left` or `right` press, it will // (If the view itself listens for a `left` or `right` press, it will
// consume it before our KeyEventView. This means sub-menus can properly // consume it before our OnEventView. This means sub-menus can properly
// be entered.) // be entered.)
s.screen_mut() s.screen_mut()
.add_layer_at(Position::absolute(offset), .add_layer_at(Position::absolute(offset),
KeyEventView::new(MenuPopup::new(menu) OnEventView::new(MenuPopup::new(menu)
.on_dismiss(|s| s.select_menubar()) .on_dismiss(|s| {
.on_action(|s| { s.select_menubar()
s.menubar().state = State::Inactive })
})) .on_action(|s| {
.register(Key::Right, |s| { s.menubar().state =
s.pop_layer(); State::Inactive
s.select_menubar(); }))
// Act as if we sent "Right" then "Down" .on_event(Key::Right, |s| {
s.menubar().on_event(Event::Key(Key::Right)).process(s); s.pop_layer();
if let EventResult::Consumed(Some(cb)) = s.select_menubar();
s.menubar() // Act as if we sent "Right" then "Down"
.on_event(Event::Key(Key::Down)) { s.menubar().on_event(Event::Key(Key::Right)).process(s);
cb(s); if let EventResult::Consumed(Some(cb)) =
} s.menubar().on_event(Event::Key(Key::Down)) {
}) cb(s);
.register(Key::Left, |s| { }
s.pop_layer(); })
s.select_menubar(); .on_event(Key::Left, |s| {
// Act as if we sent "Left" then "Down" s.pop_layer();
s.menubar().on_event(Event::Key(Key::Left)).process(s); s.select_menubar();
if let EventResult::Consumed(Some(cb)) = // Act as if we sent "Left" then "Down"
s.menubar() s.menubar().on_event(Event::Key(Key::Left)).process(s);
.on_event(Event::Key(Key::Down)) { if let EventResult::Consumed(Some(cb)) =
cb(s); s.menubar().on_event(Event::Key(Key::Down)) {
} cb(s);
})); }
}));
} }
@ -226,16 +223,19 @@ impl View for Menubar {
// since we don't know when it will be called. // since we don't know when it will be called.
let menu = self.menus[self.focus].1.clone(); let menu = self.menus[self.focus].1.clone();
self.state = State::Submenu; self.state = State::Submenu;
let offset = (self.menus[..self.focus] let offset =
.iter() (self.menus[..self.focus]
.map(|&(ref title, _)| title.width() + 2) .iter()
.fold(0, |a, b| a + b), .map(|&(ref title, _)| title.width() + 2)
if self.autohide { 1 } else { 0 }); .fold(0, |a, b| a + b),
if self.autohide { 1 } else { 0 });
// Since the closure will be called multiple times, // Since the closure will be called multiple times,
// we also need a new Rc on every call. // we also need a new Rc on every call.
return EventResult::with_cb(move |s| { return EventResult::with_cb(move |s| {
show_child(s, offset, menu.clone()) show_child(s,
}); offset,
menu.clone())
});
} }
_ => return EventResult::Ignored, _ => return EventResult::Ignored,
} }

View File

@ -43,7 +43,7 @@ mod dialog;
mod dummy; mod dummy;
mod edit_view; mod edit_view;
mod id_view; mod id_view;
mod key_event_view; mod on_event_view;
mod layer; mod layer;
mod linear_layout; mod linear_layout;
mod list_view; mod list_view;
@ -69,10 +69,10 @@ pub use self::dialog::Dialog;
pub use self::dummy::DummyView; pub use self::dummy::DummyView;
pub use self::edit_view::EditView; pub use self::edit_view::EditView;
pub use self::id_view::{IdView, ViewRef}; pub use self::id_view::{IdView, ViewRef};
pub use self::key_event_view::KeyEventView; pub use self::on_event_view::OnEventView;
pub use self::layer::Layer; pub use self::layer::Layer;
pub use self::linear_layout::LinearLayout; pub use self::linear_layout::LinearLayout;
pub use self::list_view::ListView; pub use self::list_view::{ListChild, ListView};
pub use self::menu_popup::MenuPopup; pub use self::menu_popup::MenuPopup;
pub use self::menubar::Menubar; pub use self::menubar::Menubar;
pub use self::panel::Panel; pub use self::panel::Panel;

View File

@ -4,6 +4,7 @@ use Cursive;
use event::{Callback, Event, EventResult}; use event::{Callback, Event, EventResult};
use std::collections::HashMap; use std::collections::HashMap;
use view::{View, ViewWrapper}; use view::{View, ViewWrapper};
use With;
/// A simple wrapper view that catches some ignored event from its child. /// A simple wrapper view that catches some ignored event from its child.
/// ///
@ -13,36 +14,43 @@ use view::{View, ViewWrapper};
/// ///
/// ``` /// ```
/// # use cursive::event;; /// # use cursive::event;;
/// # use cursive::views::{KeyEventView, TextView}; /// # use cursive::views::{OnEventView, TextView};
/// let view = KeyEventView::new(TextView::new("This view has an event!")) /// let view = OnEventView::new(TextView::new("This view has an event!"))
/// .register('q', |s| s.quit()) /// .on_event('q', |s| s.quit())
/// .register(event::Key::Esc, |s| s.quit()); /// .on_event(event::Key::Esc, |s| s.quit());
/// ``` /// ```
pub struct KeyEventView<T: View> { pub struct OnEventView<T: View> {
content: T, content: T,
callbacks: HashMap<Event, Callback>, callbacks: HashMap<Event, Callback>,
} }
impl<T: View> KeyEventView<T> { impl<T: View> OnEventView<T> {
/// Wraps the given view in a new KeyEventView. /// Wraps the given view in a new OnEventView.
pub fn new(view: T) -> Self { pub fn new(view: T) -> Self {
KeyEventView { OnEventView {
content: view, content: view,
callbacks: HashMap::new(), callbacks: HashMap::new(),
} }
} }
/// Registers a callback when the given key is ignored by the child. /// Registers a callback when the given event is ignored by the child.
pub fn register<F, E: Into<Event>>(mut self, event: E, cb: F) -> Self ///
/// Chainable variant.
pub fn on_event<F, E: Into<Event>>(self, event: E, cb: F) -> Self
where F: Fn(&mut Cursive) + 'static
{
self.with(|s| s.set_on_event(event, cb))
}
/// Registers a callback when the given event is ignored by the child.
pub fn set_on_event<F, E: Into<Event>>(&mut self, event: E, cb: F)
where F: Fn(&mut Cursive) + 'static where F: Fn(&mut Cursive) + 'static
{ {
self.callbacks.insert(event.into(), Callback::from_fn(cb)); self.callbacks.insert(event.into(), Callback::from_fn(cb));
self
} }
} }
impl<T: View> ViewWrapper for KeyEventView<T> { impl<T: View> ViewWrapper for OnEventView<T> {
wrap_impl!(self.content: T); wrap_impl!(self.content: T);
fn wrap_on_event(&mut self, event: Event) -> EventResult { fn wrap_on_event(&mut self, event: Event) -> EventResult {