Merge branch 'refview'

This commit is contained in:
Alexandre Bury 2017-03-25 16:09:38 -07:00
commit 200435fefe
36 changed files with 333 additions and 169 deletions

View File

@ -1,19 +1,19 @@
[package]
authors = ["Alexandre Bury <alexandre.bury@gmail.com>"]
build = "build.rs"
categories = ["command-line-interface", "gui"]
description = "A TUI (Text User Interface) library focused on ease-of-use."
documentation = "https://gyscos.github.io/Cursive/cursive/index.html"
exclude = ["doc", "assets"]
keywords = ["ncurses", "TUI", "UI"]
categories = ["command-line-interface", "gui"]
license = "MIT"
name = "cursive"
readme = "Readme.md"
repository = "https://github.com/gyscos/Cursive"
version = "0.4.1"
[badges]
travis-ci = { repository = "gyscos/Cursive" }
[badges.travis-ci]
repository = "gyscos/Cursive"
[build-dependencies.skeptic]
optional = true
@ -22,6 +22,7 @@ version = "0.7"
[dependencies]
num = "0.1"
odds = "0.2"
owning_ref = "0.2.4"
toml = "0.3"
unicode-segmentation = "1.0"
unicode-width = "0.1"

View File

@ -144,7 +144,7 @@ fn main() {
siv.run();
}
fn show_next(s: &mut Cursive) {
fn show_next(_: &mut Cursive) {
// Empty for now
}
```

View File

@ -40,7 +40,9 @@ fn main() {
fn add_name(s: &mut Cursive) {
fn ok(s: &mut Cursive, name: &str) {
s.find_id::<SelectView<String>>("select").unwrap().add_item_str(name);
s.find_id("select", |view: &mut SelectView<String>| {
view.add_item_str(name)
});
s.pop_layer();
}
@ -51,19 +53,25 @@ fn add_name(s: &mut Cursive) {
.title("Enter a new name")
.button("Ok", |s| {
let name =
s.find_id::<EditView>("name").unwrap().get_content().clone();
s.find_id("name", |view: &mut EditView| {
view.get_content().clone()
}).unwrap();
ok(s, &name);
})
.button("Cancel", |s| s.pop_layer()));
}
fn delete_name(s: &mut Cursive) {
match s.find_id::<SelectView<String>>("select").unwrap().selected_id() {
let selection = s.find_id("select", |view: &mut SelectView<String>| {
view.selected_id()
}).unwrap();
match selection {
None => s.add_layer(Dialog::info("No name to remove")),
Some(focus) => {
s.find_id::<SelectView<String>>("select")
.unwrap()
.remove_item(focus)
s.find_id("select", |view: &mut SelectView<String>| {
view.remove_item(focus)
});
}
}
}
@ -224,8 +232,8 @@ Later, you can ask the Cursive root for this ID and get access to the view.
Just what we need!
Like `BoxView`, `IdView` can be used directly with [`IdView::new`], or through
the [`Identifiable`] trait. [`Cursive::find_id`] can then give you a mutable
reference to the view.
the [`Identifiable`] trait. [`Cursive::find_id`] allows you to run a closure
on the view.
Here's what it looks like in action:
@ -236,8 +244,9 @@ fn add_name(s: &mut Cursive) {
.fixed_width(10))
.title("Enter a new name")
.button("Ok", |s| {
let name =
s.find_id::<EditView>("name").unwrap().get_content().clone();
let name = s.find_id("name", |view: &mut EditView| {
view.get_content().clone()
}).unwrap();
})
.button("Cancel", |s| s.pop_layer()));
}
@ -263,7 +272,9 @@ That way, we can update it with a new item:
```rust,ignore
fn add_name(s: &mut Cursive) {
fn ok(s: &mut Cursive, name: &str) {
s.find_id::<SelectView<String>>("select").unwrap().add_item_str(name);
s.find_id("select", |view: &mut SelectView<String>| {
view.add_item_str(name);
});
s.pop_layer();
}
@ -273,8 +284,9 @@ fn add_name(s: &mut Cursive) {
.fixed_width(10))
.title("Enter a new name")
.button("Ok", |s| {
let name =
s.find_id::<EditView>("name").unwrap().get_content().clone();
let name = s.find_id("name", |v: &mut EditView| {
v.get_content().clone()
}).unwrap();
ok(s, &name);
})
.button("Cancel", |s| s.pop_layer()));
@ -286,13 +298,13 @@ complicated:
```rust,ignore
fn delete_name(s: &mut Cursive) {
match s.find_id::<SelectView<String>>("select").unwrap().selected_id() {
match s.find_id("select", |v: &mut SelectView<String>| {
v.selected_id()
}).unwrap() {
None => s.add_layer(Dialog::info("No name to remove")),
Some(focus) => {
s.find_id::<SelectView<String>>("select")
.unwrap()
.remove_item(focus)
}
Some(focus) => s.find_id("select", |v: &mut SelectView<String>| {
v.remove_item(focus)
}),
}
}
```

View File

@ -1,8 +1,8 @@
extern crate cursive;
use cursive::Cursive;
use cursive::views::{Dialog, EditView, TextView};
use cursive::traits::*;
use cursive::views::{Dialog, EditView, TextView};
fn main() {
let mut siv = Cursive::new();
@ -18,9 +18,9 @@ fn main() {
.with_id("name")
.fixed_width(20))
.button("Ok", |s| {
let name = s.find_id::<EditView>("name")
.unwrap()
.get_content();
let name =
s.find_id("name", |view: &mut EditView| view.get_content())
.unwrap();
show_popup(s, &name);
}));

View File

@ -1,9 +1,9 @@
extern crate cursive;
use cursive::Cursive;
use cursive::traits::*;
use cursive::views::{Checkbox, Dialog, EditView, LinearLayout, ListView,
SelectView, TextView};
use cursive::traits::*;
fn main() {
let mut siv = Cursive::new();
@ -25,11 +25,12 @@ fn main() {
.with_id("email2")
.fixed_width(10)))
.child("Receive spam?",
Checkbox::new().on_change(|s, checked| {
for name in &["email1", "email2"] {
let view: &mut EditView = s.find_id(name).unwrap();
view.set_enabled(checked);
}
Checkbox::new()
.on_change(|s, checked| for name in &["email1",
"email2"] {
s.find_id(name, |view: &mut EditView| {
view.set_enabled(checked)
});
}))
.delimiter()
.child("Age",
@ -39,10 +40,8 @@ fn main() {
.item_str("19-30")
.item_str("31-40")
.item_str("41+"))
.with(|list| {
for i in 0..50 {
.with(|list| for i in 0..50 {
list.add_child(&format!("Item {}", i), EditView::new());
}
})));
siv.run();

View File

@ -13,9 +13,10 @@ fn show_popup(siv: &mut Cursive) {
Dialog::around(TextView::new("Tak!"))
.button("Change", |s| {
// Look for a view tagged "text". We _know_ it's there, so unwrap it.
let view = s.find_id::<TextView>("text").unwrap();
s.find_id("text", |view: &mut TextView| {
let content: String = view.get_content().chars().rev().collect();
view.set_content(content);
});
})
.dismiss_button("Ok"));

39
examples/refcell_view.rs Normal file
View File

@ -0,0 +1,39 @@
extern crate cursive;
use cursive::Cursive;
use cursive::view::{Boxable, Identifiable};
use cursive::views::{LinearLayout, EditView, TextView, Dialog};
fn main() {
let mut siv = Cursive::new();
// Create a dialog with 2 edit fields, and a text view.
// The text view indicates when the 2 fields content match.
siv.add_layer(Dialog::around(LinearLayout::vertical()
.child(EditView::new().on_edit(on_edit).with_id_mut("1"))
.child(EditView::new().on_edit(on_edit).with_id_mut("2"))
.child(TextView::new("match").with_id_mut("match"))
.fixed_width(10))
.button("Quit", Cursive::quit));
siv.run();
}
// Compare the content of the two edit views,
// and update the TextView accordingly.
//
// We'll ignore the `content` and `cursor` arguments,
// and directly retrieve the content from the `Cursive` root.
fn on_edit(siv: &mut Cursive, _content: &str, _cursor: usize) {
// Get handles for each view.
let edit_1 = siv.find_id_mut::<EditView>("1").unwrap();
let edit_2 = siv.find_id_mut::<EditView>("2").unwrap();
// Directly compare references to edit_1 and edit_2.
let matches = edit_1.get_content() == edit_2.get_content();
siv.find_id("match", |v: &mut TextView| v.set_content(if matches {
"match"
} else {
"no match"
}));
}

View File

@ -1,8 +1,8 @@
extern crate cursive;
use cursive::Cursive;
use cursive::views::{Dialog, SliderView};
use cursive::traits::*;
use cursive::views::{Dialog, SliderView};
fn main() {
let mut siv = Cursive::new();
@ -14,7 +14,7 @@ fn main() {
.value(7)
.on_change(|s, v| {
let title = format!("[ {} ]", v);
s.find_id::<Dialog>("dialog").unwrap().set_title(title);
s.find_id("dialog", |view: &mut Dialog| view.set_title(title));
})
.on_enter(|s, v| {
s.pop_layer();

View File

@ -1,12 +1,12 @@
extern crate bear_lib_terminal;
use ::backend;
use ::event::{Event, Key};
use self::bear_lib_terminal::Color as BltColor;
use self::bear_lib_terminal::geometry::Size;
use self::bear_lib_terminal::terminal::{self, Event as BltEvent, KeyCode};
use backend;
use event::{Event, Key};
use std::collections::BTreeMap;
use ::theme::{BaseColor, Color, ColorStyle, Effect};
use theme::{BaseColor, Color, ColorStyle, Effect};
pub struct Concrete {
colours: BTreeMap<i16, (BltColor, BltColor)>,

View File

@ -8,12 +8,12 @@ mod blt;
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
mod curses;
#[cfg(feature = "bear-lib-terminal")]
pub use self::blt::*;
#[cfg(any(feature = "ncurses", feature = "pancurses"))]
pub use self::curses::*;
#[cfg(feature = "termion")]
pub use self::termion::*;
#[cfg(feature = "bear-lib-terminal")]
pub use self::blt::*;
pub trait Backend {
fn init() -> Self;

View File

@ -15,8 +15,8 @@
//! * Relative direction: front or back.
//! Its actual direction depends on the orientation.
use vec::Vec2;
use XY;
use vec::Vec2;
/// Describes a vertical or horizontal orientation for a view.
#[derive(Clone,Copy,Debug,PartialEq)]

View File

@ -64,11 +64,13 @@ extern crate unicode_segmentation;
extern crate unicode_width;
extern crate odds;
extern crate num;
extern crate owning_ref;
#[cfg(feature = "termion")]
#[macro_use]
extern crate chan;
macro_rules! println_stderr(
($($arg:tt)*) => { {
use ::std::io::Write;
@ -365,8 +367,11 @@ impl Cursive {
/// Tries to find the view pointed to by the given selector.
///
/// Runs a closure on the view once it's found, and return the
/// result.
///
/// If the view is not found, or if it is not of the asked type,
/// it returns None.
/// returns None.
///
/// # Examples
///
@ -381,15 +386,18 @@ impl Cursive {
/// .with_id("text"));
///
/// siv.add_global_callback('p', |s| {
/// s.find::<views::TextView>(&view::Selector::Id("text"))
/// .unwrap()
/// .set_content("Text #2");
/// s.find(&view::Selector::Id("text"), |view: &mut views::TextView| {
/// view.set_content("Text #2");
/// });
/// });
/// # }
/// ```
pub fn find<V: View + Any>(&mut self, sel: &view::Selector)
-> Option<&mut V> {
self.screen_mut().find(sel)
pub fn find<V, F, R>(&mut self, sel: &view::Selector, callback: F)
-> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R
{
self.screen_mut().find(sel, callback)
}
/// Tries to find the view identified by the given id.
@ -409,14 +417,29 @@ impl Cursive {
/// .with_id("text"));
///
/// siv.add_global_callback('p', |s| {
/// s.find_id::<views::TextView>("text")
/// .unwrap()
/// .set_content("Text #2");
/// s.find_id("text", |view: &mut views::TextView| {
/// view.set_content("Text #2");
/// });
/// });
/// # }
/// ```
pub fn find_id<V: View + Any>(&mut self, id: &str) -> Option<&mut V> {
self.find(&view::Selector::Id(id))
pub fn find_id<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R
{
self.find(&view::Selector::Id(id), callback)
}
/// Convenient method to find a view wrapped in [`RefCellView`].
///
/// This looks for a `RefCellView<V>` with the given ID, and return
/// a mutable reference to the wrapped view.
///
/// [`RefCellView`]: views/struct.RefCellView.html
pub fn find_id_mut<V>(&mut self, id: &str) -> Option<views::ViewRef<V>>
where V: View + Any
{
self.find_id(id, views::RefCellView::<V>::get_mut)
}
/// Moves the focus to the view identified by `id`.

View File

@ -165,12 +165,12 @@ impl MenuTree {
/// Returns `None` if the given title was not found,
/// or if it wasn't a subtree.
pub fn find_subtree(&mut self, title: &str) -> Option<&mut MenuTree> {
self.find_item(title).and_then(|item| {
if let MenuItem::Subtree(_, ref mut tree) = *item {
self.find_item(title)
.and_then(|item| if let MenuItem::Subtree(_, ref mut tree) =
*item {
Some(Rc::make_mut(tree))
} else {
None
}
})
}
@ -191,5 +191,4 @@ impl MenuTree {
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
}

View File

@ -34,7 +34,9 @@ impl<'a> Printer<'a> {
///
/// But nobody needs to know that.
#[doc(hidden)]
pub fn new<T: Into<Vec2>>(size: T, theme: Theme, backend: &'a backend::Concrete) -> Self {
pub fn new<T: Into<Vec2>>(size: T, theme: Theme,
backend: &'a backend::Concrete)
-> Self {
Printer {
offset: Vec2::zero(),
size: size.into(),

View File

@ -14,7 +14,7 @@ pub struct Prefix {
/// The length (in bytes) of the string.
pub length: usize,
/// The unicode-width of the string.
pub width: usize
pub width: usize,
}
/// Computes the length (number of bytes) and width of a prefix that fits in the given `width`.
@ -42,7 +42,8 @@ pub struct Prefix {
/// prefix(my_text.graphemes(true), 5, "");
/// # }
/// ```
pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str) -> Prefix
pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str)
-> Prefix
where I: Iterator<Item = &'a str>
{
let delimiter_width = delimiter.width();
@ -74,7 +75,7 @@ pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str) -> Prefix
Prefix {
length: length,
width: current_width
width: current_width,
}
}

View File

@ -1,16 +1,26 @@
use view::View;
use views::IdView;
use views::{IdView, RefCellView};
/// Makes a view wrappable in an [`IdView`].
///
/// [`IdView`]: ../views/struct.IdView.html
pub trait Identifiable: View + Sized {
/// Wraps this view into an IdView with the given id.
/// Wraps this view into an `IdView` with the given id.
///
/// This is just a shortcut for `IdView::new(id, self)`
fn with_id(self, id: &str) -> IdView<Self> {
IdView::new(id, self)
}
/// Wraps this view into both a [`RefCellView`] and an `IdView`.
///
/// This allows to call [`Cursive::find_id_mut`].
///
/// [`RefCellView`]: ../views/struct.RefCellView.html
/// [`Cursive::find_id_mut`]: ../struct.Cursive.html#method.find_id_mut
fn with_id_mut(self, id: &str) -> IdView<RefCellView<Self>> {
RefCellView::new(self).with_id(id)
}
}
/// Any `View` implements this trait.

View File

@ -50,10 +50,6 @@ mod identifiable;
mod boxable;
use Printer;
use direction::Direction;
use event::{Event, EventResult};
pub use self::boxable::Boxable;
pub use self::identifiable::Identifiable;
@ -65,9 +61,14 @@ pub use self::size_cache::SizeCache;
pub use self::size_constraint::SizeConstraint;
pub use self::view_path::ViewPath;
pub use self::view_wrapper::ViewWrapper;
use std::any::Any;
use vec::Vec2;
use Printer;
use direction::Direction;
use event::{Event, EventResult};
use vec::Vec2;
use views::RefCellView;
use std::any::Any;
/// Main trait defining a view behaviour.
pub trait View {
@ -122,8 +123,8 @@ pub trait View {
/// Returns None if the path doesn't lead to a view.
///
/// Default implementation always return `None`.
fn find_any(&mut self, &Selector) -> Option<&mut Any> {
None
fn find_any<'a>(&mut self, _: &Selector, _: Box<FnMut(&mut Any) + 'a>) {
// TODO: FnMut -> FnOnce once it works
}
/// Moves the focus to the view identified by the given selector.
@ -157,17 +158,41 @@ pub trait Finder {
///
/// If the view is not found, or if it is not of the asked type,
/// it returns None.
fn find<V: View + Any>(&mut self, sel: &Selector) -> Option<&mut V>;
fn find<V, F, R>(&mut self, sel: &Selector, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R;
/// Convenient method to use `find` with a `view::Selector::Id`.
fn find_id<V: View + Any>(&mut self, id: &str) -> Option<&mut V> {
self.find(&Selector::Id(id))
fn find_id<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R
{
self.find(&Selector::Id(id), callback)
}
}
impl<T: View> Finder for T {
fn find<V: View + Any>(&mut self, sel: &Selector) -> Option<&mut V> {
self.find_any(sel).and_then(|b| b.downcast_mut::<V>())
fn find<V, F, R>(&mut self, sel: &Selector, callback: F) -> Option<R>
where V: View + Any,
F: FnOnce(&mut V) -> R
{
let mut result = None;
{
let result_ref = &mut result;
let mut callback = Some(callback);
let callback = |v: &mut Any| if let Some(callback) =
callback.take() {
if v.is::<V>() {
*result_ref = v.downcast_mut::<V>().map(|v| callback(v));
} else if v.is::<RefCellView<V>>() {
*result_ref = v.downcast_mut::<RefCellView<V>>()
.and_then(|v| v.with_view_mut(callback));
}
};
self.find_any(sel, Box::new(callback));
}
result
}
}

View File

@ -11,8 +11,8 @@ use view::{Selector, View};
/// Default implementation forwards all calls to the child view.
/// Overrides some methods as desired.
///
/// You can use the [`wrap_impl!`] macro to define `get_view` and
/// `get_view_mut` for you.
/// You can use the [`wrap_impl!`] macro to define `with_view` and
/// `with_view_mut` for you.
///
/// [`wrap_impl!`]: ../macro.wrap_impl.html
pub trait ViewWrapper {
@ -20,49 +20,51 @@ pub trait ViewWrapper {
type V: View;
/// Get an immutable reference to the wrapped view.
fn get_view(&self) -> &Self::V;
fn with_view<F, R>(&self, f: F) -> Option<R> where F: FnOnce(&Self::V) -> R;
/// Get a mutable reference to the wrapped view.
fn get_view_mut(&mut self) -> &mut Self::V;
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
where F: FnOnce(&mut Self::V) -> R;
/// Wraps the `draw` method.
fn wrap_draw(&self, printer: &Printer) {
self.get_view().draw(printer);
self.with_view(|v| v.draw(printer));
}
/// Wraps the `required_size` method.
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
self.get_view_mut().required_size(req)
self.with_view_mut(|v| v.required_size(req)).unwrap_or_else(Vec2::zero)
}
/// Wraps the `on_event` method.
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
self.get_view_mut().on_event(ch)
self.with_view_mut(|v| v.on_event(ch)).unwrap_or(EventResult::Ignored)
}
/// Wraps the `layout` method.
fn wrap_layout(&mut self, size: Vec2) {
self.get_view_mut().layout(size);
self.with_view_mut(|v| v.layout(size));
}
/// Wraps the `take_focus` method.
fn wrap_take_focus(&mut self, source: Direction) -> bool {
self.get_view_mut().take_focus(source)
self.with_view_mut(|v| v.take_focus(source)).unwrap_or(false)
}
/// Wraps the `find` method.
fn wrap_find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
self.get_view_mut().find_any(selector)
fn wrap_find_any<'a>(&mut self, selector: &Selector,
callback: Box<FnMut(&mut Any) + 'a>) {
self.with_view_mut(|v| v.find_any(selector, callback));
}
/// Wraps the `focus_view` method.
fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
self.get_view_mut().focus_view(selector)
self.with_view_mut(|v| v.focus_view(selector)).unwrap_or(Err(()))
}
/// Wraps the `needs_relayout` method.
fn wrap_needs_relayout(&self) -> bool {
self.get_view().needs_relayout()
self.with_view(|v| v.needs_relayout()).unwrap_or(true)
}
}
@ -87,8 +89,9 @@ impl<T: ViewWrapper> View for T {
self.wrap_take_focus(source)
}
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
self.wrap_find_any(selector)
fn find_any<'a>(&mut self, selector: &Selector,
callback: Box<FnMut(&mut Any) + 'a>) {
self.wrap_find_any(selector, callback)
}
fn needs_relayout(&self) -> bool {
@ -102,7 +105,7 @@ impl<T: ViewWrapper> View for T {
/// Convenient macro to implement the [`ViewWrapper`] trait.
///
/// It defines the `get_view` and `get_view_mut` implementations,
/// It defines the `with_view` and `with_view_mut` implementations,
/// as well as the `type V` declaration.
///
/// [`ViewWrapper`]: view/trait.ViewWrapper.html
@ -126,12 +129,16 @@ macro_rules! wrap_impl {
(self.$v:ident: $t:ty) => {
type V = $t;
fn get_view(&self) -> &Self::V {
&self.$v
fn with_view<F, R>(&self, f: F) -> Option<R>
where F: FnOnce(&Self::V) -> R
{
Some(f(&self.$v))
}
fn get_view_mut(&mut self) -> &mut Self::V {
&mut self.$v
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
where F: FnOnce(&mut Self::V) -> R
{
Some(f(&mut self.$v))
}
};
}

View File

@ -61,9 +61,7 @@ impl Checkbox {
///
/// Chainable variant.
pub fn checked(self) -> Self {
self.with(|s| {
s.check();
})
self.with(|s| { s.check(); })
}
/// Returns `true` if the checkbox is checked.
@ -80,9 +78,7 @@ impl Checkbox {
///
/// Chainable variant.
pub fn unchecked(self) -> Self {
self.with(|s| {
s.uncheck();
})
self.with(|s| { s.uncheck(); })
}
/// Sets the checkbox state.

View File

@ -399,8 +399,9 @@ impl View for Dialog {
}
}
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
self.content.find_any(selector)
fn find_any<'a>(&mut self, selector: &Selector,
callback: Box<FnMut(&mut Any) + 'a>) {
self.content.find_any(selector, callback);
}
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {

View File

@ -41,9 +41,8 @@ use view::View;
/// .with_id("name")
/// .fixed_width(20))
/// .button("Ok", |s| {
/// let name = s.find_id::<EditView>("name")
/// .unwrap()
/// .get_content();
/// let name = s.find_id("name", |view: &mut EditView| view.get_content())
/// .unwrap();
/// show_popup(s, &name);
/// }));
///

View File

@ -21,10 +21,11 @@ impl<T: View> IdView<T> {
impl<T: View + Any> ViewWrapper for IdView<T> {
wrap_impl!(self.view: T);
fn wrap_find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
fn wrap_find_any<'a>(&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>) {
match selector {
&Selector::Id(id) if id == self.id => Some(&mut self.view),
s => self.view.find_any(s),
&Selector::Id(id) if id == self.id => callback(&mut self.view),
s => self.view.find_any(s, callback),
}
}

View File

@ -13,9 +13,7 @@ pub struct Layer<T: View> {
impl<T: View> Layer<T> {
/// Wraps the given view.
pub fn new(view: T) -> Self {
Layer {
view: view,
}
Layer { view: view }
}
}

View File

@ -395,11 +395,11 @@ impl View for LinearLayout {
}
}
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
self.children
.iter_mut()
.filter_map(|c| c.view.find_any(selector))
.next()
fn find_any<'a>(&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>) {
for child in &mut self.children {
child.view.find_any(selector, Box::new(|any| callback(any)));
}
}
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {

View File

@ -191,7 +191,6 @@ impl View for ListView {
.unwrap_or(0) + 1;
// println_stderr!("Offset: {}", offset);
self.scrollbase.draw(printer, |printer, i| match self.children[i] {
Child::Row(ref label, ref view) => {
printer.print((0, 0), label);
@ -314,12 +313,13 @@ impl View for ListView {
true
}
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
self.children
fn find_any<'a>(&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>) {
for view in self.children
.iter_mut()
.filter_map(Child::view)
.filter_map(|v| v.find_any(selector))
.next()
.filter_map(Child::view) {
view.find_any(selector, Box::new(|any| callback(any)));
}
}
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {

View File

@ -228,12 +228,9 @@ impl View for MenuPopup {
Event::Key(Key::PageDown) => self.scroll_down(5, false),
Event::Key(Key::Home) => self.focus = 0,
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() => {
return match self.menu.children[self.focus] {
MenuItem::Subtree(_, ref tree) => {
@ -243,8 +240,7 @@ impl View for MenuPopup {
};
}
Event::Key(Key::Enter) if !self.menu.children
[self.focus]
Event::Key(Key::Enter) if !self.menu.children[self.focus]
.is_delimiter() => {
return match self.menu.children[self.focus] {
MenuItem::Leaf(_, ref cb) => {

View File

@ -52,6 +52,7 @@ mod menu_popup;
mod panel;
mod progress_bar;
mod radio;
mod refcell_view;
mod select_view;
mod slider_view;
mod shadow_view;
@ -78,6 +79,7 @@ pub use self::menubar::Menubar;
pub use self::panel::Panel;
pub use self::progress_bar::{Counter, ProgressBar};
pub use self::radio::{RadioGroup, RadioButton};
pub use self::refcell_view::{RefCellView, ViewRef};
pub use self::select_view::SelectView;
pub use self::shadow_view::ShadowView;
pub use self::sized_view::SizedView;

View File

@ -118,9 +118,7 @@ impl ProgressBar {
pub fn start<F: FnOnce(Counter) + Send + 'static>(&mut self, f: F) {
let counter: Counter = self.value.clone();
thread::spawn(move || {
f(counter);
});
thread::spawn(move || { f(counter); });
}
/// Starts a function in a separate thread, and monitor the progress.

49
src/views/refcell_view.rs Normal file
View File

@ -0,0 +1,49 @@
use owning_ref::{RcRef, OwningHandle};
use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use view::{View, ViewWrapper};
/// Wrapper around a view to provide interior mutability.
pub struct RefCellView<V: View> {
view: Rc<RefCell<V>>,
}
/// Mutable reference to a view.
pub type ViewRef<V> = OwningHandle<RcRef<RefCell<V>>, RefMut<'static, V>>;
impl<V: View> RefCellView<V> {
/// Wraps `view` in a new `RefCellView`.
pub fn new(view: V) -> Self {
RefCellView { view: Rc::new(RefCell::new(view)) }
}
/// Gets mutable access to the inner view.
pub fn get_mut(&mut self) -> ViewRef<V> {
// TODO: return a standalone item (not tied to our lifetime)
// that bundles `self.view.clone()` and allow mutable reference to
// the inner view.
let cell_ref = RcRef::new(self.view.clone());
OwningHandle::new(cell_ref,
|x| unsafe { x.as_ref() }.unwrap().borrow_mut())
}
}
impl<T: View> ViewWrapper for RefCellView<T> {
type V = T;
fn with_view<F, R>(&self, f: F) -> Option<R>
where F: FnOnce(&Self::V) -> R
{
self.view.try_borrow().ok().map(|v| f(&*v))
}
fn with_view_mut<F, R>(&mut self, f: F) -> Option<R>
where F: FnOnce(&mut Self::V) -> R
{
self.view.try_borrow_mut().ok().map(|mut v| f(&mut *v))
}
}

View File

@ -55,9 +55,7 @@ impl SliderView {
///
/// Chainable variant.
pub fn value(self, value: usize) -> Self {
self.with(|s| {
s.set_value(value);
})
self.with(|s| { s.set_value(value); })
}
/// Sets a callback to be called when the slider is moved.
@ -79,9 +77,7 @@ impl SliderView {
fn get_change_result(&self) -> EventResult {
EventResult::Consumed(self.on_change.clone().map(|cb| {
let value = self.value;
Callback::from_fn(move |s| {
cb(s, value);
})
Callback::from_fn(move |s| { cb(s, value); })
}))
}
@ -148,9 +144,7 @@ impl View for SliderView {
Event::Key(Key::Enter) if self.on_enter.is_some() => {
let value = self.value;
let cb = self.on_enter.clone().unwrap();
EventResult::with_cb(move |s| {
cb(s, value);
})
EventResult::with_cb(move |s| { cb(s, value); })
}
_ => EventResult::Ignored,
}

View File

@ -180,11 +180,11 @@ impl View for StackView {
}
}
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> {
self.layers
.iter_mut()
.filter_map(|l| l.view.find_any(selector))
.next()
fn find_any<'a>(&mut self, selector: &Selector,
mut callback: Box<FnMut(&mut Any) + 'a>) {
for layer in &mut self.layers {
layer.view.find_any(selector, Box::new(|any| callback(any)));
}
}
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {

View File

@ -375,10 +375,9 @@ impl View for TextArea {
} else {
printer.size.x
};
printer.with_effect(effect, |printer| {
for y in 0..printer.size.y {
printer.with_effect(effect,
|printer| for y in 0..printer.size.y {
printer.print_hline((0, y), w, " ");
}
});
// println_stderr!("Content: `{}`", &self.content);

View File

@ -53,6 +53,11 @@ impl TextView {
}
}
/// Creates a new empty `TextView`.
pub fn empty() -> Self {
TextView::new("")
}
/// Enable or disable the view's scrolling capabilities.
///
/// When disabled, the view will never attempt to scroll
@ -98,6 +103,13 @@ impl TextView {
self
}
/// Replace the text in this view.
///
/// Chainable variant.
pub fn content<S: Into<String>>(self, content: S) -> Self {
self.with(|s| s.set_content(content))
}
/// Replace the text in this view.
pub fn set_content<S: Into<String>>(&mut self, content: S) {
let content = content.into();