View::find_any now takes a closure instead of returning reference

Also updated Finder::find and find_id.
This commit is contained in:
Alexandre Bury 2017-02-06 18:18:17 -08:00
parent 57c1c1ad61
commit b63762d441
34 changed files with 233 additions and 200 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
extern crate ncurses; extern crate ncurses;
use backend;
use event::{Event, Key};
use self::super::find_closest; use self::super::find_closest;
use backend;
use event::{Event, Key};
use theme::{Color, ColorStyle, Effect}; use theme::{Color, ColorStyle, Effect};
use utf8; use utf8;

View File

@ -1,10 +1,10 @@
extern crate pancurses; extern crate pancurses;
use backend;
use event::{Event, Key};
use self::super::find_closest; use self::super::find_closest;
use backend;
use event::{Event, Key};
use std::cell::Cell; use std::cell::Cell;
use theme::{Color, ColorStyle, Effect}; use theme::{Color, ColorStyle, Effect};
use utf8; use utf8;
@ -23,7 +23,8 @@ impl backend::Backend for Concrete {
pancurses::cbreak(); pancurses::cbreak();
pancurses::start_color(); pancurses::start_color();
pancurses::curs_set(0); pancurses::curs_set(0);
window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as pancurses::chtype)); window.bkgd(pancurses::COLOR_PAIR(ColorStyle::Background.id() as
pancurses::chtype));
Concrete { Concrete {
window: window, window: window,
@ -66,7 +67,8 @@ impl backend::Backend for Concrete {
self.current_style.set(current_style); self.current_style.set(current_style);
// self.window.attroff(style); // self.window.attroff(style);
self.window.attron(pancurses::COLOR_PAIR(current_style.id() as pancurses::chtype)); self.window.attron(pancurses::COLOR_PAIR(current_style.id() as
pancurses::chtype));
} }
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) { fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
@ -101,7 +103,9 @@ impl backend::Backend for Concrete {
// TODO: wait for a very short delay. If more keys are // TODO: wait for a very short delay. If more keys are
// pipelined, it may be an escape sequence. // pipelined, it may be an escape sequence.
pancurses::Input::Character('\u{7f}') | pancurses::Input::Character('\u{7f}') |
pancurses::Input::Character('\u{8}') => Event::Key(Key::Backspace), pancurses::Input::Character('\u{8}') => {
Event::Key(Key::Backspace)
}
pancurses::Input::Character('\u{9}') => Event::Key(Key::Tab), pancurses::Input::Character('\u{9}') => Event::Key(Key::Tab),
pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc), pancurses::Input::Character('\u{1b}') => Event::Key(Key::Esc),
pancurses::Input::Character(c) if 32 <= (c as u32) && pancurses::Input::Character(c) if 32 <= (c as u32) &&

View File

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

View File

@ -2,14 +2,14 @@ extern crate termion;
extern crate chan_signal; extern crate chan_signal;
use ::backend;
use chan;
use ::event::{Event, Key};
use self::termion::color as tcolor; use self::termion::color as tcolor;
use self::termion::event::Key as TKey; use self::termion::event::Key as TKey;
use self::termion::input::TermRead; use self::termion::input::TermRead;
use self::termion::raw::IntoRawMode; use self::termion::raw::IntoRawMode;
use self::termion::style as tstyle; use self::termion::style as tstyle;
use backend;
use chan;
use event::{Event, Key};
use std::cell::Cell; use std::cell::Cell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
@ -17,7 +17,7 @@ use std::io::Write;
use std::thread; use std::thread;
use std::time; use std::time;
use ::theme; use theme;
pub struct Concrete { pub struct Concrete {
terminal: termion::raw::RawTerminal<::std::io::Stdout>, terminal: termion::raw::RawTerminal<::std::io::Stdout>,

View File

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

View File

@ -68,6 +68,7 @@ extern crate num;
#[macro_use] #[macro_use]
extern crate chan; extern crate chan;
macro_rules! println_stderr( macro_rules! println_stderr(
($($arg:tt)*) => { { ($($arg:tt)*) => { {
use ::std::io::Write; use ::std::io::Write;
@ -364,8 +365,11 @@ impl Cursive {
/// Tries to find the view pointed to by the given selector. /// 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, /// If the view is not found, or if it is not of the asked type,
/// it returns None. /// returns None.
/// ///
/// # Examples /// # Examples
/// ///
@ -380,15 +384,18 @@ impl Cursive {
/// .with_id("text")); /// .with_id("text"));
/// ///
/// siv.add_global_callback('p', |s| { /// siv.add_global_callback('p', |s| {
/// s.find::<views::TextView>(&view::Selector::Id("text")) /// s.find(&view::Selector::Id("text"), |view: &mut views::TextView| {
/// .unwrap() /// view.set_content("Text #2");
/// .set_content("Text #2"); /// });
/// }); /// });
/// # } /// # }
/// ``` /// ```
pub fn find<V: View + Any>(&mut self, sel: &view::Selector) pub fn find<V, F, R>(&mut self, sel: &view::Selector, callback: F)
-> Option<&mut V> { -> Option<R>
self.screen_mut().find(sel) where V: View + Any,
F: FnOnce(&mut V) -> R
{
self.screen_mut().find(sel, callback)
} }
/// Convenient method to use `find` with a `view::Selector::Id`. /// Convenient method to use `find` with a `view::Selector::Id`.
@ -406,14 +413,17 @@ impl Cursive {
/// .with_id("text")); /// .with_id("text"));
/// ///
/// siv.add_global_callback('p', |s| { /// siv.add_global_callback('p', |s| {
/// s.find_id::<views::TextView>("text") /// s.find_id("text", |view: &mut views::TextView| {
/// .unwrap() /// view.set_content("Text #2");
/// .set_content("Text #2"); /// });
/// }); /// });
/// # } /// # }
/// ``` /// ```
pub fn find_id<V: View + Any>(&mut self, id: &str) -> Option<&mut V> { pub fn find_id<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
self.find(&view::Selector::Id(id)) where V: View + Any,
F: FnOnce(&mut V) -> R
{
self.find(&view::Selector::Id(id), callback)
} }
/// Adds a global callback. /// Adds a global callback.

View File

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

View File

@ -34,7 +34,9 @@ impl<'a> Printer<'a> {
/// ///
/// But nobody needs to know that. /// But nobody needs to know that.
#[doc(hidden)] #[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 { Printer {
offset: Vec2::zero(), offset: Vec2::zero(),
size: size.into(), size: size.into(),

View File

@ -14,7 +14,7 @@ pub struct Prefix {
/// The length (in bytes) of the string. /// The length (in bytes) of the string.
pub length: usize, pub length: usize,
/// The unicode-width of the string. /// 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`. /// 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, ""); /// 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> where I: Iterator<Item = &'a str>
{ {
let delimiter_width = delimiter.width(); let delimiter_width = delimiter.width();
@ -70,7 +71,7 @@ pub fn prefix<'a, I>(iter: I, available_width: usize, delimiter: &str) -> Prefix
Prefix { Prefix {
length: length, length: length,
width: current_width width: current_width,
} }
} }

View File

@ -50,10 +50,6 @@ mod identifiable;
mod boxable; mod boxable;
use Printer;
use direction::Direction;
use event::{Event, EventResult};
pub use self::boxable::Boxable; pub use self::boxable::Boxable;
pub use self::identifiable::Identifiable; pub use self::identifiable::Identifiable;
@ -65,6 +61,10 @@ pub use self::size_cache::SizeCache;
pub use self::size_constraint::SizeConstraint; pub use self::size_constraint::SizeConstraint;
pub use self::view_path::ViewPath; pub use self::view_path::ViewPath;
pub use self::view_wrapper::ViewWrapper; pub use self::view_wrapper::ViewWrapper;
use Printer;
use direction::Direction;
use event::{Event, EventResult};
use std::any::Any; use std::any::Any;
use vec::Vec2; use vec::Vec2;
@ -122,8 +122,9 @@ pub trait View {
/// Returns None if the path doesn't lead to a view. /// Returns None if the path doesn't lead to a view.
/// ///
/// Default implementation always return `None`. /// Default implementation always return `None`.
fn find_any(&mut self, &Selector) -> Option<&mut Any> { fn find_any<'a>(&mut self, _: &Selector,
None _: Box<FnMut(&mut Any) + 'a>) {
// TODO: FnMut -> FnOnce once it works
} }
/// This view is offered focus. Will it take it? /// This view is offered focus. Will it take it?
@ -150,17 +151,38 @@ pub trait Finder {
/// ///
/// If the view is not found, or if it is not of the asked type, /// If the view is not found, or if it is not of the asked type,
/// it returns None. /// 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`. /// Convenient method to use `find` with a `view::Selector::Id`.
fn find_id<V: View + Any>(&mut self, id: &str) -> Option<&mut V> { fn find_id<V, F, R>(&mut self, id: &str, callback: F) -> Option<R>
self.find(&Selector::Id(id)) where V: View + Any,
F: FnOnce(&mut V) -> R
{
self.find(&Selector::Id(id), callback)
} }
} }
impl<T: View> Finder for T { impl<T: View> Finder for T {
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>
self.find_any(sel).and_then(|b| b.downcast_mut::<V>()) 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 let Some(v) = v.downcast_mut::<V>() {
*result_ref = Some(callback(v));
}
};
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. /// Default implementation forwards all calls to the child view.
/// Overrides some methods as desired. /// Overrides some methods as desired.
/// ///
/// You can use the [`wrap_impl!`] macro to define `get_view` and /// You can use the [`wrap_impl!`] macro to define `with_view` and
/// `get_view_mut` for you. /// `with_view_mut` for you.
/// ///
/// [`wrap_impl!`]: ../macro.wrap_impl.html /// [`wrap_impl!`]: ../macro.wrap_impl.html
pub trait ViewWrapper { pub trait ViewWrapper {
@ -20,44 +20,46 @@ pub trait ViewWrapper {
type V: View; type V: View;
/// Get an immutable reference to the wrapped view. /// Get an immutable reference to the wrapped view.
fn get_view(&self) -> &Self::V; fn with_view<F, R>(&self, f: F) -> R where F: FnOnce(&Self::V) -> R;
/// Get a mutable reference to the wrapped view. /// 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) -> R
where F: FnOnce(&mut Self::V) -> R;
/// Wraps the `draw` method. /// Wraps the `draw` method.
fn wrap_draw(&self, printer: &Printer) { fn wrap_draw(&self, printer: &Printer) {
self.get_view().draw(printer); self.with_view(|v| v.draw(printer));
} }
/// Wraps the `required_size` method. /// Wraps the `required_size` method.
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 { 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))
} }
/// Wraps the `on_event` method. /// Wraps the `on_event` method.
fn wrap_on_event(&mut self, ch: Event) -> EventResult { 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))
} }
/// Wraps the `layout` method. /// Wraps the `layout` method.
fn wrap_layout(&mut self, size: Vec2) { 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. /// Wraps the `take_focus` method.
fn wrap_take_focus(&mut self, source: Direction) -> bool { 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))
} }
/// Wraps the `find` method. /// Wraps the `find` method.
fn wrap_find_any(&mut self, selector: &Selector) -> Option<&mut Any> { fn wrap_find_any<'a>(&mut self, selector: &Selector,
self.get_view_mut().find_any(selector) callback: Box<FnMut(&mut Any) + 'a>) {
self.with_view_mut(|v| v.find_any(selector, callback));
} }
/// Wraps the `needs_relayout` method. /// Wraps the `needs_relayout` method.
fn wrap_needs_relayout(&self) -> bool { fn wrap_needs_relayout(&self) -> bool {
self.get_view().needs_relayout() self.with_view(|v| v.needs_relayout())
} }
} }
@ -82,8 +84,9 @@ impl<T: ViewWrapper> View for T {
self.wrap_take_focus(source) self.wrap_take_focus(source)
} }
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> { fn find_any<'a>(&mut self, selector: &Selector,
self.wrap_find_any(selector) callback: Box<FnMut(&mut Any) + 'a>) {
self.wrap_find_any(selector, callback)
} }
fn needs_relayout(&self) -> bool { fn needs_relayout(&self) -> bool {
@ -93,7 +96,7 @@ impl<T: ViewWrapper> View for T {
/// Convenient macro to implement the [`ViewWrapper`] trait. /// 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. /// as well as the `type V` declaration.
/// ///
/// [`ViewWrapper`]: view/trait.ViewWrapper.html /// [`ViewWrapper`]: view/trait.ViewWrapper.html
@ -117,12 +120,14 @@ macro_rules! wrap_impl {
(self.$v:ident: $t:ty) => { (self.$v:ident: $t:ty) => {
type V = $t; type V = $t;
fn get_view(&self) -> &Self::V { fn with_view<F, R>(&self, f: F) -> R where F: FnOnce(&Self::V) -> R {
&self.$v f(&self.$v)
} }
fn get_view_mut(&mut self) -> &mut Self::V { fn with_view_mut<F, R>(&mut self, f: F) -> R
&mut self.$v where F: FnOnce(&mut Self::V) -> R
{
f(&mut self.$v)
} }
}; };
} }

View File

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

View File

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

View File

@ -40,9 +40,8 @@ use view::View;
/// .with_id("name") /// .with_id("name")
/// .fixed_width(20)) /// .fixed_width(20))
/// .button("Ok", |s| { /// .button("Ok", |s| {
/// let name = s.find_id::<EditView>("name") /// let name = s.find_id("name", |view: &mut EditView| view.get_content())
/// .unwrap() /// .unwrap();
/// .get_content();
/// show_popup(s, &name); /// show_popup(s, &name);
/// })); /// }));
/// ///
@ -239,9 +238,10 @@ impl EditView {
// Look at the content before the cursor (we will print its tail). // Look at the content before the cursor (we will print its tail).
// From the end, count the length until we reach `available`. // From the end, count the length until we reach `available`.
// Then sum the byte lengths. // Then sum the byte lengths.
let suffix_length = let suffix_length = simple_suffix(&self.content[self.offset..
simple_suffix(&self.content[self.offset..self.cursor], self.cursor],
available).length; available)
.length;
self.offset = self.cursor - suffix_length; self.offset = self.cursor - suffix_length;
// Make sure the cursor is in view // Make sure the cursor is in view
assert!(self.cursor >= self.offset); assert!(self.cursor >= self.offset);
@ -250,8 +250,8 @@ impl EditView {
// If we have too much space // If we have too much space
if self.content[self.offset..].width() < self.last_length { if self.content[self.offset..].width() < self.last_length {
let suffix_length = simple_suffix(&self.content, let suffix_length =
self.last_length - 1).length; simple_suffix(&self.content, self.last_length - 1).length;
self.offset = self.content.len() - suffix_length; self.offset = self.content.len() - suffix_length;
} }
} }
@ -392,9 +392,7 @@ impl View for EditView {
Event::Key(Key::Enter) if self.on_submit.is_some() => { Event::Key(Key::Enter) if self.on_submit.is_some() => {
let cb = self.on_submit.clone().unwrap(); let cb = self.on_submit.clone().unwrap();
let content = self.content.clone(); let content = self.content.clone();
return EventResult::with_cb(move |s| { return EventResult::with_cb(move |s| { cb(s, &content); });
cb(s, &content);
});
} }
_ => return EventResult::Ignored, _ => return EventResult::Ignored,
} }
@ -407,9 +405,7 @@ impl View for EditView {
let content = self.content.clone(); let content = self.content.clone();
let cursor = self.cursor; let cursor = self.cursor;
Callback::from_fn(move |s| { Callback::from_fn(move |s| { cb(s, &content, cursor); })
cb(s, &content, cursor);
})
}); });
EventResult::Consumed(cb) EventResult::Consumed(cb)
} }

