From 017bb217109c5b0dfc38dcac3e9d6ae456cf6fb5 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sat, 25 Mar 2017 14:50:52 -0700 Subject: [PATCH] Add `View::focus_view` --- src/lib.rs | 14 ++++++++++++++ src/view/mod.rs | 9 ++++++++- src/view/view_wrapper.rs | 9 +++++++++ src/views/dialog.rs | 4 ++++ src/views/id_view.rs | 7 +++++++ src/views/linear_layout.rs | 11 +++++++++++ src/views/list_view.rs | 28 +++++++++++++++++++++++----- src/views/stack_view.rs | 10 ++++++++++ 8 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 45fc31c..a6a721c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -392,6 +392,8 @@ impl Cursive { self.screen_mut().find(sel) } + /// Tries to find the view identified by the given id. + /// /// Convenient method to use `find` with a `view::Selector::Id`. /// /// # Examples @@ -417,6 +419,18 @@ impl Cursive { self.find(&view::Selector::Id(id)) } + /// Moves the focus to the view identified by `id`. + /// + /// Convenient method to call `focus` with a `view::Selector::Id`. + pub fn focus_id(&mut self, id: &str) -> Result<(), ()> { + self.focus(&view::Selector::Id(id)) + } + + /// Moves the focus to the view identified by `sel`. + pub fn focus(&mut self, sel: &view::Selector) -> Result<(), ()> { + self.screen_mut().focus_view(sel) + } + /// Adds a global callback. /// /// Will be triggered on the given key press when no view catches it. diff --git a/src/view/mod.rs b/src/view/mod.rs index 5614c0d..29f499a 100644 --- a/src/view/mod.rs +++ b/src/view/mod.rs @@ -113,7 +113,7 @@ pub trait View { /// Draws the view with the given printer (includes bounds) and focus. fn draw(&self, printer: &Printer); - /// Finds the view pointed to by the given path. + /// Finds the view identified by the given selector. /// /// See [`Finder::find`] for a nicer interface, implemented for all views. /// @@ -126,6 +126,13 @@ pub trait View { None } + /// Moves the focus to the view identified by the given selector. + /// + /// Returns `Ok(())` if the view was found and selected. + fn focus_view(&mut self, &Selector) -> Result<(), ()> { + Err(()) + } + /// This view is offered focus. Will it take it? /// /// `source` indicates where the focus comes from. diff --git a/src/view/view_wrapper.rs b/src/view/view_wrapper.rs index b981bbe..879f887 100644 --- a/src/view/view_wrapper.rs +++ b/src/view/view_wrapper.rs @@ -55,6 +55,11 @@ pub trait ViewWrapper { self.get_view_mut().find_any(selector) } + /// Wraps the `focus_view` method. + fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + self.get_view_mut().focus_view(selector) + } + /// Wraps the `needs_relayout` method. fn wrap_needs_relayout(&self) -> bool { self.get_view().needs_relayout() @@ -89,6 +94,10 @@ impl View for T { fn needs_relayout(&self) -> bool { self.wrap_needs_relayout() } + + fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + self.wrap_focus_view(selector) + } } /// Convenient macro to implement the [`ViewWrapper`] trait. diff --git a/src/views/dialog.rs b/src/views/dialog.rs index 0172f76..9f79b00 100644 --- a/src/views/dialog.rs +++ b/src/views/dialog.rs @@ -402,4 +402,8 @@ impl View for Dialog { fn find_any(&mut self, selector: &Selector) -> Option<&mut Any> { self.content.find_any(selector) } + + fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + self.content.focus_view(selector) + } } diff --git a/src/views/id_view.rs b/src/views/id_view.rs index 73ae6c2..e26baf3 100644 --- a/src/views/id_view.rs +++ b/src/views/id_view.rs @@ -27,4 +27,11 @@ impl ViewWrapper for IdView { s => self.view.find_any(s), } } + + fn wrap_focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + match selector { + &Selector::Id(id) if id == self.id => Ok(()), + s => self.view.focus_view(s), + } + } } diff --git a/src/views/linear_layout.rs b/src/views/linear_layout.rs index 82e4286..8519c0c 100644 --- a/src/views/linear_layout.rs +++ b/src/views/linear_layout.rs @@ -401,4 +401,15 @@ impl View for LinearLayout { .filter_map(|c| c.view.find_any(selector)) .next() } + + fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + for (i, child) in self.children.iter_mut().enumerate() { + if child.view.focus_view(selector).is_ok() { + self.focus = i; + return Ok(()); + } + } + + Err(()) + } } diff --git a/src/views/list_view.rs b/src/views/list_view.rs index df56bb5..ad94fb8 100644 --- a/src/views/list_view.rs +++ b/src/views/list_view.rs @@ -118,7 +118,10 @@ impl ListView { direction::Relative::Front => { let start = if from_focus { self.focus } else { 0 }; - Box::new(self.children.iter_mut().enumerate().skip(start)) + Box::new(self.children + .iter_mut() + .enumerate() + .skip(start)) } direction::Relative::Back => { let end = if from_focus { @@ -296,10 +299,11 @@ impl View for ListView { fn take_focus(&mut self, source: direction::Direction) -> bool { let rel = source.relative(direction::Orientation::Vertical); - 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() { + 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 @@ -317,4 +321,18 @@ impl View for ListView { .filter_map(|v| v.find_any(selector)) .next() } + + fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + if let Some(i) = self.children + .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() { + self.focus = i; + Ok(()) + } else { + Err(()) + } + } } diff --git a/src/views/stack_view.rs b/src/views/stack_view.rs index e48c01a..b79f121 100644 --- a/src/views/stack_view.rs +++ b/src/views/stack_view.rs @@ -186,4 +186,14 @@ impl View for StackView { .filter_map(|l| l.view.find_any(selector)) .next() } + + fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + for layer in &mut self.layers { + if layer.view.focus_view(selector).is_ok() { + return Ok(()); + } + } + + Err(()) + } }