mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Add Direction
enum
And add a Direction argument to take_focus
This commit is contained in:
parent
bd7286a8b2
commit
91fdf96066
@ -17,6 +17,9 @@ fn main() {
|
|||||||
.child(TextView::new("Title").h_align(HAlign::Center))
|
.child(TextView::new("Title").h_align(HAlign::Center))
|
||||||
// Box the textview, so it doesn't get too wide.
|
// Box the textview, so it doesn't get too wide.
|
||||||
// A 0 height value means it will be unconstrained.
|
// A 0 height value means it will be unconstrained.
|
||||||
|
.child(BoxView::fixed_width(30, TextView::new(text)))
|
||||||
|
.child(BoxView::fixed_width(30, TextView::new(text)))
|
||||||
|
.child(BoxView::fixed_width(30, TextView::new(text)))
|
||||||
.child(BoxView::fixed_width(30, TextView::new(text))))
|
.child(BoxView::fixed_width(30, TextView::new(text))))
|
||||||
.button("Quit", |s| s.quit())
|
.button("Quit", |s| s.quit())
|
||||||
.h_align(HAlign::Center));
|
.h_align(HAlign::Center));
|
||||||
|
203
src/direction.rs
Normal file
203
src/direction.rs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
//! Direction-related structures.
|
||||||
|
//!
|
||||||
|
//! This module defines two main concepts: [Orientation] and [Direction].
|
||||||
|
//!
|
||||||
|
//! ### Orientation
|
||||||
|
//!
|
||||||
|
//! `Orientation` is a simple enum that can take two values:
|
||||||
|
//! `Horizontal` or `Vertical`.
|
||||||
|
//!
|
||||||
|
//! ### Direction
|
||||||
|
//!
|
||||||
|
//! `Direction` is a bit more complex, and can be of two kinds:
|
||||||
|
//! * Absolute direction: left, up, right, or down
|
||||||
|
//! * Relative direction: front or back.
|
||||||
|
//! Its actual direction depends on the orientation.
|
||||||
|
|
||||||
|
use vec::Vec2;
|
||||||
|
|
||||||
|
/// Describes a vertical or horizontal orientation for a view.
|
||||||
|
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||||
|
pub enum Orientation {
|
||||||
|
/// Horizontal orientation
|
||||||
|
Horizontal,
|
||||||
|
/// Vertical orientation
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Orientation {
|
||||||
|
/// Returns the component of `v` corresponding to this orientation.
|
||||||
|
///
|
||||||
|
/// (`Horizontal` will return the x value,
|
||||||
|
/// and `Vertical` will return the y value.)
|
||||||
|
pub fn get(&self, v: &Vec2) -> usize {
|
||||||
|
*v.get(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the other orientation.
|
||||||
|
pub fn swap(&self) -> Self {
|
||||||
|
match *self {
|
||||||
|
Orientation::Horizontal => Orientation::Vertical,
|
||||||
|
Orientation::Vertical => Orientation::Horizontal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the component of the given vector
|
||||||
|
/// corresponding to this orientation.
|
||||||
|
pub fn get_ref<'a, 'b>(&'a self, v: &'b mut Vec2) -> &'b mut usize {
|
||||||
|
match *self {
|
||||||
|
Orientation::Horizontal => &mut v.x,
|
||||||
|
Orientation::Vertical => &mut v.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes an iterator on sizes, and stack them in the current orientation,
|
||||||
|
/// returning the size of the required bounding box.
|
||||||
|
///
|
||||||
|
/// For an horizontal view, returns (Sum(x), Max(y)).
|
||||||
|
/// For a vertical view, returns (Max(x),Sum(y)).
|
||||||
|
pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
|
||||||
|
match *self {
|
||||||
|
Orientation::Horizontal => {
|
||||||
|
iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
|
||||||
|
}
|
||||||
|
Orientation::Vertical => {
|
||||||
|
iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `Vec2` with `value` in `self`'s axis.
|
||||||
|
pub fn make_vec(&self, value: usize) -> Vec2 {
|
||||||
|
let mut result = Vec2::zero();
|
||||||
|
*self.get_ref(&mut result) = value;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a direction, either absolute or orientation-dependent.
|
||||||
|
///
|
||||||
|
/// * Absolute directions are Up, Down, Left, and Right.
|
||||||
|
/// * Relative directions are Front and Back.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum Direction {
|
||||||
|
/// An absolute direction.
|
||||||
|
Abs(Absolute),
|
||||||
|
/// A direction relative to the current orientation.
|
||||||
|
Rel(Relative),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
/// Returns the relative direction for the given orientation.
|
||||||
|
pub fn relative(self, orientation: Orientation) -> Option<Relative> {
|
||||||
|
match self {
|
||||||
|
Direction::Abs(abs) => abs.relative(orientation),
|
||||||
|
Direction::Rel(rel) => Some(rel),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the absolute direction in the given `orientation`.
|
||||||
|
pub fn absolute(self, orientation: Orientation) -> Absolute {
|
||||||
|
match self {
|
||||||
|
Direction::Abs(abs) => abs,
|
||||||
|
Direction::Rel(rel) => rel.absolute(orientation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Rel(Relative::Back)`
|
||||||
|
pub fn back() -> Self {
|
||||||
|
Direction::Rel(Relative::Back)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Rel(Relative::Front)`
|
||||||
|
pub fn front() -> Self {
|
||||||
|
Direction::Rel(Relative::Front)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Abs(Absolute::Left)`
|
||||||
|
pub fn left() -> Self {
|
||||||
|
Direction::Abs(Absolute::Left)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Abs(Absolute::Right)`
|
||||||
|
pub fn right() -> Self {
|
||||||
|
Direction::Abs(Absolute::Right)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Abs(Absolute::Up)`
|
||||||
|
pub fn up() -> Self {
|
||||||
|
Direction::Abs(Absolute::Up)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Abs(Absolute::Down)`
|
||||||
|
pub fn down() -> Self {
|
||||||
|
Direction::Abs(Absolute::Down)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut to create `Direction::Abs(Absolute::None)`
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Direction::Abs(Absolute::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direction relative to an orientation.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum Relative {
|
||||||
|
/// Front relative direction.
|
||||||
|
///
|
||||||
|
/// * Horizontally, this means `Left`
|
||||||
|
/// * Vertically, this means `Up`
|
||||||
|
///
|
||||||
|
/// TODO: handle right-to-left?
|
||||||
|
Front,
|
||||||
|
|
||||||
|
/// Back relative direction.
|
||||||
|
///
|
||||||
|
/// * Horizontally, this means `Right`
|
||||||
|
/// * Vertically, this means `Down`.
|
||||||
|
Back,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Relative {
|
||||||
|
/// Returns the absolute direction in the given `orientation`.
|
||||||
|
pub fn absolute(self, orientation: Orientation) -> Absolute {
|
||||||
|
match (orientation, self) {
|
||||||
|
(Orientation::Horizontal, Relative::Front) => Absolute::Left,
|
||||||
|
(Orientation::Horizontal, Relative::Back) => Absolute::Right,
|
||||||
|
(Orientation::Vertical, Relative::Front) => Absolute::Up,
|
||||||
|
(Orientation::Vertical, Relative::Back) => Absolute::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Absolute direction (up, down, left, right).
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum Absolute {
|
||||||
|
/// Left
|
||||||
|
Left,
|
||||||
|
/// Up
|
||||||
|
Up,
|
||||||
|
/// Right
|
||||||
|
Right,
|
||||||
|
/// Down
|
||||||
|
Down,
|
||||||
|
|
||||||
|
/// No real direction
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Absolute {
|
||||||
|
/// Returns the relative direction for the given orientation.
|
||||||
|
///
|
||||||
|
/// Returns `None` when the direction does not apply to the given
|
||||||
|
/// orientation (ex: `Left` and `Vertical`).
|
||||||
|
pub fn relative(self, orientation: Orientation) -> Option<Relative> {
|
||||||
|
match (orientation, self) {
|
||||||
|
(Orientation::Horizontal, Absolute::Left) |
|
||||||
|
(Orientation::Vertical, Absolute::Up) => Some(Relative::Front),
|
||||||
|
(Orientation::Horizontal, Absolute::Right) |
|
||||||
|
(Orientation::Vertical, Absolute::Down) => Some(Relative::Back),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,12 +52,12 @@ pub mod vec;
|
|||||||
pub mod theme;
|
pub mod theme;
|
||||||
pub mod align;
|
pub mod align;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
|
pub mod direction;
|
||||||
|
|
||||||
// This probably doesn't need to be public?
|
// This probably doesn't need to be public?
|
||||||
mod printer;
|
mod printer;
|
||||||
mod menubar;
|
mod menubar;
|
||||||
mod xy;
|
mod xy;
|
||||||
mod orientation;
|
|
||||||
|
|
||||||
mod div;
|
mod div;
|
||||||
mod utf8;
|
mod utf8;
|
||||||
@ -65,7 +65,6 @@ mod utf8;
|
|||||||
mod backend;
|
mod backend;
|
||||||
|
|
||||||
pub use xy::XY;
|
pub use xy::XY;
|
||||||
pub use orientation::Orientation;
|
|
||||||
pub use printer::Printer;
|
pub use printer::Printer;
|
||||||
|
|
||||||
use backend::{Backend, NcursesBackend};
|
use backend::{Backend, NcursesBackend};
|
||||||
|
@ -89,7 +89,9 @@ impl MenuTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a actionnable leaf to the end of this tree - chainable variant.
|
/// Adds a actionnable leaf to the end of this tree - chainable variant.
|
||||||
pub fn leaf<F: 'static + Fn(&mut Cursive)>(self, title: &str, cb: F) -> Self {
|
pub fn leaf<F>(self, title: &str, cb: F) -> Self
|
||||||
|
where F: 'static + Fn(&mut Cursive)
|
||||||
|
{
|
||||||
self.with(|menu| menu.add_leaf(title, cb))
|
self.with(|menu| menu.add_leaf(title, cb))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
//! Define an Orientation and associated methods.
|
|
||||||
use vec::Vec2;
|
|
||||||
|
|
||||||
/// Describes a vertical or horizontal orientation for a view.
|
|
||||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
|
||||||
pub enum Orientation {
|
|
||||||
/// Horizontal orientation
|
|
||||||
Horizontal,
|
|
||||||
/// Vertical orientation
|
|
||||||
Vertical,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Orientation {
|
|
||||||
/// Returns the component of `v` corresponding to this orientation.
|
|
||||||
///
|
|
||||||
/// (`Horizontal` will return the x value,
|
|
||||||
/// and `Vertical` will return the y value.)
|
|
||||||
pub fn get(&self, v: &Vec2) -> usize {
|
|
||||||
*v.get(*self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the other orientation.
|
|
||||||
pub fn swap(&self) -> Self {
|
|
||||||
match *self {
|
|
||||||
Orientation::Horizontal => Orientation::Vertical,
|
|
||||||
Orientation::Vertical => Orientation::Horizontal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the component of the given vector
|
|
||||||
/// corresponding to this orientation.
|
|
||||||
pub fn get_ref<'a, 'b>(&'a self, v: &'b mut Vec2) -> &'b mut usize {
|
|
||||||
match *self {
|
|
||||||
Orientation::Horizontal => &mut v.x,
|
|
||||||
Orientation::Vertical => &mut v.y,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes an iterator on sizes, and stack them in the current orientation,
|
|
||||||
/// returning the size of the required bounding box.
|
|
||||||
///
|
|
||||||
/// For an horizontal view, returns (Sum(x), Max(y)).
|
|
||||||
/// For a vertical view, returns (Max(x),Sum(y)).
|
|
||||||
pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
|
|
||||||
match *self {
|
|
||||||
Orientation::Horizontal => {
|
|
||||||
iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
|
|
||||||
}
|
|
||||||
Orientation::Vertical => {
|
|
||||||
iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Vec2` with `value` in `self`'s axis.
|
|
||||||
pub fn make_vec(&self, value: usize) -> Vec2 {
|
|
||||||
let mut result = Vec2::zero();
|
|
||||||
*self.get_ref(&mut result) = value;
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
//! Points on the 2D character grid.
|
//! Points on the 2D character grid.
|
||||||
use XY;
|
use XY;
|
||||||
use orientation::Orientation;
|
use direction::Orientation;
|
||||||
|
|
||||||
use std::ops::{Add, Div, Mul, Sub};
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
use std::cmp::{Ordering, max, min};
|
use std::cmp::{Ordering, max, min};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use direction::Direction;
|
||||||
use theme::ColorStyle;
|
use theme::ColorStyle;
|
||||||
use Cursive;
|
use Cursive;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
@ -58,7 +59,7 @@ impl View for Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, _: Direction) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use std::cmp::max;
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
use Cursive;
|
use Cursive;
|
||||||
|
use direction;
|
||||||
use align::*;
|
use align::*;
|
||||||
use event::*;
|
use event::*;
|
||||||
use theme::ColorStyle;
|
use theme::ColorStyle;
|
||||||
@ -246,16 +247,34 @@ impl View for Dialog {
|
|||||||
EventResult::Ignored => {
|
EventResult::Ignored => {
|
||||||
match event {
|
match event {
|
||||||
// Up goes back to the content
|
// Up goes back to the content
|
||||||
Event::Key(Key::Up) |
|
Event::Key(Key::Up) => {
|
||||||
Event::Key(Key::Tab) |
|
if self.content
|
||||||
Event::Shift(Key::Tab) => {
|
.take_focus(direction::Direction::down()) {
|
||||||
if self.content.take_focus() {
|
|
||||||
self.focus = Focus::Content;
|
self.focus = Focus::Content;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
} else {
|
} else {
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Event::Shift(Key::Tab) => {
|
||||||
|
if self.content
|
||||||
|
.take_focus(direction::Direction::back()) {
|
||||||
|
self.focus = Focus::Content;
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
} else {
|
||||||
|
EventResult::Ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Key(Key::Tab) => {
|
||||||
|
if self.content
|
||||||
|
.take_focus(direction::Direction::front()) {
|
||||||
|
self.focus = Focus::Content;
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
} else {
|
||||||
|
EventResult::Ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// Left and Right move to other buttons
|
// Left and Right move to other buttons
|
||||||
Event::Key(Key::Right) if i + 1 <
|
Event::Key(Key::Right) if i + 1 <
|
||||||
self.buttons
|
self.buttons
|
||||||
@ -276,9 +295,10 @@ impl View for Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
||||||
// TODO: add a direction to the focus. Meanwhile, takes button first.
|
// Dialogs aren't meant to be used in layouts, so...
|
||||||
if self.content.take_focus() {
|
// Let's be super lazy and not even care about the focus source.
|
||||||
|
if self.content.take_focus(source) {
|
||||||
self.focus = Focus::Content;
|
self.focus = Focus::Content;
|
||||||
true
|
true
|
||||||
} else if !self.buttons.is_empty() {
|
} else if !self.buttons.is_empty() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
use direction::Direction;
|
||||||
use theme::{ColorStyle, Effect};
|
use theme::{ColorStyle, Effect};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{IdView, View};
|
use view::{IdView, View};
|
||||||
@ -144,7 +145,7 @@ impl View for EditView {
|
|||||||
Vec2::new(self.min_length, 1)
|
Vec2::new(self.min_length, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, _: Direction) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use view::{View, ViewWrapper};
|
use view::{View, ViewWrapper};
|
||||||
use orientation::Orientation;
|
use direction::Orientation;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
|
|
||||||
/// Simple wrapper view that asks for all the space it can get.
|
/// Simple wrapper view that asks for all the space it can get.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use XY;
|
use XY;
|
||||||
|
use direction;
|
||||||
use view::View;
|
use view::View;
|
||||||
use view::SizeCache;
|
use view::SizeCache;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use Printer;
|
use Printer;
|
||||||
use orientation::Orientation;
|
|
||||||
use event::{Event, EventResult, Key};
|
use event::{Event, EventResult, Key};
|
||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
@ -11,7 +11,7 @@ use std::cmp::min;
|
|||||||
/// Arranges its children linearly according to its orientation.
|
/// Arranges its children linearly according to its orientation.
|
||||||
pub struct LinearLayout {
|
pub struct LinearLayout {
|
||||||
children: Vec<Child>,
|
children: Vec<Child>,
|
||||||
orientation: Orientation,
|
orientation: direction::Orientation,
|
||||||
focus: usize,
|
focus: usize,
|
||||||
|
|
||||||
cache: Option<XY<SizeCache>>,
|
cache: Option<XY<SizeCache>>,
|
||||||
@ -40,7 +40,7 @@ impl Child {
|
|||||||
|
|
||||||
impl LinearLayout {
|
impl LinearLayout {
|
||||||
/// Creates a new layout with the given orientation.
|
/// Creates a new layout with the given orientation.
|
||||||
pub fn new(orientation: Orientation) -> Self {
|
pub fn new(orientation: direction::Orientation) -> Self {
|
||||||
LinearLayout {
|
LinearLayout {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
orientation: orientation,
|
orientation: orientation,
|
||||||
@ -77,12 +77,12 @@ impl LinearLayout {
|
|||||||
|
|
||||||
/// Creates a new vertical layout.
|
/// Creates a new vertical layout.
|
||||||
pub fn vertical() -> Self {
|
pub fn vertical() -> Self {
|
||||||
LinearLayout::new(Orientation::Vertical)
|
LinearLayout::new(direction::Orientation::Vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new horizontal layout.
|
/// Creates a new horizontal layout.
|
||||||
pub fn horizontal() -> Self {
|
pub fn horizontal() -> Self {
|
||||||
LinearLayout::new(Orientation::Horizontal)
|
LinearLayout::new(direction::Orientation::Horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cache can be used, return the cached size.
|
// If the cache can be used, return the cached size.
|
||||||
@ -110,15 +110,60 @@ impl LinearLayout {
|
|||||||
.any(View::needs_relayout)
|
.any(View::needs_relayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a cyclic mutable iterator starting with the child in focus
|
||||||
|
fn iter_mut<'a>(&'a mut self, from_focus: bool,
|
||||||
|
direction: direction::Relative)
|
||||||
|
-> Box<Iterator<Item = (usize, &'a mut Child)> + 'a> {
|
||||||
|
|
||||||
|
match direction {
|
||||||
|
direction::Relative::Front => {
|
||||||
|
let start = if from_focus {
|
||||||
|
self.focus
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(self.children.iter_mut().enumerate().skip(start))
|
||||||
|
}
|
||||||
|
direction::Relative::Back => {
|
||||||
|
let end = if from_focus {
|
||||||
|
self.focus + 1
|
||||||
|
} else {
|
||||||
|
self.children.len()
|
||||||
|
};
|
||||||
|
Box::new(self.children[..end].iter_mut().enumerate().rev())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_focus(&mut self, source: direction::Direction) -> EventResult {
|
||||||
|
|
||||||
|
let i = if let Some(i) = source.relative(self.orientation)
|
||||||
|
.and_then(|rel| {
|
||||||
|
// The iterator starts at the focused element.
|
||||||
|
// We don't want that one.
|
||||||
|
self.iter_mut(true, rel)
|
||||||
|
.skip(1)
|
||||||
|
.filter_map(|p| try_focus(p, source))
|
||||||
|
.next()
|
||||||
|
}) {
|
||||||
|
i
|
||||||
|
} else {
|
||||||
|
return EventResult::Ignored;
|
||||||
|
};
|
||||||
|
self.focus = i;
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
|
|
||||||
fn focus_prev(&mut self) -> EventResult {
|
fn focus_prev(&mut self) -> EventResult {
|
||||||
if let Some(i) = self.children[..self.focus]
|
if let Some(i) = self.children[..self.focus]
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.map(Child::as_mut)
|
.map(Child::as_mut)
|
||||||
.position(View::take_focus) {
|
.position(|v| v.take_focus(direction::Direction::back())) {
|
||||||
|
|
||||||
// We're looking at the list in reverse
|
// We're looking at the list in reverse
|
||||||
self.focus -= i+1;
|
self.focus -= i + 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
} else {
|
} else {
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
@ -130,7 +175,7 @@ impl LinearLayout {
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.rev()
|
.rev()
|
||||||
.map(Child::as_mut)
|
.map(Child::as_mut)
|
||||||
.position(View::take_focus) {
|
.position(|v| v.take_focus(direction::Direction::front())) {
|
||||||
// Our slice doesn't start at 0
|
// Our slice doesn't start at 0
|
||||||
self.focus += i + 1;
|
self.focus += i + 1;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
@ -140,6 +185,15 @@ impl LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_focus((i, child): (usize, &mut Child), source: direction::Direction)
|
||||||
|
-> Option<usize> {
|
||||||
|
if child.view.take_focus(source) {
|
||||||
|
Some(i)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl View for LinearLayout {
|
impl View for LinearLayout {
|
||||||
fn draw(&mut self, printer: &Printer) {
|
fn draw(&mut self, printer: &Printer) {
|
||||||
// Use pre-computed sizes
|
// Use pre-computed sizes
|
||||||
@ -282,16 +336,25 @@ impl View for LinearLayout {
|
|||||||
compromise
|
compromise
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
||||||
if let Some(i) = self.children
|
// In what order will we iterate on the children?
|
||||||
.iter_mut()
|
let rel = source.relative(self.orientation);
|
||||||
.map(Child::as_mut)
|
// We activate from_focus only if coming from the "sides".
|
||||||
.position(View::take_focus) {
|
let i = if let Some(i) = self.iter_mut(rel.is_none(),
|
||||||
self.focus = i;
|
rel.unwrap_or(direction::Relative::Front))
|
||||||
true
|
.filter_map(|p| try_focus(p, source))
|
||||||
|
.next() {
|
||||||
|
|
||||||
|
// ... we can't update `self.focus` here,
|
||||||
|
// because rustc thinks we still borrow `self`.
|
||||||
|
// :(
|
||||||
|
i
|
||||||
} else {
|
} else {
|
||||||
false
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
self.focus = i;
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
@ -299,31 +362,36 @@ impl View for LinearLayout {
|
|||||||
EventResult::Ignored => {
|
EventResult::Ignored => {
|
||||||
match event {
|
match event {
|
||||||
Event::Shift(Key::Tab) if self.focus > 0 => {
|
Event::Shift(Key::Tab) if self.focus > 0 => {
|
||||||
self.focus_prev()
|
self.move_focus(direction::Direction::back())
|
||||||
}
|
}
|
||||||
Event::Key(Key::Tab) if self.focus + 1 <
|
Event::Key(Key::Tab) if self.focus + 1 <
|
||||||
self.children.len() => {
|
self.children.len() => {
|
||||||
self.focus_next()
|
self.move_focus(direction::Direction::front())
|
||||||
}
|
}
|
||||||
Event::Key(Key::Left) if self.orientation ==
|
Event::Key(Key::Left)
|
||||||
Orientation::Horizontal &&
|
if self.orientation ==
|
||||||
self.focus > 0 => {
|
direction::Orientation::Horizontal &&
|
||||||
self.focus_prev()
|
self.focus > 0 => {
|
||||||
|
self.move_focus(direction::Direction::right())
|
||||||
}
|
}
|
||||||
Event::Key(Key::Up) if self.orientation ==
|
Event::Key(Key::Up) if self.orientation ==
|
||||||
Orientation::Vertical &&
|
direction::Orientation::Vertical &&
|
||||||
self.focus > 0 => self.focus_prev(),
|
self.focus > 0 => {
|
||||||
Event::Key(Key::Right) if self.orientation ==
|
self.move_focus(direction::Direction::down())
|
||||||
Orientation::Horizontal &&
|
|
||||||
self.focus + 1 <
|
|
||||||
self.children.len() => {
|
|
||||||
self.focus_next()
|
|
||||||
}
|
}
|
||||||
Event::Key(Key::Down) if self.orientation ==
|
Event::Key(Key::Right)
|
||||||
Orientation::Vertical &&
|
if self.orientation ==
|
||||||
self.focus + 1 <
|
direction::Orientation::Horizontal &&
|
||||||
self.children.len() => {
|
self.focus + 1 <
|
||||||
self.focus_next()
|
self.children.len() => {
|
||||||
|
self.move_focus(direction::Direction::left())
|
||||||
|
}
|
||||||
|
Event::Key(Key::Down)
|
||||||
|
if self.orientation ==
|
||||||
|
direction::Orientation::Vertical &&
|
||||||
|
self.focus + 1 <
|
||||||
|
self.children.len() => {
|
||||||
|
self.move_focus(direction::Direction::up())
|
||||||
}
|
}
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
//! * By default, `View::layout()` should be called before any call to
|
//! * By default, `View::layout()` should be called before any call to
|
||||||
//! `View::draw()` with the same available size. The only exceptions is
|
//! `View::draw()` with the same available size. The only exceptions is
|
||||||
//! when both following conditions are met:
|
//! when both following conditions are met:
|
||||||
//! * The available size has not changed since the last call to `View::layout()`
|
//! * The available size has not changed since the last call to
|
||||||
|
//! `View::layout()`
|
||||||
//! * `View::needs_relayout()` returns `false`
|
//! * `View::needs_relayout()` returns `false`
|
||||||
//!
|
//!
|
||||||
//! In this case, it is safe to omit the call to `View::layout()`.
|
//! In this case, it is safe to omit the call to `View::layout()`.
|
||||||
@ -65,6 +66,7 @@ mod tracked_view;
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
use XY;
|
use XY;
|
||||||
|
use direction::Direction;
|
||||||
use event::{Event, EventResult};
|
use event::{Event, EventResult};
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use Printer;
|
use Printer;
|
||||||
@ -135,7 +137,11 @@ pub trait View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This view is offered focus. Will it take it?
|
/// This view is offered focus. Will it take it?
|
||||||
fn take_focus(&mut self) -> bool {
|
///
|
||||||
|
/// `source` indicates where the focus comes from.
|
||||||
|
/// When the source is unclear, `Front` is usually used.
|
||||||
|
fn take_focus(&mut self, source: Direction) -> bool {
|
||||||
|
let _ = source;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,7 +184,7 @@ impl SizeCache {
|
|||||||
/// * for each dimension, `constrained = (size == req)`
|
/// * for each dimension, `constrained = (size == req)`
|
||||||
fn build(size: Vec2, req: Vec2) -> XY<Self> {
|
fn build(size: Vec2, req: Vec2) -> XY<Self> {
|
||||||
XY::new(SizeCache::new(size.x, size.x == req.x),
|
XY::new(SizeCache::new(size.x, size.x == req.x),
|
||||||
SizeCache::new(size.y, size.y == req.y))
|
SizeCache::new(size.y, size.y == req.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use std::cmp::min;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use Cursive;
|
use Cursive;
|
||||||
|
use direction::Direction;
|
||||||
use view::{IdView, View};
|
use view::{IdView, View};
|
||||||
use align::{Align, HAlign, VAlign};
|
use align::{Align, HAlign, VAlign};
|
||||||
use view::scroll::ScrollBase;
|
use view::scroll::ScrollBase;
|
||||||
@ -215,7 +216,7 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, _: Direction) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
|
use direction::Direction;
|
||||||
use backend::Backend;
|
use backend::Backend;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{Offset, Position, Selector, ShadowView, View};
|
use view::{Offset, Position, Selector, ShadowView, View};
|
||||||
@ -96,7 +97,7 @@ impl View for StackView {
|
|||||||
// We do it here instead of when adding a new layer because...?
|
// We do it here instead of when adding a new layer because...?
|
||||||
// (TODO: try to make it during layer addition)
|
// (TODO: try to make it during layer addition)
|
||||||
if layer.virgin {
|
if layer.virgin {
|
||||||
layer.view.take_focus();
|
layer.view.take_focus(Direction::none());
|
||||||
layer.virgin = false;
|
layer.virgin = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,10 +112,10 @@ impl View for StackView {
|
|||||||
.fold(Vec2::new(1, 1), Vec2::max)
|
.fold(Vec2::new(1, 1), Vec2::max)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, source: Direction) -> bool {
|
||||||
match self.layers.last_mut() {
|
match self.layers.last_mut() {
|
||||||
None => false,
|
None => false,
|
||||||
Some(mut v) => v.view.take_focus(),
|
Some(mut v) => v.view.take_focus(source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use XY;
|
use XY;
|
||||||
|
use direction::Direction;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::View;
|
use view::View;
|
||||||
use view::SizeCache;
|
use view::SizeCache;
|
||||||
@ -239,7 +240,7 @@ impl View for TextView {
|
|||||||
size.or_min((self.width.unwrap_or(0), self.rows.len()))
|
size.or_min((self.width.unwrap_or(0), self.rows.len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, _: Direction) -> bool {
|
||||||
self.scrollbase.scrollable()
|
self.scrollbase.scrollable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
|
use direction::Direction;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{Selector, View};
|
use view::{Selector, View};
|
||||||
use Printer;
|
use Printer;
|
||||||
@ -37,8 +38,8 @@ pub trait ViewWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps the `take_focus` method.
|
/// Wraps the `take_focus` method.
|
||||||
fn wrap_take_focus(&mut self) -> bool {
|
fn wrap_take_focus(&mut self, source: Direction) -> bool {
|
||||||
self.get_view_mut().take_focus()
|
self.get_view_mut().take_focus(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps the `find` method.
|
/// Wraps the `find` method.
|
||||||
@ -69,8 +70,8 @@ impl<T: ViewWrapper> View for T {
|
|||||||
self.wrap_layout(size);
|
self.wrap_layout(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self) -> bool {
|
fn take_focus(&mut self, source: Direction) -> bool {
|
||||||
self.wrap_take_focus()
|
self.wrap_take_focus(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find(&mut self, selector: &Selector) -> Option<&mut Any> {
|
fn find(&mut self, selector: &Selector) -> Option<&mut Any> {
|
||||||
|
10
src/xy.rs
10
src/xy.rs
@ -1,4 +1,4 @@
|
|||||||
use orientation::Orientation;
|
use direction::Orientation;
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
@ -46,14 +46,12 @@ impl<T> XY<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new XY by calling `f` on `self` and `other` for each axis.
|
/// Returns a new XY by calling `f` on `self` and `other` for each axis.
|
||||||
pub fn zip_map<U,V,F: Fn(T,U) -> V>(self, other: XY<U>, f: F) -> XY<V> {
|
pub fn zip_map<U, V, F: Fn(T, U) -> V>(self, other: XY<U>, f: F) -> XY<V> {
|
||||||
XY::new(f(self.x, other.x),
|
XY::new(f(self.x, other.x), f(self.y, other.y))
|
||||||
f(self.y, other.y))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <T> XY<Option<T>> {
|
impl<T> XY<Option<T>> {
|
||||||
|
|
||||||
/// Returns a new XY by calling `unwrap_or` on each axis.
|
/// Returns a new XY by calling `unwrap_or` on each axis.
|
||||||
pub fn unwrap_or(self, other: XY<T>) -> XY<T> {
|
pub fn unwrap_or(self, other: XY<T>) -> XY<T> {
|
||||||
self.zip_map(other, |s, o| s.unwrap_or(o))
|
self.zip_map(other, |s, o| s.unwrap_or(o))
|
||||||
|
Loading…
Reference in New Issue
Block a user