View File

@ -21,10 +21,11 @@ impl<T: View> IdView<T> {
impl<T: View + Any> ViewWrapper for IdView<T> { impl<T: View + Any> ViewWrapper for IdView<T> {
wrap_impl!(self.view: 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 { match selector {
&Selector::Id(id) if id == self.id => Some(&mut self.view), &Selector::Id(id) if id == self.id => callback(&mut self.view),
s => self.view.find_any(s), s => self.view.find_any(s, callback),
} }
} }
} }

View File

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

View File

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

View File

@ -185,14 +185,12 @@ impl View for ListView {
.max() .max()
.unwrap_or(0) + 1; .unwrap_or(0) + 1;
self.scrollbase.draw(printer, |printer, i| { self.scrollbase.draw(printer, |printer, i| match self.children[i] {
match self.children[i] {
Child::Row(ref label, ref view) => { Child::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 => (), Child::Delimiter => (),
}
}); });
} }
@ -298,11 +296,12 @@ impl View for ListView {
true true
} }
fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> { fn find_any<'a>(&mut self, selector: &Selector,
self.children mut callback: Box<FnMut(&mut Any) + 'a>) {
for view in self.children
.iter_mut() .iter_mut()
.filter_map(Child::view) .filter_map(Child::view) {
.filter_map(|v| v.find_any(selector)) view.find_any(selector, Box::new(|any| callback(any)));
.next() }
} }
} }

View File

@ -228,12 +228,9 @@ impl View for MenuPopup {
Event::Key(Key::PageDown) => self.scroll_down(5, false), Event::Key(Key::PageDown) => self.scroll_down(5, false),
Event::Key(Key::Home) => self.focus = 0, Event::Key(Key::Home) => self.focus = 0,
Event::Key(Key::End) => { Event::Key(Key::End) => self.focus = self.menu.children.len() - 1,
self.focus = self.menu.children.len() - 1
}
Event::Key(Key::Right) if self.menu.children Event::Key(Key::Right) if self.menu.children[self.focus]
[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) => {
@ -243,8 +240,7 @@ impl View for MenuPopup {
}; };
} }
Event::Key(Key::Enter) if !self.menu.children Event::Key(Key::Enter) if !self.menu.children[self.focus]
[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) => {

View File

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

View File

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

View File

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

View File

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