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