mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Merge remote-tracking branch 'origin/master' into custom_color
This commit is contained in:
commit
e4464cb44d
@ -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"
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
Loading…
Reference in New Issue
Block a user