mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Add more control to focus changes.
- Add `Event::FocusLost` - View groups: send a "focus lost" event to the focused child when the focus is changing. - Change return type of `View::take_focus` from `bool` to `Result<EventResult, CannotFocus>`. - Change return type of `View::focus_view` from `Result<(), ViewNotFound>` to `Result<EventResult, ViewNotFound>`. - Add `views::FocusTracker` to run callbacks on focus gain/loss.
This commit is contained in:
parent
e417a8be0b
commit
7fd86b69ec
@ -285,7 +285,10 @@ impl Cursive {
|
|||||||
|
|
||||||
/// Selects the menubar.
|
/// Selects the menubar.
|
||||||
pub fn select_menubar(&mut self) {
|
pub fn select_menubar(&mut self) {
|
||||||
self.menubar.take_focus(direction::Direction::none());
|
if let Ok(res) = self.menubar.take_focus(direction::Direction::none())
|
||||||
|
{
|
||||||
|
res.process(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the menubar autohide feature.
|
/// Sets the menubar autohide feature.
|
||||||
@ -306,18 +309,18 @@ impl Cursive {
|
|||||||
/// # use cursive_core::{Cursive, event};
|
/// # use cursive_core::{Cursive, event};
|
||||||
/// # use cursive_core::views::{Dialog};
|
/// # use cursive_core::views::{Dialog};
|
||||||
/// # use cursive_core::traits::*;
|
/// # use cursive_core::traits::*;
|
||||||
/// # use cursive_core::menu::*;
|
/// # use cursive_core::menu;
|
||||||
/// #
|
/// #
|
||||||
/// let mut siv = Cursive::new();
|
/// let mut siv = Cursive::new();
|
||||||
///
|
///
|
||||||
/// siv.menubar()
|
/// siv.menubar()
|
||||||
/// .add_subtree(
|
/// .add_subtree(
|
||||||
/// "File",
|
/// "File",
|
||||||
/// MenuTree::new()
|
/// menu::Tree::new()
|
||||||
/// .leaf("New", |s| s.add_layer(Dialog::info("New file!")))
|
/// .leaf("New", |s| s.add_layer(Dialog::info("New file!")))
|
||||||
/// .subtree(
|
/// .subtree(
|
||||||
/// "Recent",
|
/// "Recent",
|
||||||
/// MenuTree::new().with(|tree| {
|
/// menu::Tree::new().with(|tree| {
|
||||||
/// for i in 1..100 {
|
/// for i in 1..100 {
|
||||||
/// tree.add_leaf(format!("Item {}", i), |_| ())
|
/// tree.add_leaf(format!("Item {}", i), |_| ())
|
||||||
/// }
|
/// }
|
||||||
@ -334,10 +337,10 @@ impl Cursive {
|
|||||||
/// )
|
/// )
|
||||||
/// .add_subtree(
|
/// .add_subtree(
|
||||||
/// "Help",
|
/// "Help",
|
||||||
/// MenuTree::new()
|
/// menu::Tree::new()
|
||||||
/// .subtree(
|
/// .subtree(
|
||||||
/// "Help",
|
/// "Help",
|
||||||
/// MenuTree::new()
|
/// menu::Tree::new()
|
||||||
/// .leaf("General", |s| {
|
/// .leaf("General", |s| {
|
||||||
/// s.add_layer(Dialog::info("Help message!"))
|
/// s.add_layer(Dialog::info("Help message!"))
|
||||||
/// })
|
/// })
|
||||||
@ -618,13 +621,16 @@ impl Cursive {
|
|||||||
/// Moves the focus to the view identified by `name`.
|
/// Moves the focus to the view identified by `name`.
|
||||||
///
|
///
|
||||||
/// Convenient method to call `focus` with a [`view::Selector::Name`].
|
/// Convenient method to call `focus` with a [`view::Selector::Name`].
|
||||||
pub fn focus_name(&mut self, name: &str) -> Result<(), ViewNotFound> {
|
pub fn focus_name(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
self.focus(&view::Selector::Name(name))
|
self.focus(&view::Selector::Name(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [`focus_name`](Cursive::focus_name).
|
/// Same as [`focus_name`](Cursive::focus_name).
|
||||||
#[deprecated(note = "`focus_id` is being renamed to `focus_name`")]
|
#[deprecated(note = "`focus_id` is being renamed to `focus_name`")]
|
||||||
pub fn focus_id(&mut self, id: &str) -> Result<(), ViewNotFound> {
|
pub fn focus_id(&mut self, id: &str) -> Result<EventResult, ViewNotFound> {
|
||||||
self.focus(&view::Selector::Name(id))
|
self.focus(&view::Selector::Name(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,7 +638,7 @@ impl Cursive {
|
|||||||
pub fn focus(
|
pub fn focus(
|
||||||
&mut self,
|
&mut self,
|
||||||
sel: &view::Selector<'_>,
|
sel: &view::Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
self.root.focus_view(sel)
|
self.root.focus_view(sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +253,11 @@ impl EventResult {
|
|||||||
EventResult::Consumed(Some(Callback::from_fn(f)))
|
EventResult::Consumed(Some(Callback::from_fn(f)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenient method to create `Consumed(None)`
|
||||||
|
pub fn consumed() -> Self {
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if `self` is `EventResult::Consumed`.
|
/// Returns `true` if `self` is `EventResult::Consumed`.
|
||||||
pub fn is_consumed(&self) -> bool {
|
pub fn is_consumed(&self) -> bool {
|
||||||
matches!(*self, EventResult::Consumed(_))
|
matches!(*self, EventResult::Consumed(_))
|
||||||
@ -469,6 +474,9 @@ pub enum Event {
|
|||||||
/// Event fired when the window is resized.
|
/// Event fired when the window is resized.
|
||||||
WindowResize,
|
WindowResize,
|
||||||
|
|
||||||
|
/// Event fired when the view is about to lose focus.
|
||||||
|
FocusLost,
|
||||||
|
|
||||||
/// Event fired regularly when a auto-refresh is set.
|
/// Event fired regularly when a auto-refresh is set.
|
||||||
Refresh,
|
Refresh,
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ pub use self::scroll_base::ScrollBase;
|
|||||||
pub use self::scrollable::Scrollable;
|
pub use self::scrollable::Scrollable;
|
||||||
pub use self::size_cache::SizeCache;
|
pub use self::size_cache::SizeCache;
|
||||||
pub use self::size_constraint::SizeConstraint;
|
pub use self::size_constraint::SizeConstraint;
|
||||||
pub use self::view_trait::{View, ViewNotFound};
|
pub use self::view_trait::{CannotFocus, View, ViewNotFound};
|
||||||
pub use self::view_wrapper::ViewWrapper;
|
pub use self::view_wrapper::ViewWrapper;
|
||||||
|
|
||||||
#[deprecated(note = "`Boxable` is being renamed to `Resizable`")]
|
#[deprecated(note = "`Boxable` is being renamed to `Resizable`")]
|
||||||
|
@ -10,12 +10,22 @@ use std::any::Any;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ViewNotFound;
|
pub struct ViewNotFound;
|
||||||
|
|
||||||
|
/// Error indicating a view could not take focus.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CannotFocus;
|
||||||
|
|
||||||
impl std::fmt::Display for ViewNotFound {
|
impl std::fmt::Display for ViewNotFound {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "View could not be found")
|
write!(f, "View could not be found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CannotFocus {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "View does not take focus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::error::Error for ViewNotFound {}
|
impl std::error::Error for ViewNotFound {}
|
||||||
|
|
||||||
/// Main trait defining a view behaviour.
|
/// Main trait defining a view behaviour.
|
||||||
@ -99,23 +109,32 @@ pub trait View: Any + AnyView {
|
|||||||
|
|
||||||
/// Moves the focus to the view identified by the given selector.
|
/// Moves the focus to the view identified by the given selector.
|
||||||
///
|
///
|
||||||
/// Returns `Ok(())` if the view was found and selected.
|
/// Returns `Ok(_)` if the view was found and selected.
|
||||||
|
/// A callback may be included, it should be run on `&mut Cursive`.
|
||||||
///
|
///
|
||||||
/// Default implementation simply returns `Err(())`.
|
/// Default implementation simply returns `Err(ViewNotFound)`.
|
||||||
fn focus_view(&mut self, _: &Selector<'_>) -> Result<(), ViewNotFound> {
|
fn focus_view(
|
||||||
|
&mut self,
|
||||||
|
_: &Selector<'_>,
|
||||||
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
Err(ViewNotFound)
|
Err(ViewNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This view is offered focus. Will it take it?
|
/// Attempt to give this view the focus.
|
||||||
///
|
///
|
||||||
/// `source` indicates where the focus comes from.
|
/// `source` indicates where the focus comes from.
|
||||||
/// When the source is unclear (for example mouse events),
|
/// When the source is unclear (for example mouse events),
|
||||||
/// `Direction::none()` can be used.
|
/// `Direction::none()` can be used.
|
||||||
///
|
///
|
||||||
/// Default implementation always return `false`.
|
/// Returns `Ok(_)` if the focus was taken.
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
/// Returns `Err(_)` if this view does not take focus (default implementation).
|
||||||
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
let _ = source;
|
let _ = source;
|
||||||
false
|
|
||||||
|
Err(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What part of the view is important and should be visible?
|
/// What part of the view is important and should be visible?
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
direction::Direction,
|
direction::Direction,
|
||||||
event::{AnyCb, Event, EventResult},
|
event::{AnyCb, Event, EventResult},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
view::{Selector, View, ViewNotFound},
|
view::{CannotFocus, Selector, View, ViewNotFound},
|
||||||
Printer, Vec2,
|
Printer, Vec2,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,9 +70,12 @@ pub trait ViewWrapper: 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
self.with_view_mut(|v| v.take_focus(source))
|
self.with_view_mut(|v| v.take_focus(source))
|
||||||
.unwrap_or(false)
|
.unwrap_or(Err(CannotFocus))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps the `find` method.
|
/// Wraps the `find` method.
|
||||||
@ -88,7 +91,7 @@ pub trait ViewWrapper: 'static {
|
|||||||
fn wrap_focus_view(
|
fn wrap_focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
self.with_view_mut(|v| v.focus_view(selector))
|
self.with_view_mut(|v| v.focus_view(selector))
|
||||||
.unwrap_or(Err(ViewNotFound))
|
.unwrap_or(Err(ViewNotFound))
|
||||||
}
|
}
|
||||||
@ -123,7 +126,10 @@ impl<T: ViewWrapper> View for T {
|
|||||||
self.wrap_layout(size);
|
self.wrap_layout(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
self.wrap_take_focus(source)
|
self.wrap_take_focus(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +148,7 @@ impl<T: ViewWrapper> View for T {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
self.wrap_focus_view(selector)
|
self.wrap_focus_view(selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use crate::align::HAlign;
|
use crate::{
|
||||||
use crate::direction::Direction;
|
align::HAlign,
|
||||||
use crate::event::*;
|
direction::Direction,
|
||||||
use crate::rect::Rect;
|
event::*,
|
||||||
use crate::theme::ColorStyle;
|
rect::Rect,
|
||||||
use crate::view::View;
|
theme::ColorStyle,
|
||||||
use crate::Vec2;
|
view::{CannotFocus, View},
|
||||||
use crate::{Cursive, Printer};
|
Cursive, Printer, Vec2,
|
||||||
|
};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
/// Simple text label with a callback when <Enter> is pressed.
|
/// Simple text label with a callback when <Enter> is pressed.
|
||||||
@ -185,8 +186,11 @@ impl View for Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
self.enabled
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn important_area(&self, view_size: Vec2) -> Rect {
|
fn important_area(&self, view_size: Vec2) -> Rect {
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
direction::Direction,
|
direction::Direction,
|
||||||
event::{AnyCb, Event, EventResult},
|
event::{AnyCb, Event, EventResult},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
view::{Selector, View, ViewNotFound},
|
view::{CannotFocus, Selector, View, ViewNotFound},
|
||||||
Printer, Vec2, With,
|
Printer, Vec2, With,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,9 +49,11 @@ pub struct Canvas<T> {
|
|||||||
on_event: Box<dyn FnMut(&mut T, Event) -> EventResult>,
|
on_event: Box<dyn FnMut(&mut T, Event) -> EventResult>,
|
||||||
required_size: Box<dyn FnMut(&mut T, Vec2) -> Vec2>,
|
required_size: Box<dyn FnMut(&mut T, Vec2) -> Vec2>,
|
||||||
layout: Box<dyn FnMut(&mut T, Vec2)>,
|
layout: Box<dyn FnMut(&mut T, Vec2)>,
|
||||||
take_focus: Box<dyn FnMut(&mut T, Direction) -> bool>,
|
take_focus:
|
||||||
|
Box<dyn FnMut(&mut T, Direction) -> Result<EventResult, CannotFocus>>,
|
||||||
needs_relayout: Box<dyn Fn(&T) -> bool>,
|
needs_relayout: Box<dyn Fn(&T) -> bool>,
|
||||||
focus_view: Box<dyn FnMut(&mut T, &Selector) -> Result<(), ViewNotFound>>,
|
focus_view:
|
||||||
|
Box<dyn FnMut(&mut T, &Selector) -> Result<EventResult, ViewNotFound>>,
|
||||||
call_on_any: CallOnAny<T>,
|
call_on_any: CallOnAny<T>,
|
||||||
important_area: Box<dyn Fn(&T, Vec2) -> Rect>,
|
important_area: Box<dyn Fn(&T, Vec2) -> Rect>,
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@ impl<T> Canvas<T> {
|
|||||||
on_event: Box::new(|_, _| EventResult::Ignored),
|
on_event: Box::new(|_, _| EventResult::Ignored),
|
||||||
required_size: Box::new(|_, _| Vec2::new(1, 1)),
|
required_size: Box::new(|_, _| Vec2::new(1, 1)),
|
||||||
layout: Box::new(|_, _| ()),
|
layout: Box::new(|_, _| ()),
|
||||||
take_focus: Box::new(|_, _| false),
|
take_focus: Box::new(|_, _| Err(CannotFocus)),
|
||||||
needs_relayout: Box::new(|_| true),
|
needs_relayout: Box::new(|_| true),
|
||||||
focus_view: Box::new(|_, _| Err(ViewNotFound)),
|
focus_view: Box::new(|_, _| Err(ViewNotFound)),
|
||||||
call_on_any: Box::new(|_, _, _| ()),
|
call_on_any: Box::new(|_, _, _| ()),
|
||||||
@ -173,7 +175,8 @@ impl<T> Canvas<T> {
|
|||||||
/// Sets the closure for `take_focus(Direction)`.
|
/// Sets the closure for `take_focus(Direction)`.
|
||||||
pub fn set_take_focus<F>(&mut self, f: F)
|
pub fn set_take_focus<F>(&mut self, f: F)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(&mut T, Direction) -> bool,
|
F: 'static
|
||||||
|
+ FnMut(&mut T, Direction) -> Result<EventResult, CannotFocus>,
|
||||||
{
|
{
|
||||||
self.take_focus = Box::new(f);
|
self.take_focus = Box::new(f);
|
||||||
}
|
}
|
||||||
@ -183,7 +186,8 @@ impl<T> Canvas<T> {
|
|||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
pub fn with_take_focus<F>(self, f: F) -> Self
|
pub fn with_take_focus<F>(self, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(&mut T, Direction) -> bool,
|
F: 'static
|
||||||
|
+ FnMut(&mut T, Direction) -> Result<EventResult, CannotFocus>,
|
||||||
{
|
{
|
||||||
self.with(|s| s.set_take_focus(f))
|
self.with(|s| s.set_take_focus(f))
|
||||||
}
|
}
|
||||||
@ -245,7 +249,8 @@ impl<T> Canvas<T> {
|
|||||||
/// Sets the closure for `focus_view()`.
|
/// Sets the closure for `focus_view()`.
|
||||||
pub fn set_focus_view<F>(&mut self, f: F)
|
pub fn set_focus_view<F>(&mut self, f: F)
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(&mut T, &Selector<'_>) -> Result<(), ViewNotFound>,
|
F: 'static
|
||||||
|
+ FnMut(&mut T, &Selector<'_>) -> Result<EventResult, ViewNotFound>,
|
||||||
{
|
{
|
||||||
self.focus_view = Box::new(f);
|
self.focus_view = Box::new(f);
|
||||||
}
|
}
|
||||||
@ -255,7 +260,8 @@ impl<T> Canvas<T> {
|
|||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
pub fn with_focus_view<F>(self, f: F) -> Self
|
pub fn with_focus_view<F>(self, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(&mut T, &Selector<'_>) -> Result<(), ViewNotFound>,
|
F: 'static
|
||||||
|
+ FnMut(&mut T, &Selector<'_>) -> Result<EventResult, ViewNotFound>,
|
||||||
{
|
{
|
||||||
self.with(|s| s.set_focus_view(f))
|
self.with(|s| s.set_focus_view(f))
|
||||||
}
|
}
|
||||||
@ -278,7 +284,10 @@ impl<T: 'static> View for Canvas<T> {
|
|||||||
(self.layout)(&mut self.state, size);
|
(self.layout)(&mut self.state, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
(self.take_focus)(&mut self.state, source)
|
(self.take_focus)(&mut self.state, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +298,7 @@ impl<T: 'static> View for Canvas<T> {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
(self.focus_view)(&mut self.state, selector)
|
(self.focus_view)(&mut self.state, selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
use crate::direction::Direction;
|
use crate::{
|
||||||
use crate::event::{Event, EventResult, Key, MouseButton, MouseEvent};
|
direction::Direction,
|
||||||
use crate::theme::ColorStyle;
|
event::{Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
use crate::view::View;
|
theme::ColorStyle,
|
||||||
use crate::Cursive;
|
view::{CannotFocus, View},
|
||||||
use crate::Printer;
|
Cursive, Printer, Vec2, With,
|
||||||
use crate::Vec2;
|
};
|
||||||
use crate::With;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Checkable box.
|
/// Checkable box.
|
||||||
@ -141,8 +140,11 @@ impl View for Checkbox {
|
|||||||
Vec2::new(3, 1)
|
Vec2::new(3, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
self.enabled
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, printer: &Printer) {
|
fn draw(&self, printer: &Printer) {
|
||||||
|
@ -54,6 +54,16 @@ impl<T: View> CircularFocus<T> {
|
|||||||
self.wrap_arrows
|
self.wrap_arrows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make this view now wrap focus around when the Tab key is pressed.
|
||||||
|
pub fn set_wrap_tab(&mut self, wrap_tab: bool) {
|
||||||
|
self.wrap_tab = wrap_tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make this view now wrap focus around when arrow keys are pressed.
|
||||||
|
pub fn set_wrap_arrows(&mut self, wrap_arrows: bool) {
|
||||||
|
self.wrap_arrows = wrap_arrows;
|
||||||
|
}
|
||||||
|
|
||||||
inner_getters!(self.view: T);
|
inner_getters!(self.view: T);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,61 +74,49 @@ impl<T: View> ViewWrapper for CircularFocus<T> {
|
|||||||
match (self.view.on_event(event.clone()), event) {
|
match (self.view.on_event(event.clone()), event) {
|
||||||
(EventResult::Ignored, Event::Key(Key::Tab)) if self.wrap_tab => {
|
(EventResult::Ignored, Event::Key(Key::Tab)) if self.wrap_tab => {
|
||||||
// Focus comes back!
|
// Focus comes back!
|
||||||
if self.view.take_focus(Direction::front()) {
|
self.view
|
||||||
EventResult::Consumed(None)
|
.take_focus(Direction::front())
|
||||||
} else {
|
.unwrap_or(EventResult::Ignored)
|
||||||
EventResult::Ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(EventResult::Ignored, Event::Shift(Key::Tab))
|
(EventResult::Ignored, Event::Shift(Key::Tab))
|
||||||
if self.wrap_tab =>
|
if self.wrap_tab =>
|
||||||
{
|
{
|
||||||
// Focus comes back!
|
// Focus comes back!
|
||||||
if self.view.take_focus(Direction::back()) {
|
self.view
|
||||||
EventResult::Consumed(None)
|
.take_focus(Direction::back())
|
||||||
} else {
|
.unwrap_or(EventResult::Ignored)
|
||||||
EventResult::Ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(EventResult::Ignored, Event::Key(Key::Right))
|
(EventResult::Ignored, Event::Key(Key::Right))
|
||||||
if self.wrap_arrows =>
|
if self.wrap_arrows =>
|
||||||
{
|
{
|
||||||
// Focus comes back!
|
// Focus comes back!
|
||||||
if self.view.take_focus(Direction::left()) {
|
self.view
|
||||||
EventResult::Consumed(None)
|
.take_focus(Direction::left())
|
||||||
} else {
|
.unwrap_or(EventResult::Ignored)
|
||||||
EventResult::Ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(EventResult::Ignored, Event::Key(Key::Left))
|
(EventResult::Ignored, Event::Key(Key::Left))
|
||||||
if self.wrap_arrows =>
|
if self.wrap_arrows =>
|
||||||
{
|
{
|
||||||
// Focus comes back!
|
// Focus comes back!
|
||||||
if self.view.take_focus(Direction::right()) {
|
self.view
|
||||||
EventResult::Consumed(None)
|
.take_focus(Direction::right())
|
||||||
} else {
|
.unwrap_or(EventResult::Ignored)
|
||||||
EventResult::Ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(EventResult::Ignored, Event::Key(Key::Up))
|
(EventResult::Ignored, Event::Key(Key::Up))
|
||||||
if self.wrap_arrows =>
|
if self.wrap_arrows =>
|
||||||
{
|
{
|
||||||
// Focus comes back!
|
// Focus comes back!
|
||||||
if self.view.take_focus(Direction::down()) {
|
self.view
|
||||||
EventResult::Consumed(None)
|
.take_focus(Direction::down())
|
||||||
} else {
|
.unwrap_or(EventResult::Ignored)
|
||||||
EventResult::Ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(EventResult::Ignored, Event::Key(Key::Down))
|
(EventResult::Ignored, Event::Key(Key::Down))
|
||||||
if self.wrap_arrows =>
|
if self.wrap_arrows =>
|
||||||
{
|
{
|
||||||
// Focus comes back!
|
// Focus comes back!
|
||||||
if self.view.take_focus(Direction::up()) {
|
self.view
|
||||||
EventResult::Consumed(None)
|
.take_focus(Direction::up())
|
||||||
} else {
|
.unwrap_or(EventResult::Ignored)
|
||||||
EventResult::Ignored
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(other, _) => other,
|
(other, _) => other,
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ use crate::{
|
|||||||
rect::Rect,
|
rect::Rect,
|
||||||
theme::ColorStyle,
|
theme::ColorStyle,
|
||||||
utils::markup::StyledString,
|
utils::markup::StyledString,
|
||||||
view::{IntoBoxedView, Margins, Selector, View, ViewNotFound},
|
view::{
|
||||||
|
CannotFocus, IntoBoxedView, Margins, Selector, View, ViewNotFound,
|
||||||
|
},
|
||||||
views::{BoxedView, Button, DummyView, LastSizeView, TextView},
|
views::{BoxedView, Button, DummyView, LastSizeView, TextView},
|
||||||
Cursive, Printer, Vec2, With,
|
Cursive, Printer, Vec2, With,
|
||||||
};
|
};
|
||||||
@ -227,11 +229,17 @@ impl Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Removes any button from `self`.
|
/// Removes any button from `self`.
|
||||||
pub fn clear_buttons(&mut self) {
|
pub fn clear_buttons(&mut self) -> EventResult {
|
||||||
self.buttons.clear();
|
self.buttons.clear();
|
||||||
self.invalidate();
|
self.invalidate();
|
||||||
self.content.take_focus(Direction::none());
|
if self.focus != DialogFocus::Content {
|
||||||
self.focus = DialogFocus::Content;
|
self.focus = DialogFocus::Content;
|
||||||
|
self.content
|
||||||
|
.take_focus(Direction::none())
|
||||||
|
.unwrap_or(EventResult::Ignored)
|
||||||
|
} else {
|
||||||
|
EventResult::Ignored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a button from this dialog.
|
/// Removes a button from this dialog.
|
||||||
@ -239,21 +247,24 @@ impl Dialog {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `i >= self.buttons_len()`.
|
/// Panics if `i >= self.buttons_len()`.
|
||||||
pub fn remove_button(&mut self, i: usize) {
|
pub fn remove_button(&mut self, i: usize) -> EventResult {
|
||||||
self.buttons.remove(i);
|
self.buttons.remove(i);
|
||||||
self.invalidate();
|
self.invalidate();
|
||||||
// Fix focus?
|
// Fix focus?
|
||||||
match (self.buttons.len(), self.focus) {
|
match (self.buttons.len(), self.focus) {
|
||||||
// TODO: take focus?
|
|
||||||
(0, ref mut focus) => {
|
(0, ref mut focus) => {
|
||||||
self.content.take_focus(Direction::none());
|
|
||||||
*focus = DialogFocus::Content;
|
*focus = DialogFocus::Content;
|
||||||
|
return self
|
||||||
|
.content
|
||||||
|
.take_focus(Direction::none())
|
||||||
|
.unwrap_or(EventResult::Ignored);
|
||||||
}
|
}
|
||||||
(n, DialogFocus::Button(ref mut i)) => {
|
(n, DialogFocus::Button(ref mut i)) => {
|
||||||
*i = usize::min(*i, n - 1);
|
*i = usize::min(*i, n - 1);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
EventResult::Ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the horizontal alignment for the buttons, if any.
|
/// Sets the horizontal alignment for the buttons, if any.
|
||||||
@ -453,6 +464,7 @@ impl Dialog {
|
|||||||
DialogFocus::Content
|
DialogFocus::Content
|
||||||
}
|
}
|
||||||
DialogFocus::Button(c) => {
|
DialogFocus::Button(c) => {
|
||||||
|
// TODO: send Event::LostFocus?
|
||||||
DialogFocus::Button(min(c, self.buttons.len() - 1))
|
DialogFocus::Button(min(c, self.buttons.len() - 1))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -500,9 +512,11 @@ impl Dialog {
|
|||||||
match event {
|
match event {
|
||||||
// Up goes back to the content
|
// Up goes back to the content
|
||||||
Event::Key(Key::Up) => {
|
Event::Key(Key::Up) => {
|
||||||
if self.content.take_focus(Direction::down()) {
|
if let Ok(res) =
|
||||||
|
self.content.take_focus(Direction::down())
|
||||||
|
{
|
||||||
self.focus = DialogFocus::Content;
|
self.focus = DialogFocus::Content;
|
||||||
EventResult::Consumed(None)
|
res
|
||||||
} else {
|
} else {
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
}
|
}
|
||||||
@ -511,9 +525,11 @@ impl Dialog {
|
|||||||
if self.focus == DialogFocus::Button(0) =>
|
if self.focus == DialogFocus::Button(0) =>
|
||||||
{
|
{
|
||||||
// If we're at the first button, jump back to the content.
|
// If we're at the first button, jump back to the content.
|
||||||
if self.content.take_focus(Direction::back()) {
|
if let Ok(res) =
|
||||||
|
self.content.take_focus(Direction::back())
|
||||||
|
{
|
||||||
self.focus = DialogFocus::Content;
|
self.focus = DialogFocus::Content;
|
||||||
EventResult::Consumed(None)
|
res
|
||||||
} else {
|
} else {
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
}
|
}
|
||||||
@ -651,7 +667,7 @@ impl Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_focus_grab(&mut self, event: &Event) {
|
fn check_focus_grab(&mut self, event: &Event) -> Option<EventResult> {
|
||||||
if let Event::Mouse {
|
if let Event::Mouse {
|
||||||
offset,
|
offset,
|
||||||
position,
|
position,
|
||||||
@ -659,11 +675,11 @@ impl Dialog {
|
|||||||
} = *event
|
} = *event
|
||||||
{
|
{
|
||||||
if !event.grabs_focus() {
|
if !event.grabs_focus() {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let position = match position.checked_sub(offset) {
|
let position = match position.checked_sub(offset) {
|
||||||
None => return,
|
None => return None,
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -678,12 +694,15 @@ impl Dialog {
|
|||||||
} else if position.fits_in_rect(
|
} else if position.fits_in_rect(
|
||||||
(self.padding + self.borders).top_left(),
|
(self.padding + self.borders).top_left(),
|
||||||
self.content.size,
|
self.content.size,
|
||||||
) && self.content.take_focus(Direction::none())
|
) {
|
||||||
{
|
if let Ok(res) = self.content.take_focus(Direction::none()) {
|
||||||
// Or did we click the content?
|
// Or did we click the content?
|
||||||
self.focus = DialogFocus::Content;
|
self.focus = DialogFocus::Content;
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&mut self) {
|
fn invalidate(&mut self) {
|
||||||
@ -777,52 +796,67 @@ impl View for Dialog {
|
|||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
// First: some mouse events can instantly change the focus.
|
// First: some mouse events can instantly change the focus.
|
||||||
self.check_focus_grab(&event);
|
let res = self
|
||||||
|
.check_focus_grab(&event)
|
||||||
|
.unwrap_or(EventResult::Ignored);
|
||||||
|
|
||||||
match self.focus {
|
res.and(match self.focus {
|
||||||
// If we are on the content, we can only go down.
|
// If we are on the content, we can only go down.
|
||||||
// TODO: Careful if/when we add buttons elsewhere on the dialog!
|
// TODO: Careful if/when we add buttons elsewhere on the dialog!
|
||||||
DialogFocus::Content => self.on_event_content(event),
|
DialogFocus::Content => self.on_event_content(event),
|
||||||
// If we are on a button, we have more choice
|
// If we are on a button, we have more choice
|
||||||
DialogFocus::Button(i) => self.on_event_button(event, i),
|
DialogFocus::Button(i) => self.on_event_button(event, i),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
// TODO: This may depend on button position relative to the content?
|
// TODO: This may depend on button position relative to the content?
|
||||||
//
|
//
|
||||||
match source {
|
match source {
|
||||||
Direction::Abs(Absolute::None) => {
|
Direction::Abs(Absolute::None) => {
|
||||||
// Only reject focus if no buttons and no focus-taking content.
|
// Only reject focus if no buttons and no focus-taking content.
|
||||||
// Also fix focus if we're focusing the wrong thing.
|
// Also fix focus if we're focusing the wrong thing.
|
||||||
match (
|
match (self.focus, !self.buttons.is_empty()) {
|
||||||
self.focus,
|
(DialogFocus::Button(_), true) => {
|
||||||
self.content.take_focus(source),
|
// Focus stays on the button.
|
||||||
!self.buttons.is_empty(),
|
Ok(EventResult::Consumed(None))
|
||||||
) {
|
|
||||||
(DialogFocus::Content, false, true) => {
|
|
||||||
self.focus = DialogFocus::Button(0);
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
(DialogFocus::Button(_), true, false) => {
|
(DialogFocus::Button(_), false) => {
|
||||||
self.focus = DialogFocus::Content;
|
let res = self.content.take_focus(source);
|
||||||
true
|
if res.is_ok() {
|
||||||
|
self.focus = DialogFocus::Content;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
(DialogFocus::Content, false) => {
|
||||||
|
self.content.take_focus(source)
|
||||||
|
}
|
||||||
|
(DialogFocus::Content, true) => {
|
||||||
|
match self.content.take_focus(source) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(CannotFocus) => {
|
||||||
|
self.focus = DialogFocus::Button(0);
|
||||||
|
Ok(EventResult::Consumed(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(_, content, buttons) => content || buttons,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::Rel(Relative::Front)
|
Direction::Rel(Relative::Front)
|
||||||
| Direction::Abs(Absolute::Left)
|
| Direction::Abs(Absolute::Left)
|
||||||
| Direction::Abs(Absolute::Up) => {
|
| Direction::Abs(Absolute::Up) => {
|
||||||
// Forward focus: content, then buttons
|
// Forward focus: content, then buttons
|
||||||
if self.content.take_focus(source) {
|
if let Ok(res) = self.content.take_focus(source) {
|
||||||
self.focus = DialogFocus::Content;
|
self.focus = DialogFocus::Content;
|
||||||
true
|
Ok(res)
|
||||||
} else if self.buttons.is_empty() {
|
} else if self.buttons.is_empty() {
|
||||||
false
|
Err(CannotFocus)
|
||||||
} else {
|
} else {
|
||||||
self.focus = DialogFocus::Button(0);
|
self.focus = DialogFocus::Button(0);
|
||||||
true
|
Ok(EventResult::Consumed(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction::Rel(Relative::Back)
|
Direction::Rel(Relative::Back)
|
||||||
@ -831,12 +865,12 @@ impl View for Dialog {
|
|||||||
// Back focus: first buttons, then content
|
// Back focus: first buttons, then content
|
||||||
if !self.buttons.is_empty() {
|
if !self.buttons.is_empty() {
|
||||||
self.focus = DialogFocus::Button(self.buttons.len() - 1);
|
self.focus = DialogFocus::Button(self.buttons.len() - 1);
|
||||||
true
|
Ok(EventResult::Consumed(None))
|
||||||
} else if self.content.take_focus(source) {
|
} else if let Ok(res) = self.content.take_focus(source) {
|
||||||
self.focus = DialogFocus::Content;
|
self.focus = DialogFocus::Content;
|
||||||
true
|
Ok(res)
|
||||||
} else {
|
} else {
|
||||||
false
|
Err(CannotFocus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -853,7 +887,7 @@ impl View for Dialog {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
self.content.focus_view(selector)
|
self.content.focus_view(selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use crate::direction::Direction;
|
use crate::{
|
||||||
use crate::event::{Callback, Event, EventResult, Key, MouseEvent};
|
direction::Direction,
|
||||||
use crate::rect::Rect;
|
event::{Callback, Event, EventResult, Key, MouseEvent},
|
||||||
use crate::theme::{ColorStyle, Effect};
|
rect::Rect,
|
||||||
use crate::utils::lines::simple::{simple_prefix, simple_suffix};
|
theme::{ColorStyle, Effect},
|
||||||
use crate::view::View;
|
utils::lines::simple::{simple_prefix, simple_suffix},
|
||||||
use crate::Vec2;
|
view::{CannotFocus, View},
|
||||||
use crate::{Cursive, Printer, With};
|
Cursive, Printer, Vec2, With,
|
||||||
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
@ -596,8 +597,11 @@ impl View for EditView {
|
|||||||
self.last_length = size.x;
|
self.last_length = size.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
self.enabled
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
direction::{Absolute, Direction, Relative},
|
direction::{Absolute, Direction, Relative},
|
||||||
event::{AnyCb, Event, EventResult, Key},
|
event::{AnyCb, Event, EventResult, Key},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
view::{IntoBoxedView, Selector, ViewNotFound},
|
view::{CannotFocus, IntoBoxedView, Selector, ViewNotFound},
|
||||||
{Printer, Vec2, View, With},
|
{Printer, Vec2, View, With},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,6 +39,15 @@ struct Child {
|
|||||||
position: Rect,
|
position: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Child {
|
||||||
|
// Convenient function to look for a focusable child in an iterator.
|
||||||
|
fn focuser(
|
||||||
|
source: Direction,
|
||||||
|
) -> impl Fn((usize, &mut Self)) -> Option<(usize, EventResult)> {
|
||||||
|
move |(i, c)| c.view.take_focus(source).ok().map(|res| (i, res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_default!(FixedLayout);
|
new_default!(FixedLayout);
|
||||||
|
|
||||||
impl FixedLayout {
|
impl FixedLayout {
|
||||||
@ -75,17 +84,22 @@ impl FixedLayout {
|
|||||||
pub fn set_focus_index(
|
pub fn set_focus_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
if self
|
self.children
|
||||||
.children
|
|
||||||
.get_mut(index)
|
.get_mut(index)
|
||||||
.map(|child| child.view.take_focus(Direction::none()))
|
.and_then(|child| child.view.take_focus(Direction::none()).ok())
|
||||||
.unwrap_or(false)
|
.map(|res| self.set_focus_unchecked(index).and(res))
|
||||||
{
|
.ok_or(ViewNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_focus_unchecked(&mut self, index: usize) -> EventResult {
|
||||||
|
if index != self.focus {
|
||||||
|
let result =
|
||||||
|
self.children[self.focus].view.on_event(Event::FocusLost);
|
||||||
self.focus = index;
|
self.focus = index;
|
||||||
Ok(())
|
result
|
||||||
} else {
|
} else {
|
||||||
Err(ViewNotFound)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,13 +197,12 @@ impl FixedLayout {
|
|||||||
|
|
||||||
fn move_focus_rel(&mut self, target: Relative) -> EventResult {
|
fn move_focus_rel(&mut self, target: Relative) -> EventResult {
|
||||||
let source = Direction::Rel(target.swap());
|
let source = Direction::Rel(target.swap());
|
||||||
for (i, c) in
|
let focus_res = Self::iter_mut(source, &mut self.children)
|
||||||
Self::iter_mut(source, &mut self.children).skip(self.focus + 1)
|
.skip(self.focus + 1)
|
||||||
{
|
.find_map(Child::focuser(source));
|
||||||
if c.view.take_focus(source) {
|
|
||||||
self.focus = i;
|
if let Some((i, res)) = focus_res {
|
||||||
return EventResult::Consumed(None);
|
return self.set_focus_unchecked(i).and(res);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
@ -207,8 +220,8 @@ impl FixedLayout {
|
|||||||
let current_side = current_position.side(orientation.swap());
|
let current_side = current_position.side(orientation.swap());
|
||||||
let current_edge = current_position.edge(target);
|
let current_edge = current_position.edge(target);
|
||||||
|
|
||||||
let children =
|
let focus_res = Self::iter_mut(source, &mut self.children)
|
||||||
Self::iter_mut(source, &mut self.children).filter(|(_, c)| {
|
.filter(|(_, c)| {
|
||||||
// Only select children actually aligned with us
|
// Only select children actually aligned with us
|
||||||
Some(rel)
|
Some(rel)
|
||||||
== Relative::a_to_b(current_edge, c.position.edge(target))
|
== Relative::a_to_b(current_edge, c.position.edge(target))
|
||||||
@ -216,19 +229,16 @@ impl FixedLayout {
|
|||||||
c.position.side(orientation.swap()),
|
c.position.side(orientation.swap()),
|
||||||
current_side,
|
current_side,
|
||||||
)
|
)
|
||||||
});
|
})
|
||||||
|
.find_map(Child::focuser(source));
|
||||||
|
|
||||||
for (i, c) in children {
|
if let Some((i, res)) = focus_res {
|
||||||
if c.view.take_focus(source) {
|
return self.set_focus_unchecked(i).and(res);
|
||||||
self.focus = i;
|
|
||||||
return EventResult::Consumed(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_focus_grab(&mut self, event: &Event) {
|
fn check_focus_grab(&mut self, event: &Event) -> Option<EventResult> {
|
||||||
if let Event::Mouse {
|
if let Event::Mouse {
|
||||||
offset,
|
offset,
|
||||||
position,
|
position,
|
||||||
@ -236,22 +246,26 @@ impl FixedLayout {
|
|||||||
} = *event
|
} = *event
|
||||||
{
|
{
|
||||||
if !event.grabs_focus() {
|
if !event.grabs_focus() {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let position = match position.checked_sub(offset) {
|
let position = match position.checked_sub(offset) {
|
||||||
None => return,
|
None => return None,
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, child) in self.children.iter_mut().enumerate() {
|
if let Some((i, res)) = self
|
||||||
if child.position.contains(position)
|
.children
|
||||||
&& child.view.take_focus(Direction::none())
|
.iter_mut()
|
||||||
{
|
.enumerate()
|
||||||
self.focus = i;
|
.filter(|(_, c)| c.position.contains(position))
|
||||||
}
|
.find_map(Child::focuser(Direction::none()))
|
||||||
|
{
|
||||||
|
return Some(self.set_focus_unchecked(i).and(res));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +288,9 @@ impl View for FixedLayout {
|
|||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_focus_grab(&event);
|
let res = self
|
||||||
|
.check_focus_grab(&event)
|
||||||
|
.unwrap_or(EventResult::Ignored);
|
||||||
|
|
||||||
let child = &mut self.children[self.focus];
|
let child = &mut self.children[self.focus];
|
||||||
|
|
||||||
@ -282,7 +298,7 @@ impl View for FixedLayout {
|
|||||||
.view
|
.view
|
||||||
.on_event(event.relativized(child.position.top_left()));
|
.on_event(event.relativized(child.position.top_left()));
|
||||||
|
|
||||||
match result {
|
res.and(match result {
|
||||||
EventResult::Ignored => match event {
|
EventResult::Ignored => match event {
|
||||||
Event::Shift(Key::Tab) => self.move_focus_rel(Relative::Front),
|
Event::Shift(Key::Tab) => self.move_focus_rel(Relative::Front),
|
||||||
Event::Key(Key::Tab) => self.move_focus_rel(Relative::Back),
|
Event::Key(Key::Tab) => self.move_focus_rel(Relative::Back),
|
||||||
@ -293,7 +309,7 @@ impl View for FixedLayout {
|
|||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
},
|
},
|
||||||
res => res,
|
res => res,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn important_area(&self, size: Vec2) -> Rect {
|
fn important_area(&self, size: Vec2) -> Rect {
|
||||||
@ -314,33 +330,33 @@ impl View for FixedLayout {
|
|||||||
.fold(Vec2::zero(), Vec2::max)
|
.fold(Vec2::zero(), Vec2::max)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
match source {
|
match source {
|
||||||
Direction::Abs(Absolute::None) => {
|
Direction::Abs(Absolute::None) => {
|
||||||
// We want to guarantee:
|
// We want to guarantee:
|
||||||
// * If the current focus _is_ focusable, keep it
|
// * If the current focus _is_ focusable, keep it
|
||||||
// * If it isn't, find _any_ focusable view, and focus it
|
// * If it isn't, find _any_ focusable view, and focus it
|
||||||
// * Otherwise, we can't take focus.
|
// * Otherwise, we can't take focus.
|
||||||
for (i, c) in
|
let focus_res =
|
||||||
Self::circular_mut(self.focus, &mut self.children)
|
Self::circular_mut(self.focus, &mut self.children)
|
||||||
{
|
.find_map(Child::focuser(source));
|
||||||
if c.view.take_focus(source) {
|
if let Some((i, res)) = focus_res {
|
||||||
self.focus = i;
|
return Ok(self.set_focus_unchecked(i).and(res));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Err(CannotFocus)
|
||||||
}
|
}
|
||||||
source => {
|
source => {
|
||||||
for (i, c) in Self::iter_mut(source, &mut self.children) {
|
let focus_res = Self::iter_mut(source, &mut self.children)
|
||||||
if c.view.take_focus(source) {
|
.find_map(Child::focuser(source));
|
||||||
self.focus = i;
|
if let Some((i, res)) = focus_res {
|
||||||
return true;
|
return Ok(self.set_focus_unchecked(i).and(res));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Err(CannotFocus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -358,12 +374,13 @@ impl View for FixedLayout {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
for (i, child) in self.children.iter_mut().enumerate() {
|
let focus_res =
|
||||||
if child.view.focus_view(selector).is_ok() {
|
self.children.iter_mut().enumerate().find_map(|(i, c)| {
|
||||||
self.focus = i;
|
c.view.focus_view(selector).ok().map(|res| (i, res))
|
||||||
return Ok(());
|
});
|
||||||
}
|
if let Some((i, res)) = focus_res {
|
||||||
|
return Ok(self.set_focus_unchecked(i).and(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ViewNotFound)
|
Err(ViewNotFound)
|
||||||
|
63
cursive-core/src/views/focus_tracker.rs
Normal file
63
cursive-core/src/views/focus_tracker.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use crate::{
|
||||||
|
direction::Direction,
|
||||||
|
event::{Event, EventResult},
|
||||||
|
view::{CannotFocus, View, ViewWrapper},
|
||||||
|
With,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Detects focus events for a view.
|
||||||
|
pub struct FocusTracker<T> {
|
||||||
|
view: T,
|
||||||
|
on_focus_lost: Box<dyn FnMut(&mut T) -> EventResult>,
|
||||||
|
on_focus: Box<dyn FnMut(&mut T) -> EventResult>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FocusTracker<T> {
|
||||||
|
/// Wraps a view in a new `FocusTracker`.
|
||||||
|
pub fn new(view: T) -> Self {
|
||||||
|
FocusTracker {
|
||||||
|
view,
|
||||||
|
on_focus_lost: Box::new(|_| EventResult::Ignored),
|
||||||
|
on_focus: Box::new(|_| EventResult::Ignored),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run when the focus is gained.
|
||||||
|
pub fn on_focus<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(&mut T) -> EventResult,
|
||||||
|
{
|
||||||
|
self.with(|s| s.on_focus = Box::new(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run when the focus is lost.
|
||||||
|
pub fn on_focus_lost<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(&mut T) -> EventResult,
|
||||||
|
{
|
||||||
|
self.with(|s| s.on_focus_lost = Box::new(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: View> ViewWrapper for FocusTracker<T> {
|
||||||
|
wrap_impl!(self.view: T);
|
||||||
|
|
||||||
|
fn wrap_take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
match self.view.take_focus(source) {
|
||||||
|
Ok(res) => Ok(res.and((self.on_focus)(&mut self.view))),
|
||||||
|
Err(CannotFocus) => Err(CannotFocus),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
let res = if let Event::FocusLost = event {
|
||||||
|
(self.on_focus_lost)(&mut self.view)
|
||||||
|
} else {
|
||||||
|
EventResult::Ignored
|
||||||
|
};
|
||||||
|
res.and(self.view.on_event(event))
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
|
/// Event fired when the view is about to lose focus.
|
||||||
use crate::{
|
use crate::{
|
||||||
direction,
|
direction,
|
||||||
event::{AnyCb, Event, EventResult, Key},
|
event::{AnyCb, Event, EventResult, Key},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
view::{IntoBoxedView, Selector, SizeCache, View, ViewNotFound},
|
view::{
|
||||||
|
CannotFocus, IntoBoxedView, Selector, SizeCache, View, ViewNotFound,
|
||||||
|
},
|
||||||
Printer, Vec2, With, XY,
|
Printer, Vec2, With, XY,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@ -230,17 +233,24 @@ impl LinearLayout {
|
|||||||
pub fn set_focus_index(
|
pub fn set_focus_index(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
if self
|
self.children
|
||||||
.children
|
|
||||||
.get_mut(index)
|
.get_mut(index)
|
||||||
.map(|child| child.view.take_focus(direction::Direction::none()))
|
.and_then(|child| {
|
||||||
.unwrap_or(false)
|
child.view.take_focus(direction::Direction::none()).ok()
|
||||||
{
|
})
|
||||||
|
.map(|res| res.and(self.set_focus_unchecked(index)))
|
||||||
|
.ok_or(ViewNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_focus_unchecked(&mut self, index: usize) -> EventResult {
|
||||||
|
if index != self.focus {
|
||||||
|
let result =
|
||||||
|
self.children[self.focus].view.on_event(Event::FocusLost);
|
||||||
self.focus = index;
|
self.focus = index;
|
||||||
Ok(())
|
result
|
||||||
} else {
|
} else {
|
||||||
Err(ViewNotFound)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,19 +383,17 @@ impl LinearLayout {
|
|||||||
// We don't want that one.
|
// We don't want that one.
|
||||||
self.iter_mut(true, rel)
|
self.iter_mut(true, rel)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.filter_map(|p| try_focus(p, source))
|
.find_map(|p| try_focus(p, source))
|
||||||
.next()
|
|
||||||
})
|
})
|
||||||
.map_or(EventResult::Ignored, |i| {
|
.map_or(EventResult::Ignored, |(i, res)| {
|
||||||
self.focus = i;
|
res.and(self.set_focus_unchecked(i))
|
||||||
EventResult::Consumed(None)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the focus to the selected view if needed.
|
// Move the focus to the selected view if needed.
|
||||||
//
|
//
|
||||||
// Does nothing if the event is not a `MouseEvent`.
|
// Does nothing if the event is not a `MouseEvent`.
|
||||||
fn check_focus_grab(&mut self, event: &Event) {
|
fn check_focus_grab(&mut self, event: &Event) -> Option<EventResult> {
|
||||||
if let Event::Mouse {
|
if let Event::Mouse {
|
||||||
offset,
|
offset,
|
||||||
position,
|
position,
|
||||||
@ -393,11 +401,11 @@ impl LinearLayout {
|
|||||||
} = *event
|
} = *event
|
||||||
{
|
{
|
||||||
if !event.grabs_focus() {
|
if !event.grabs_focus() {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let position = match position.checked_sub(offset) {
|
let position = match position.checked_sub(offset) {
|
||||||
None => return,
|
None => return None,
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -419,27 +427,27 @@ impl LinearLayout {
|
|||||||
// this will give us the allowed window for a click.
|
// this will give us the allowed window for a click.
|
||||||
let child_size = item.child.last_size.get(self.orientation);
|
let child_size = item.child.last_size.get(self.orientation);
|
||||||
|
|
||||||
if item.offset + child_size > position {
|
if item.offset + child_size <= position {
|
||||||
if item.child.view.take_focus(direction::Direction::none())
|
continue;
|
||||||
{
|
|
||||||
self.focus = i;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
.child
|
||||||
|
.view
|
||||||
|
.take_focus(direction::Direction::none())
|
||||||
|
.ok()
|
||||||
|
.map(|res| res.and(self.set_focus_unchecked(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_focus(
|
fn try_focus(
|
||||||
(i, child): (usize, &mut Child),
|
(i, child): (usize, &mut Child),
|
||||||
source: direction::Direction,
|
source: direction::Direction,
|
||||||
) -> Option<usize> {
|
) -> Option<(usize, EventResult)> {
|
||||||
if child.view.take_focus(source) {
|
child.view.take_focus(source).ok().map(|res| (i, res))
|
||||||
Some(i)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for LinearLayout {
|
impl View for LinearLayout {
|
||||||
@ -623,24 +631,24 @@ impl View for LinearLayout {
|
|||||||
compromise
|
compromise
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: direction::Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
// In what order will we iterate on the children?
|
// In what order will we iterate on the children?
|
||||||
let rel = source.relative(self.orientation);
|
let rel = source.relative(self.orientation);
|
||||||
// We activate from_focus only if coming from the "sides".
|
|
||||||
let mut get_next_focus = || {
|
|
||||||
self.iter_mut(
|
|
||||||
rel.is_none(),
|
|
||||||
rel.unwrap_or(direction::Relative::Front),
|
|
||||||
)
|
|
||||||
.filter_map(|p| try_focus(p, source))
|
|
||||||
.next()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(i) = get_next_focus() {
|
// We activate from_focus only if coming from the "sides".
|
||||||
self.focus = i;
|
let focus_res = self
|
||||||
true
|
.iter_mut(rel.is_none(), rel.unwrap_or(direction::Relative::Front))
|
||||||
|
.find_map(|p| try_focus(p, source));
|
||||||
|
|
||||||
|
if let Some((next_focus, res)) = focus_res {
|
||||||
|
// No "FocusLost" here, since we didn't have focus before.
|
||||||
|
self.focus = next_focus;
|
||||||
|
Ok(res)
|
||||||
} else {
|
} else {
|
||||||
false
|
Err(CannotFocus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,7 +657,9 @@ impl View for LinearLayout {
|
|||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_focus_grab(&event);
|
let res = self
|
||||||
|
.check_focus_grab(&event)
|
||||||
|
.unwrap_or(EventResult::Ignored);
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let mut iterator = ChildIterator::new(
|
let mut iterator = ChildIterator::new(
|
||||||
@ -661,7 +671,7 @@ impl View for LinearLayout {
|
|||||||
let offset = self.orientation.make_vec(item.offset, 0);
|
let offset = self.orientation.make_vec(item.offset, 0);
|
||||||
item.child.view.on_event(event.relativized(offset))
|
item.child.view.on_event(event.relativized(offset))
|
||||||
};
|
};
|
||||||
match result {
|
res.and(match result {
|
||||||
EventResult::Ignored => match event {
|
EventResult::Ignored => match event {
|
||||||
Event::Shift(Key::Tab) if self.focus > 0 => {
|
Event::Shift(Key::Tab) if self.focus > 0 => {
|
||||||
self.move_focus(direction::Direction::back())
|
self.move_focus(direction::Direction::back())
|
||||||
@ -702,7 +712,7 @@ impl View for LinearLayout {
|
|||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
},
|
},
|
||||||
res => res,
|
res => res,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_on_any<'a>(
|
fn call_on_any<'a>(
|
||||||
@ -718,11 +728,10 @@ impl View for LinearLayout {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
for (i, child) in self.children.iter_mut().enumerate() {
|
for (i, child) in self.children.iter_mut().enumerate() {
|
||||||
if child.view.focus_view(selector).is_ok() {
|
if child.view.focus_view(selector).is_ok() {
|
||||||
self.focus = i;
|
return Ok(self.set_focus_unchecked(i));
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
direction,
|
direction,
|
||||||
event::{AnyCb, Callback, Event, EventResult, Key},
|
event::{AnyCb, Callback, Event, EventResult, Key},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
view::{IntoBoxedView, Selector, View, ViewNotFound},
|
view::{CannotFocus, IntoBoxedView, Selector, View, ViewNotFound},
|
||||||
Cursive, Printer, Vec2, With,
|
Cursive, Printer, Vec2, With,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@ -96,8 +96,10 @@ impl ListView {
|
|||||||
label: &str,
|
label: &str,
|
||||||
view: V,
|
view: V,
|
||||||
) {
|
) {
|
||||||
let mut view = view.into_boxed_view();
|
let view = view.into_boxed_view();
|
||||||
view.take_focus(direction::Direction::none());
|
|
||||||
|
// Why were we doing this here?
|
||||||
|
// view.take_focus(direction::Direction::none());
|
||||||
self.children.push(ListChild::Row(label.to_string(), view));
|
self.children.push(ListChild::Row(label.to_string(), view));
|
||||||
self.children_heights.push(0);
|
self.children_heights.push(0);
|
||||||
}
|
}
|
||||||
@ -106,7 +108,6 @@ impl ListView {
|
|||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.children.clear();
|
self.children.clear();
|
||||||
self.children_heights.clear();
|
self.children_heights.clear();
|
||||||
self.focus = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a view to the end of the list.
|
/// Adds a view to the end of the list.
|
||||||
@ -190,12 +191,31 @@ impl ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unfocus_child(&mut self) -> EventResult {
|
||||||
|
self.children
|
||||||
|
.get_mut(self.focus)
|
||||||
|
.and_then(ListChild::view)
|
||||||
|
.map(|v| v.on_event(Event::FocusLost))
|
||||||
|
.unwrap_or(EventResult::Ignored)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move focus to the given index, regardless of whether that child accepts focus.
|
||||||
|
fn set_focus_unchecked(&mut self, index: usize) -> EventResult {
|
||||||
|
if index != self.focus {
|
||||||
|
let res = self.unfocus_child();
|
||||||
|
self.focus = index;
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn move_focus(
|
fn move_focus(
|
||||||
&mut self,
|
&mut self,
|
||||||
n: usize,
|
n: usize,
|
||||||
source: direction::Direction,
|
source: direction::Direction,
|
||||||
) -> EventResult {
|
) -> EventResult {
|
||||||
let i = if let Some(i) = source
|
let (i, res) = if let Some((i, res)) = source
|
||||||
.relative(direction::Orientation::Vertical)
|
.relative(direction::Orientation::Vertical)
|
||||||
.and_then(|rel| {
|
.and_then(|rel| {
|
||||||
// The iterator starts at the focused element.
|
// The iterator starts at the focused element.
|
||||||
@ -206,17 +226,17 @@ impl ListView {
|
|||||||
.take(n)
|
.take(n)
|
||||||
.last()
|
.last()
|
||||||
}) {
|
}) {
|
||||||
i
|
(i, res)
|
||||||
} else {
|
} else {
|
||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
};
|
};
|
||||||
self.focus = i;
|
self.set_focus_unchecked(i);
|
||||||
|
|
||||||
EventResult::Consumed(self.on_select.clone().map(|cb| {
|
res.and(EventResult::Consumed(self.on_select.clone().map(|cb| {
|
||||||
let i = self.focus();
|
let i = self.focus();
|
||||||
let focused_string = String::from(self.children[i].label());
|
let focused_string = String::from(self.children[i].label());
|
||||||
Callback::from_fn(move |s| cb(s, &focused_string))
|
Callback::from_fn(move |s| cb(s, &focused_string))
|
||||||
}))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn labels_width(&self) -> usize {
|
fn labels_width(&self) -> usize {
|
||||||
@ -228,7 +248,7 @@ impl ListView {
|
|||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_focus_grab(&mut self, event: &Event) {
|
fn check_focus_grab(&mut self, event: &Event) -> Option<EventResult> {
|
||||||
if let Event::Mouse {
|
if let Event::Mouse {
|
||||||
offset,
|
offset,
|
||||||
position,
|
position,
|
||||||
@ -236,51 +256,56 @@ impl ListView {
|
|||||||
} = *event
|
} = *event
|
||||||
{
|
{
|
||||||
if !event.grabs_focus() {
|
if !event.grabs_focus() {
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut position = match position.checked_sub(offset) {
|
let mut position = match position.checked_sub(offset) {
|
||||||
None => return,
|
None => return None,
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
// eprintln!("Rel pos: {:?}", position);
|
// eprintln!("Rel pos: {:?}", position);
|
||||||
|
|
||||||
// Now that we have a relative position, checks for buttons?
|
// Now that we have a relative position, checks for buttons?
|
||||||
for (i, (child, height)) in self
|
for (i, (child, &height)) in self
|
||||||
.children
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(&self.children_heights)
|
.zip(&self.children_heights)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
if position.y < *height {
|
if let Some(y) = position.y.checked_sub(height) {
|
||||||
if let ListChild::Row(_, ref mut view) = child {
|
// Not this child. Move on.
|
||||||
if view.take_focus(direction::Direction::none()) {
|
position.y = y;
|
||||||
self.focus = i;
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
position.y -= height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We found the correct target, try to focus it.
|
||||||
|
if let ListChild::Row(_, ref mut view) = child {
|
||||||
|
match view.take_focus(direction::Direction::none()) {
|
||||||
|
Ok(res) => {
|
||||||
|
return Some(self.set_focus_unchecked(i).and(res));
|
||||||
|
}
|
||||||
|
Err(CannotFocus) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We found the target, but we can't focus it.
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_focus(
|
fn try_focus(
|
||||||
(i, child): (usize, &mut ListChild),
|
(i, child): (usize, &mut ListChild),
|
||||||
source: direction::Direction,
|
source: direction::Direction,
|
||||||
) -> Option<usize> {
|
) -> Option<(usize, EventResult)> {
|
||||||
match *child {
|
match *child {
|
||||||
ListChild::Delimiter => None,
|
ListChild::Delimiter => None,
|
||||||
ListChild::Row(_, ref mut view) => {
|
ListChild::Row(_, ref mut view) => match view.take_focus(source) {
|
||||||
if view.take_focus(source) {
|
Ok(res) => Some((i, res)),
|
||||||
Some(i)
|
Err(CannotFocus) => None,
|
||||||
} else {
|
},
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,7 +392,9 @@ impl View for ListView {
|
|||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_focus_grab(&event);
|
let res = self
|
||||||
|
.check_focus_grab(&event)
|
||||||
|
.unwrap_or(EventResult::Ignored);
|
||||||
|
|
||||||
// Send the event to the focused child.
|
// Send the event to the focused child.
|
||||||
let labels_width = self.labels_width();
|
let labels_width = self.labels_width();
|
||||||
@ -376,12 +403,12 @@ impl View for ListView {
|
|||||||
let offset = (labels_width + 1, y);
|
let offset = (labels_width + 1, y);
|
||||||
let result = view.on_event(event.relativized(offset));
|
let result = view.on_event(event.relativized(offset));
|
||||||
if result.is_consumed() {
|
if result.is_consumed() {
|
||||||
return result;
|
return res.and(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the child ignored this event, change the focus.
|
// If the child ignored this event, change the focus.
|
||||||
match event {
|
res.and(match event {
|
||||||
Event::Key(Key::Up) if self.focus > 0 => {
|
Event::Key(Key::Up) if self.focus > 0 => {
|
||||||
self.move_focus(1, direction::Direction::down())
|
self.move_focus(1, direction::Direction::down())
|
||||||
}
|
}
|
||||||
@ -405,22 +432,24 @@ impl View for ListView {
|
|||||||
self.move_focus(1, direction::Direction::back())
|
self.move_focus(1, direction::Direction::back())
|
||||||
}
|
}
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: direction::Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
let rel = source.relative(direction::Orientation::Vertical);
|
let rel = source.relative(direction::Orientation::Vertical);
|
||||||
let i = if let Some(i) = self
|
let (i, res) = if let Some((i, res)) = self
|
||||||
.iter_mut(rel.is_none(), rel.unwrap_or(direction::Relative::Front))
|
.iter_mut(rel.is_none(), rel.unwrap_or(direction::Relative::Front))
|
||||||
.find_map(|p| try_focus(p, source))
|
.find_map(|p| try_focus(p, source))
|
||||||
{
|
{
|
||||||
i
|
(i, res)
|
||||||
} else {
|
} else {
|
||||||
// No one wants to be in focus
|
// No one wants to be in focus
|
||||||
return false;
|
return Err(CannotFocus);
|
||||||
};
|
};
|
||||||
self.focus = i;
|
Ok(self.set_focus_unchecked(i).and(res))
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_on_any<'a>(
|
fn call_on_any<'a>(
|
||||||
@ -436,17 +465,16 @@ impl View for ListView {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
// Try to focus each view. Skip over delimiters.
|
// Try to focus each view. Skip over delimiters.
|
||||||
if let Some(i) = self
|
if let Some((i, res)) = self
|
||||||
.children
|
.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
|
.filter_map(|(i, v)| v.view().map(|v| (i, v)))
|
||||||
.find_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
|
.find_map(|(i, v)| v.focus_view(selector).ok().map(|res| (i, res)))
|
||||||
{
|
{
|
||||||
self.focus = i;
|
Ok(self.set_focus_unchecked(i).and(res))
|
||||||
Ok(())
|
|
||||||
} else {
|
} else {
|
||||||
Err(ViewNotFound)
|
Err(ViewNotFound)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crate::direction;
|
use crate::{
|
||||||
use crate::event::*;
|
direction,
|
||||||
use crate::menu;
|
event::*,
|
||||||
use crate::rect::Rect;
|
menu,
|
||||||
use crate::theme::ColorStyle;
|
rect::Rect,
|
||||||
use crate::view::{Position, View};
|
theme::ColorStyle,
|
||||||
use crate::views::{MenuPopup, OnEventView};
|
view::{CannotFocus, Position, View},
|
||||||
use crate::Cursive;
|
views::{MenuPopup, OnEventView},
|
||||||
use crate::Printer;
|
Cursive, Printer, Vec2,
|
||||||
use crate::Vec2;
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
@ -393,9 +393,12 @@ impl View for Menubar {
|
|||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: direction::Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
_: direction::Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
self.state = State::Selected;
|
self.state = State::Selected;
|
||||||
true
|
Ok(EventResult::consumed())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
||||||
|
@ -70,6 +70,7 @@ mod dummy;
|
|||||||
mod edit_view;
|
mod edit_view;
|
||||||
mod enableable_view;
|
mod enableable_view;
|
||||||
mod fixed_layout;
|
mod fixed_layout;
|
||||||
|
mod focus_tracker;
|
||||||
mod hideable_view;
|
mod hideable_view;
|
||||||
mod last_size_view;
|
mod last_size_view;
|
||||||
mod layer;
|
mod layer;
|
||||||
@ -108,6 +109,7 @@ pub use self::{
|
|||||||
edit_view::EditView,
|
edit_view::EditView,
|
||||||
enableable_view::EnableableView,
|
enableable_view::EnableableView,
|
||||||
fixed_layout::FixedLayout,
|
fixed_layout::FixedLayout,
|
||||||
|
focus_tracker::FocusTracker,
|
||||||
hideable_view::HideableView,
|
hideable_view::HideableView,
|
||||||
last_size_view::LastSizeView,
|
last_size_view::LastSizeView,
|
||||||
layer::Layer,
|
layer::Layer,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
event::AnyCb,
|
event::{AnyCb, EventResult},
|
||||||
view::{Selector, View, ViewNotFound, ViewWrapper},
|
view::{Selector, View, ViewNotFound, ViewWrapper},
|
||||||
};
|
};
|
||||||
use owning_ref::{OwningHandle, RcRef};
|
use owning_ref::{OwningHandle, RcRef};
|
||||||
@ -111,13 +111,13 @@ impl<T: View + 'static> ViewWrapper for NamedView<T> {
|
|||||||
fn wrap_focus_view(
|
fn wrap_focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
match selector {
|
match selector {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
&Selector::Name(name) | &Selector::Id(name)
|
&Selector::Name(name) | &Selector::Id(name)
|
||||||
if name == self.name =>
|
if name == self.name =>
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(EventResult::Consumed(None))
|
||||||
}
|
}
|
||||||
s => self
|
s => self
|
||||||
.view
|
.view
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::direction::Direction;
|
use crate::{
|
||||||
use crate::event::{Event, EventResult, Key, MouseButton, MouseEvent};
|
direction::Direction,
|
||||||
use crate::theme::ColorStyle;
|
event::{Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
use crate::view::View;
|
theme::ColorStyle,
|
||||||
use crate::Cursive;
|
view::{CannotFocus, View},
|
||||||
use crate::Vec2;
|
Cursive, Printer, Vec2, With,
|
||||||
use crate::{Printer, With};
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -198,8 +198,11 @@ impl<T: 'static> View for RadioButton<T> {
|
|||||||
self.req_size()
|
self.req_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
self.enabled
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
self.enabled.then(EventResult::consumed).ok_or(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, printer: &Printer) {
|
fn draw(&self, printer: &Printer) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
event::AnyCb,
|
event::{AnyCb, EventResult},
|
||||||
view::{Selector, View, ViewNotFound},
|
view::{Selector, View, ViewNotFound},
|
||||||
views::BoxedView,
|
views::BoxedView,
|
||||||
};
|
};
|
||||||
@ -133,11 +133,11 @@ where
|
|||||||
fn wrap_focus_view(
|
fn wrap_focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
for (i, child) in self.screens.iter_mut().enumerate() {
|
for (i, child) in self.screens.iter_mut().enumerate() {
|
||||||
if child.focus_view(selector).is_ok() {
|
if let Ok(res) = child.focus_view(selector) {
|
||||||
self.active_screen = i;
|
self.active_screen = i;
|
||||||
return Ok(());
|
return Ok(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
direction::Direction,
|
direction::Direction,
|
||||||
event::{AnyCb, Event, EventResult},
|
event::{AnyCb, Event, EventResult},
|
||||||
view::{scroll, ScrollStrategy, Selector, View, ViewNotFound},
|
view::{
|
||||||
|
scroll, CannotFocus, ScrollStrategy, Selector, View, ViewNotFound,
|
||||||
|
},
|
||||||
Cursive, Printer, Rect, Vec2, With,
|
Cursive, Printer, Rect, Vec2, With,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -364,25 +366,35 @@ where
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
self.inner.focus_view(selector).map(|()| {
|
self.inner.focus_view(selector).map(|res| {
|
||||||
self.scroll_to_important_area();
|
self.scroll_to_important_area();
|
||||||
|
res
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
// If the inner view takes focus, re-align the important area.
|
// If the inner view takes focus, re-align the important area.
|
||||||
if self.inner.take_focus(source) {
|
match self.inner.take_focus(source) {
|
||||||
// Don't do anything if we come from `None`
|
Ok(res) => {
|
||||||
if source != Direction::none() {
|
// Don't do anything if we come from `None`
|
||||||
self.scroll_to_important_area();
|
if source != Direction::none() {
|
||||||
|
self.scroll_to_important_area();
|
||||||
|
|
||||||
// Note: we can't really return an `EventResult` here :(
|
// Note: we can't really return an `EventResult` here :(
|
||||||
self.on_scroll_callback();
|
self.on_scroll_callback();
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
true
|
Err(CannotFocus) => self
|
||||||
} else {
|
.core
|
||||||
self.core.is_scrolling().any()
|
.is_scrolling()
|
||||||
|
.any()
|
||||||
|
.then(|| EventResult::Consumed(None))
|
||||||
|
.ok_or(CannotFocus),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
use crate::align::{Align, HAlign, VAlign};
|
use crate::{
|
||||||
use crate::direction::Direction;
|
align::{Align, HAlign, VAlign},
|
||||||
use crate::event::{
|
direction,
|
||||||
Callback, Event, EventResult, Key, MouseButton, MouseEvent,
|
event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
|
menu,
|
||||||
|
rect::Rect,
|
||||||
|
theme::ColorStyle,
|
||||||
|
utils::markup::StyledString,
|
||||||
|
view::{CannotFocus, Position, View},
|
||||||
|
views::MenuPopup,
|
||||||
|
Cursive, Printer, Vec2, With,
|
||||||
};
|
};
|
||||||
use crate::menu;
|
|
||||||
use crate::rect::Rect;
|
|
||||||
use crate::theme::ColorStyle;
|
|
||||||
use crate::utils::markup::StyledString;
|
|
||||||
use crate::view::{Position, View};
|
|
||||||
use crate::views::MenuPopup;
|
|
||||||
use crate::Cursive;
|
|
||||||
use crate::Printer;
|
|
||||||
use crate::Vec2;
|
|
||||||
use crate::With;
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cmp::{min, Ordering};
|
use std::cmp::{min, Ordering};
|
||||||
@ -962,8 +959,24 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
self.enabled && !self.items.is_empty()
|
&mut self,
|
||||||
|
source: direction::Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
(self.enabled && !self.items.is_empty())
|
||||||
|
.then(|| {
|
||||||
|
match source {
|
||||||
|
direction::Direction::Abs(direction::Absolute::Up) => {
|
||||||
|
self.focus.set(0)
|
||||||
|
}
|
||||||
|
direction::Direction::Abs(direction::Absolute::Down) => {
|
||||||
|
self.focus.set(self.items.len().saturating_sub(1))
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
})
|
||||||
|
.ok_or(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use crate::direction::{Direction, Orientation};
|
use crate::{
|
||||||
use crate::event::{
|
direction::{Direction, Orientation},
|
||||||
Callback, Event, EventResult, Key, MouseButton, MouseEvent,
|
event::{Callback, Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
|
theme::ColorStyle,
|
||||||
|
view::{CannotFocus, View},
|
||||||
|
Cursive, Printer, Vec2, With,
|
||||||
};
|
};
|
||||||
use crate::theme::ColorStyle;
|
|
||||||
use crate::view::View;
|
|
||||||
use crate::Vec2;
|
|
||||||
use crate::With;
|
|
||||||
use crate::{Cursive, Printer};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// A horizontal or vertical slider.
|
/// A horizontal or vertical slider.
|
||||||
@ -232,7 +230,10 @@ impl View for SliderView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
true
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
Ok(EventResult::Consumed(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ use crate::{
|
|||||||
event::{AnyCb, Event, EventResult},
|
event::{AnyCb, Event, EventResult},
|
||||||
theme::ColorStyle,
|
theme::ColorStyle,
|
||||||
view::{
|
view::{
|
||||||
IntoBoxedView, Offset, Position, Selector, View, ViewNotFound,
|
CannotFocus, IntoBoxedView, Offset, Position, Selector, View,
|
||||||
ViewWrapper,
|
ViewNotFound, ViewWrapper,
|
||||||
},
|
},
|
||||||
views::{BoxedView, CircularFocus, Layer, ShadowView},
|
views::{BoxedView, CircularFocus, Layer, ShadowView},
|
||||||
Printer, Vec2, With,
|
Printer, Vec2, With,
|
||||||
@ -164,7 +164,10 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.take_focus(source),
|
ChildWrapper::Shadow(ref mut v) => v.take_focus(source),
|
||||||
ChildWrapper::Backfilled(ref mut v) => v.take_focus(source),
|
ChildWrapper::Backfilled(ref mut v) => v.take_focus(source),
|
||||||
@ -193,7 +196,7 @@ impl<T: View> View for ChildWrapper<T> {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
match *self {
|
match *self {
|
||||||
ChildWrapper::Shadow(ref mut v) => v.focus_view(selector),
|
ChildWrapper::Shadow(ref mut v) => v.focus_view(selector),
|
||||||
ChildWrapper::Backfilled(ref mut v) => v.focus_view(selector),
|
ChildWrapper::Backfilled(ref mut v) => v.focus_view(selector),
|
||||||
@ -257,7 +260,7 @@ impl StackView {
|
|||||||
let boxed = BoxedView::boxed(view);
|
let boxed = BoxedView::boxed(view);
|
||||||
self.layers.push(Child {
|
self.layers.push(Child {
|
||||||
view: ChildWrapper::Backfilled(Layer::new(
|
view: ChildWrapper::Backfilled(Layer::new(
|
||||||
CircularFocus::wrap_tab(boxed),
|
CircularFocus::new(boxed).wrap_tab(),
|
||||||
)),
|
)),
|
||||||
size: Vec2::zero(),
|
size: Vec2::zero(),
|
||||||
placement: Placement::Fullscreen,
|
placement: Placement::Fullscreen,
|
||||||
@ -372,9 +375,11 @@ impl StackView {
|
|||||||
self.layers.push(Child {
|
self.layers.push(Child {
|
||||||
// Skip padding for absolute/parent-placed views
|
// Skip padding for absolute/parent-placed views
|
||||||
view: ChildWrapper::Shadow(
|
view: ChildWrapper::Shadow(
|
||||||
ShadowView::new(Layer::new(CircularFocus::wrap_tab(boxed)))
|
ShadowView::new(Layer::new(
|
||||||
.top_padding(position.y == Offset::Center)
|
CircularFocus::new(boxed).wrap_tab(),
|
||||||
.left_padding(position.x == Offset::Center),
|
))
|
||||||
|
.top_padding(position.y == Offset::Center)
|
||||||
|
.left_padding(position.x == Offset::Center),
|
||||||
),
|
),
|
||||||
size: Vec2::new(0, 0),
|
size: Vec2::new(0, 0),
|
||||||
placement: Placement::Floating(position),
|
placement: Placement::Floating(position),
|
||||||
@ -397,7 +402,7 @@ impl StackView {
|
|||||||
{
|
{
|
||||||
let boxed = BoxedView::boxed(view);
|
let boxed = BoxedView::boxed(view);
|
||||||
self.layers.push(Child {
|
self.layers.push(Child {
|
||||||
view: ChildWrapper::Plain(CircularFocus::wrap_tab(boxed)),
|
view: ChildWrapper::Plain(CircularFocus::new(boxed).wrap_tab()),
|
||||||
size: Vec2::new(0, 0),
|
size: Vec2::new(0, 0),
|
||||||
placement: Placement::Floating(position),
|
placement: Placement::Floating(position),
|
||||||
virgin: true,
|
virgin: true,
|
||||||
@ -652,7 +657,9 @@ impl View for StackView {
|
|||||||
// The text view takes focus because it's scrolling, but it only
|
// The text view takes focus because it's scrolling, but it only
|
||||||
// knows that after a call to `layout()`.
|
// knows that after a call to `layout()`.
|
||||||
if layer.virgin {
|
if layer.virgin {
|
||||||
layer.view.take_focus(Direction::none());
|
// Here we can't really forward the callback.
|
||||||
|
// So just ignore the result. :(
|
||||||
|
layer.view.take_focus(Direction::none()).ok();
|
||||||
layer.virgin = false;
|
layer.virgin = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -667,9 +674,12 @@ impl View for StackView {
|
|||||||
.fold(Vec2::new(1, 1), Vec2::max)
|
.fold(Vec2::new(1, 1), Vec2::max)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
source: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
match self.layers.last_mut() {
|
match self.layers.last_mut() {
|
||||||
None => false,
|
None => Err(CannotFocus),
|
||||||
Some(v) => v.view.take_focus(source),
|
Some(v) => v.view.take_focus(source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -687,10 +697,10 @@ impl View for StackView {
|
|||||||
fn focus_view(
|
fn focus_view(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: &Selector<'_>,
|
selector: &Selector<'_>,
|
||||||
) -> Result<(), ViewNotFound> {
|
) -> Result<EventResult, ViewNotFound> {
|
||||||
for layer in &mut self.layers {
|
for layer in &mut self.layers {
|
||||||
if layer.view.focus_view(selector).is_ok() {
|
if layer.view.focus_view(selector).is_ok() {
|
||||||
return Ok(());
|
return Ok(EventResult::Consumed(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use crate::direction::Direction;
|
|
||||||
use crate::event::{Event, EventResult, Key, MouseButton, MouseEvent};
|
|
||||||
use crate::rect::Rect;
|
|
||||||
use crate::theme::{ColorStyle, Effect};
|
|
||||||
use crate::utils::lines::simple::{prefix, simple_prefix, LinesIterator, Row};
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
use crate::view::{ScrollBase, SizeCache, View};
|
use crate::{
|
||||||
use crate::Vec2;
|
direction::Direction,
|
||||||
use crate::{Printer, With, XY};
|
event::{Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
|
rect::Rect,
|
||||||
|
theme::{ColorStyle, Effect},
|
||||||
|
utils::lines::simple::{prefix, simple_prefix, LinesIterator, Row},
|
||||||
|
view::{CannotFocus, ScrollBase, SizeCache, View},
|
||||||
|
Vec2, {Printer, With, XY},
|
||||||
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
@ -639,8 +640,13 @@ impl View for TextArea {
|
|||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
self.enabled
|
self.enabled
|
||||||
|
.then(|| EventResult::Consumed(None))
|
||||||
|
.ok_or(CannotFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use cursive::views::{CircularFocus, Dialog, TextView};
|
use cursive::{
|
||||||
|
views::{CircularFocus, Dialog, TextView},
|
||||||
|
With as _,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Creates the cursive root - required for every application.
|
// Creates the cursive root - required for every application.
|
||||||
@ -7,12 +10,12 @@ fn main() {
|
|||||||
// Creates a dialog with a single "Quit" button
|
// Creates a dialog with a single "Quit" button
|
||||||
siv.add_layer(
|
siv.add_layer(
|
||||||
// Most views can be configured in a chainable way
|
// Most views can be configured in a chainable way
|
||||||
CircularFocus::wrap_tab(
|
Dialog::around(TextView::new("Hello Dialog!"))
|
||||||
Dialog::around(TextView::new("Hello Dialog!"))
|
.title("Cursive")
|
||||||
.title("Cursive")
|
.button("Foo", |_s| ())
|
||||||
.button("Foo", |_s| ())
|
.button("Quit", |s| s.quit())
|
||||||
.button("Quit", |s| s.quit()),
|
.wrap_with(CircularFocus::new)
|
||||||
),
|
.wrap_tab(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Starts the event loop.
|
// Starts the event loop.
|
||||||
|
42
examples/src/bin/focus.rs
Normal file
42
examples/src/bin/focus.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use cursive::traits::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut siv = cursive::default();
|
||||||
|
|
||||||
|
siv.add_layer(
|
||||||
|
cursive::views::Dialog::new().content(
|
||||||
|
cursive::views::LinearLayout::vertical()
|
||||||
|
.child(
|
||||||
|
cursive::views::TextView::new("Focused").with_name("text"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
cursive::views::EditView::new()
|
||||||
|
.wrap_with(cursive::views::FocusTracker::new)
|
||||||
|
.on_focus(|_| {
|
||||||
|
cursive::event::EventResult::with_cb(|s| {
|
||||||
|
s.call_on_name(
|
||||||
|
"text",
|
||||||
|
|v: &mut cursive::views::TextView| {
|
||||||
|
v.set_content("Focused");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_focus_lost(|_| {
|
||||||
|
cursive::event::EventResult::with_cb(|s| {
|
||||||
|
s.call_on_name(
|
||||||
|
"text",
|
||||||
|
|v: &mut cursive::views::TextView| {
|
||||||
|
v.set_content("Focus lost");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(cursive::views::Button::new("Quit", |s| s.quit()))
|
||||||
|
.fixed_width(20),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
siv.run();
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
mod game;
|
mod game;
|
||||||
|
|
||||||
use cursive::direction::Direction;
|
use cursive::{
|
||||||
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
|
direction::Direction,
|
||||||
use cursive::theme::{BaseColor, Color, ColorStyle};
|
event::{Event, EventResult, MouseButton, MouseEvent},
|
||||||
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};
|
theme::{BaseColor, Color, ColorStyle},
|
||||||
use cursive::Cursive;
|
view::CannotFocus,
|
||||||
use cursive::Printer;
|
views::{Button, Dialog, LinearLayout, Panel, SelectView},
|
||||||
use cursive::Vec2;
|
Cursive, Printer, Vec2,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut siv = cursive::default();
|
let mut siv = cursive::default();
|
||||||
@ -211,8 +212,11 @@ impl cursive::view::View for BoardView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(
|
||||||
true
|
&mut self,
|
||||||
|
_: Direction,
|
||||||
|
) -> Result<EventResult, CannotFocus> {
|
||||||
|
Ok(EventResult::Consumed(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
Loading…
Reference in New Issue
Block a user