2017-01-10 21:34:59 +00:00
|
|
|
use Cursive;
|
2016-07-16 20:25:21 +00:00
|
|
|
use Printer;
|
|
|
|
use With;
|
|
|
|
use direction;
|
2017-10-15 04:18:50 +00:00
|
|
|
use event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent};
|
2016-07-17 01:18:33 +00:00
|
|
|
use std::any::Any;
|
2017-01-10 21:34:59 +00:00
|
|
|
use std::rc::Rc;
|
2017-03-05 19:34:45 +00:00
|
|
|
use unicode_width::UnicodeWidthStr;
|
2016-10-02 22:22:29 +00:00
|
|
|
use vec::Vec2;
|
|
|
|
use view::ScrollBase;
|
|
|
|
use view::Selector;
|
|
|
|
use view::View;
|
2016-07-17 01:18:33 +00:00
|
|
|
|
2017-05-21 16:50:16 +00:00
|
|
|
/// Represents a child from a [`ListView`].
|
|
|
|
///
|
|
|
|
/// [`ListView`]: struct.ListView.html
|
|
|
|
pub enum ListChild {
|
|
|
|
/// A single row, with a label and a view.
|
2016-07-16 20:25:21 +00:00
|
|
|
Row(String, Box<View>),
|
2017-05-21 16:50:16 +00:00
|
|
|
/// A delimiter between groups.
|
2016-07-16 20:25:21 +00:00
|
|
|
Delimiter,
|
|
|
|
}
|
|
|
|
|
2017-05-21 16:50:16 +00:00
|
|
|
impl ListChild {
|
2016-07-16 20:25:21 +00:00
|
|
|
fn label(&self) -> &str {
|
|
|
|
match *self {
|
2017-05-21 16:50:16 +00:00
|
|
|
ListChild::Row(ref label, _) => label,
|
2016-07-16 20:25:21 +00:00
|
|
|
_ => "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-17 23:43:50 +00:00
|
|
|
fn view(&mut self) -> Option<&mut View> {
|
2016-07-16 20:25:21 +00:00
|
|
|
match *self {
|
2017-07-17 23:43:50 +00:00
|
|
|
ListChild::Row(_, ref mut view) => Some(view.as_mut()),
|
2016-07-16 20:25:21 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Displays a scrollable list of elements.
|
|
|
|
pub struct ListView {
|
2017-05-21 16:50:16 +00:00
|
|
|
children: Vec<ListChild>,
|
2016-07-16 20:25:21 +00:00
|
|
|
scrollbase: ScrollBase,
|
|
|
|
focus: usize,
|
2017-01-10 21:34:59 +00:00
|
|
|
// This callback is called when the selection is changed.
|
|
|
|
on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
|
2017-10-15 04:01:07 +00:00
|
|
|
last_size: Vec2,
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
|
2016-07-17 00:28:42 +00:00
|
|
|
new_default!(ListView);
|
2016-07-16 20:25:21 +00:00
|
|
|
|
|
|
|
impl ListView {
|
|
|
|
/// Creates a new, empty `ListView`.
|
|
|
|
pub fn new() -> Self {
|
|
|
|
ListView {
|
|
|
|
children: Vec::new(),
|
|
|
|
scrollbase: ScrollBase::new(),
|
|
|
|
focus: 0,
|
2017-01-10 21:34:59 +00:00
|
|
|
on_select: None,
|
2017-10-15 04:01:07 +00:00
|
|
|
last_size: Vec2::zero(),
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-21 16:50:16 +00:00
|
|
|
/// Returns the number of children, including delimiters.
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.children.len()
|
|
|
|
}
|
|
|
|
|
2017-06-08 16:41:53 +00:00
|
|
|
/// 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()
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-21 16:50:16 +00:00
|
|
|
/// 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]
|
|
|
|
}
|
|
|
|
|
2016-07-16 20:25:21 +00:00
|
|
|
/// Adds a view to the end of the list.
|
2016-10-13 18:57:10 +00:00
|
|
|
pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
|
|
|
|
view.take_focus(direction::Direction::none());
|
2017-10-15 04:18:50 +00:00
|
|
|
self.children
|
|
|
|
.push(ListChild::Row(label.to_string(), Box::new(view)));
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
|
2016-09-05 22:33:52 +00:00
|
|
|
/// Removes all children from this view.
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.children.clear();
|
2016-10-13 18:57:10 +00:00
|
|
|
self.focus = 0;
|
2016-09-05 22:33:52 +00:00
|
|
|
}
|
|
|
|
|
2016-07-16 20:25:21 +00:00
|
|
|
/// Adds a view to the end of the list.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn child<V: View + 'static>(self, label: &str, view: V) -> Self {
|
|
|
|
self.with(|s| s.add_child(label, view))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a delimiter to the end of the list.
|
|
|
|
pub fn add_delimiter(&mut self) {
|
2017-05-21 16:50:16 +00:00
|
|
|
self.children.push(ListChild::Delimiter);
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a delimiter to the end of the list.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn delimiter(self) -> Self {
|
|
|
|
self.with(Self::add_delimiter)
|
|
|
|
}
|
|
|
|
|
2017-01-10 21:34:59 +00:00
|
|
|
/// Sets a callback to be used when an item is selected.
|
|
|
|
pub fn set_on_select<F>(&mut self, cb: F)
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: Fn(&mut Cursive, &String) + 'static,
|
2017-01-10 21:34:59 +00:00
|
|
|
{
|
|
|
|
self.on_select = Some(Rc::new(cb));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets a callback to be used when an item is selected.
|
|
|
|
///
|
|
|
|
/// Chainable variant.
|
|
|
|
pub fn on_select<F>(self, cb: F) -> Self
|
2017-10-12 23:38:55 +00:00
|
|
|
where
|
|
|
|
F: Fn(&mut Cursive, &String) + 'static,
|
2017-01-10 21:34:59 +00:00
|
|
|
{
|
|
|
|
self.with(|s| s.set_on_select(cb))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the index of the currently focused item.
|
|
|
|
///
|
|
|
|
/// Panics if the list is empty.
|
|
|
|
pub fn focus(&self) -> usize {
|
|
|
|
self.focus
|
|
|
|
}
|
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
fn iter_mut<'a>(
|
|
|
|
&'a mut self, from_focus: bool, source: direction::Relative
|
|
|
|
) -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
|
2016-07-16 20:25:21 +00:00
|
|
|
match source {
|
|
|
|
direction::Relative::Front => {
|
2016-10-02 22:22:29 +00:00
|
|
|
let start = if from_focus { self.focus } else { 0 };
|
2016-07-16 20:25:21 +00:00
|
|
|
|
2017-10-12 23:38:55 +00:00
|
|
|
Box::new(self.children.iter_mut().enumerate().skip(start))
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
direction::Relative::Back => {
|
|
|
|
let end = if from_focus {
|
|
|
|
self.focus + 1
|
|
|
|
} else {
|
|
|
|
self.children.len()
|
|
|
|
};
|
|
|
|
Box::new(self.children[..end].iter_mut().enumerate().rev())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
fn move_focus(
|
|
|
|
&mut self, n: usize, source: direction::Direction
|
|
|
|
) -> EventResult {
|
2017-10-12 23:38:55 +00:00
|
|
|
let i = if let Some(i) = source
|
|
|
|
.relative(direction::Orientation::Vertical)
|
|
|
|
.and_then(|rel| {
|
|
|
|
// The iterator starts at the focused element.
|
|
|
|
// We don't want that one.
|
|
|
|
self.iter_mut(true, rel)
|
|
|
|
.skip(1)
|
|
|
|
.filter_map(|p| try_focus(p, source))
|
|
|
|
.take(n)
|
|
|
|
.last()
|
2017-10-15 04:18:50 +00:00
|
|
|
}) {
|
2016-07-16 20:25:21 +00:00
|
|
|
i
|
|
|
|
} else {
|
|
|
|
return EventResult::Ignored;
|
|
|
|
};
|
|
|
|
self.focus = i;
|
|
|
|
self.scrollbase.scroll_to(self.focus);
|
|
|
|
|
2017-01-10 21:34:59 +00:00
|
|
|
EventResult::Consumed(self.on_select.clone().map(|cb| {
|
|
|
|
let i = self.focus();
|
|
|
|
let focused_string = String::from(self.children[i].label());
|
|
|
|
Callback::from_fn(move |s| cb(s, &focused_string))
|
|
|
|
}))
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
2017-10-12 23:42:34 +00:00
|
|
|
|
|
|
|
fn labels_width(&self) -> usize {
|
|
|
|
self.children
|
|
|
|
.iter()
|
|
|
|
.map(ListChild::label)
|
|
|
|
.map(UnicodeWidthStr::width)
|
|
|
|
.max()
|
|
|
|
.unwrap_or(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_focus_grab(&mut self, event: &Event) {
|
2017-10-13 18:22:02 +00:00
|
|
|
if let Event::Mouse {
|
2017-10-12 23:42:34 +00:00
|
|
|
offset,
|
|
|
|
position,
|
|
|
|
event,
|
2017-10-13 18:22:02 +00:00
|
|
|
} = *event
|
2017-10-12 23:42:34 +00:00
|
|
|
{
|
|
|
|
if !event.grabs_focus() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let position = match position.checked_sub(offset) {
|
|
|
|
None => return,
|
|
|
|
Some(pos) => pos,
|
|
|
|
};
|
|
|
|
|
|
|
|
if position.y > self.scrollbase.view_height {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// eprintln!("Rel pos: {:?}", position);
|
|
|
|
|
|
|
|
// Now that we have a relative position, checks for buttons?
|
|
|
|
let focus = position.y + self.scrollbase.start_line;
|
2017-10-13 18:22:02 +00:00
|
|
|
if let ListChild::Row(_, ref mut view) = self.children[focus] {
|
2017-10-12 23:42:34 +00:00
|
|
|
if view.take_focus(direction::Direction::none()) {
|
|
|
|
self.focus = focus;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
fn try_focus(
|
|
|
|
(i, child): (usize, &mut ListChild), source: direction::Direction
|
|
|
|
) -> Option<usize> {
|
2016-07-16 20:25:21 +00:00
|
|
|
match *child {
|
2017-05-21 16:50:16 +00:00
|
|
|
ListChild::Delimiter => None,
|
2017-10-15 04:18:50 +00:00
|
|
|
ListChild::Row(_, ref mut view) => if view.take_focus(source) {
|
|
|
|
Some(i)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
},
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl View for ListView {
|
|
|
|
fn draw(&self, printer: &Printer) {
|
2016-10-13 18:57:10 +00:00
|
|
|
if self.children.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-12 23:42:34 +00:00
|
|
|
let offset = self.labels_width() + 1;
|
2016-07-16 20:25:21 +00:00
|
|
|
|
2017-08-23 14:49:09 +00:00
|
|
|
debug!("Offset: {}", offset);
|
2017-10-15 04:18:50 +00:00
|
|
|
self.scrollbase
|
|
|
|
.draw(printer, |printer, i| match self.children[i] {
|
2017-10-12 23:38:55 +00:00
|
|
|
ListChild::Row(ref label, ref view) => {
|
|
|
|
printer.print((0, 0), label);
|
|
|
|
view.draw(&printer.offset((offset, 0), i == self.focus));
|
|
|
|
}
|
|
|
|
ListChild::Delimiter => (),
|
2017-10-15 04:18:50 +00:00
|
|
|
});
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
|
2017-01-24 06:52:29 +00:00
|
|
|
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
2017-03-05 19:34:45 +00:00
|
|
|
// We'll show 2 columns: the labels, and the views.
|
|
|
|
let label_width = self.children
|
2016-07-16 20:25:21 +00:00
|
|
|
.iter()
|
2017-05-21 16:50:16 +00:00
|
|
|
.map(ListChild::label)
|
2017-03-05 19:34:45 +00:00
|
|
|
.map(UnicodeWidthStr::width)
|
2016-07-16 20:25:21 +00:00
|
|
|
.max()
|
|
|
|
.unwrap_or(0);
|
2017-03-05 19:34:45 +00:00
|
|
|
|
2016-07-16 20:25:21 +00:00
|
|
|
let view_size = self.children
|
|
|
|
.iter_mut()
|
2017-05-21 16:50:16 +00:00
|
|
|
.filter_map(ListChild::view)
|
2017-01-24 06:52:29 +00:00
|
|
|
.map(|v| v.required_size(req).x)
|
2016-07-16 20:25:21 +00:00
|
|
|
.max()
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
|
|
|
if self.children.len() > req.y {
|
2017-03-05 19:34:45 +00:00
|
|
|
Vec2::new(label_width + 1 + view_size + 2, req.y)
|
2016-07-16 20:25:21 +00:00
|
|
|
} else {
|
2017-03-05 19:34:45 +00:00
|
|
|
Vec2::new(label_width + 1 + view_size, self.children.len())
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn layout(&mut self, size: Vec2) {
|
2017-10-15 04:01:07 +00:00
|
|
|
self.last_size = size;
|
2016-07-16 20:25:21 +00:00
|
|
|
self.scrollbase.set_heights(size.y, self.children.len());
|
|
|
|
|
2017-03-05 19:34:45 +00:00
|
|
|
// We'll show 2 columns: the labels, and the views.
|
|
|
|
let label_width = self.children
|
2016-07-16 20:25:21 +00:00
|
|
|
.iter()
|
2017-05-21 16:50:16 +00:00
|
|
|
.map(ListChild::label)
|
2017-03-05 19:34:45 +00:00
|
|
|
.map(UnicodeWidthStr::width)
|
2016-07-16 20:25:21 +00:00
|
|
|
.max()
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
2017-03-05 19:34:45 +00:00
|
|
|
let spacing = 1;
|
|
|
|
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
|
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
let available = size.x
|
|
|
|
.saturating_sub(label_width + spacing + scrollbar_width);
|
2017-03-05 19:34:45 +00:00
|
|
|
|
2017-08-23 14:49:09 +00:00
|
|
|
debug!("Available: {}", available);
|
2016-07-16 20:25:21 +00:00
|
|
|
|
2017-05-21 16:50:16 +00:00
|
|
|
for child in self.children.iter_mut().filter_map(ListChild::view) {
|
2016-07-16 20:25:21 +00:00
|
|
|
child.layout(Vec2::new(available, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn on_event(&mut self, event: Event) -> EventResult {
|
2016-10-13 18:57:10 +00:00
|
|
|
if self.children.is_empty() {
|
|
|
|
return EventResult::Ignored;
|
|
|
|
}
|
|
|
|
|
2017-10-15 04:01:07 +00:00
|
|
|
// First: some events can directly affect the ListView
|
|
|
|
match event {
|
|
|
|
Event::Mouse {
|
|
|
|
event: MouseEvent::Press(MouseButton::Left),
|
|
|
|
position,
|
|
|
|
offset,
|
2017-10-15 04:18:50 +00:00
|
|
|
} if position
|
|
|
|
.checked_sub(offset)
|
|
|
|
.map(|position| {
|
2017-10-15 04:01:07 +00:00
|
|
|
self.scrollbase.start_drag(position, self.last_size.x)
|
|
|
|
})
|
2017-10-15 04:18:50 +00:00
|
|
|
.unwrap_or(false) =>
|
|
|
|
{
|
2017-10-15 04:01:07 +00:00
|
|
|
return EventResult::Consumed(None);
|
|
|
|
}
|
|
|
|
Event::Mouse {
|
|
|
|
event: MouseEvent::Hold(MouseButton::Left),
|
|
|
|
position,
|
|
|
|
offset,
|
2017-10-15 04:18:50 +00:00
|
|
|
} if self.scrollbase.is_dragging() =>
|
|
|
|
{
|
2017-10-15 04:34:37 +00:00
|
|
|
let position = position.saturating_sub(offset);
|
|
|
|
self.scrollbase.drag(position);
|
2017-10-15 04:01:07 +00:00
|
|
|
return EventResult::Consumed(None);
|
|
|
|
}
|
|
|
|
Event::Mouse {
|
2017-10-15 04:18:50 +00:00
|
|
|
event: MouseEvent::Release(MouseButton::Left),
|
|
|
|
..
|
|
|
|
} if self.scrollbase.is_dragging() =>
|
|
|
|
{
|
2017-10-15 04:01:07 +00:00
|
|
|
self.scrollbase.release_grab();
|
|
|
|
return EventResult::Consumed(None);
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Then: some events can move the focus around.
|
2017-10-12 23:42:34 +00:00
|
|
|
self.check_focus_grab(&event);
|
|
|
|
|
|
|
|
// Send the event to the focused child.
|
|
|
|
let labels_width = self.labels_width();
|
2017-05-21 16:50:16 +00:00
|
|
|
if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
|
2017-10-12 23:42:34 +00:00
|
|
|
let y = self.focus - self.scrollbase.start_line;
|
|
|
|
let offset = (labels_width + 1, y);
|
|
|
|
let result = view.on_event(event.relativized(offset));
|
2016-07-16 20:25:21 +00:00
|
|
|
if result.is_consumed() {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-12 23:42:34 +00:00
|
|
|
// If the child ignored this event, change the focus.
|
2016-07-16 20:25:21 +00:00
|
|
|
match event {
|
|
|
|
Event::Key(Key::Up) if self.focus > 0 => {
|
|
|
|
self.move_focus(1, direction::Direction::down())
|
|
|
|
}
|
|
|
|
Event::Key(Key::Down) if self.focus + 1 < self.children.len() => {
|
|
|
|
self.move_focus(1, direction::Direction::up())
|
|
|
|
}
|
|
|
|
Event::Key(Key::PageUp) => {
|
|
|
|
self.move_focus(10, direction::Direction::down())
|
|
|
|
}
|
|
|
|
Event::Key(Key::PageDown) => {
|
|
|
|
self.move_focus(10, direction::Direction::up())
|
|
|
|
}
|
2017-10-15 04:18:50 +00:00
|
|
|
Event::Key(Key::Home) | Event::Ctrl(Key::Home) => {
|
2017-10-12 23:38:55 +00:00
|
|
|
self.move_focus(
|
|
|
|
usize::max_value(),
|
|
|
|
direction::Direction::back(),
|
|
|
|
)
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|
2017-10-15 04:34:37 +00:00
|
|
|
Event::Key(Key::End) | Event::Ctrl(Key::End) => self.move_focus(
|
|
|
|
usize::max_value(),
|
|
|
|
direction::Direction::front(),
|
|
|
|
),
|
2016-07-16 20:25:21 +00:00
|
|
|
Event::Key(Key::Tab) => {
|
|
|
|
self.move_focus(1, direction::Direction::front())
|
|
|
|
}
|
|
|
|
Event::Shift(Key::Tab) => {
|
|
|
|
self.move_focus(1, direction::Direction::back())
|
|
|
|
}
|
2017-10-15 04:18:50 +00:00
|
|
|
Event::Mouse {
|
|
|
|
event: MouseEvent::WheelDown,
|
|
|
|
..
|
|
|
|
} if self.scrollbase.can_scroll_down() =>
|
|
|
|
{
|
2017-10-15 04:01:07 +00:00
|
|
|
self.scrollbase.scroll_down(5);
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2017-10-15 04:18:50 +00:00
|
|
|
Event::Mouse {
|
|
|
|
event: MouseEvent::WheelUp,
|
|
|
|
..
|
|
|
|
} if self.scrollbase.can_scroll_up() =>
|
|
|
|
{
|
2017-10-15 04:01:07 +00:00
|
|
|
self.scrollbase.scroll_up(5);
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2016-07-16 20:25:21 +00:00
|
|
|
_ => EventResult::Ignored,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
|
|
|
let rel = source.relative(direction::Orientation::Vertical);
|
2017-10-15 04:18:50 +00:00
|
|
|
let i = if let Some(i) = self.iter_mut(
|
|
|
|
rel.is_none(),
|
|
|
|
rel.unwrap_or(direction::Relative::Front),
|
|
|
|
).filter_map(|p| try_focus(p, source))
|
|
|
|
.next()
|
|
|
|
{
|
|
|
|
i
|
|
|
|
} else {
|
|
|
|
// No one wants to be in focus
|
|
|
|
return false;
|
|
|
|
};
|
2016-07-16 20:25:21 +00:00
|
|
|
self.focus = i;
|
|
|
|
self.scrollbase.scroll_to(self.focus);
|
|
|
|
true
|
|
|
|
}
|
2016-07-17 01:18:33 +00:00
|
|
|
|
2017-10-15 04:18:50 +00:00
|
|
|
fn call_on_any<'a>(
|
|
|
|
&mut self, selector: &Selector,
|
|
|
|
mut callback: Box<FnMut(&mut Any) + 'a>,
|
|
|
|
) {
|
2017-08-14 23:32:01 +00:00
|
|
|
for view in self.children.iter_mut().filter_map(ListChild::view) {
|
2017-03-27 03:50:50 +00:00
|
|
|
view.call_on_any(selector, Box::new(|any| callback(any)));
|
2017-02-07 02:18:17 +00:00
|
|
|
}
|
2016-07-17 01:18:33 +00:00
|
|
|
}
|
2017-03-25 21:50:52 +00:00
|
|
|
|
|
|
|
fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
|
|
|
|
if let Some(i) = self.children
|
2017-10-12 23:38:55 +00:00
|
|
|
.iter_mut()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
|
|
|
|
.filter_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
|
|
|
|
.next()
|
|
|
|
{
|
2017-03-25 21:50:52 +00:00
|
|
|
self.focus = i;
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
2016-07-16 20:25:21 +00:00
|
|
|
}
|