mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 09:25:01 +00:00
Add FixedLayout
This commit is contained in:
parent
28cd51c265
commit
bd6386fd74
@ -154,6 +154,14 @@ impl Direction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the direction opposite `self`.
|
||||||
|
pub fn opposite(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Direction::Abs(abs) => Direction::Abs(abs.opposite()),
|
||||||
|
Direction::Rel(rel) => Direction::Rel(rel.swap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Shortcut to create `Direction::Rel(Relative::Back)`
|
/// Shortcut to create `Direction::Rel(Relative::Back)`
|
||||||
pub fn back() -> Self {
|
pub fn back() -> Self {
|
||||||
Direction::Rel(Relative::Back)
|
Direction::Rel(Relative::Back)
|
||||||
@ -191,7 +199,7 @@ impl Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Direction relative to an orientation.
|
/// Direction relative to an orientation.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Relative {
|
pub enum Relative {
|
||||||
// TODO: handle right-to-left? (Arabic, ...)
|
// TODO: handle right-to-left? (Arabic, ...)
|
||||||
/// Front relative direction.
|
/// Front relative direction.
|
||||||
@ -217,6 +225,39 @@ impl Relative {
|
|||||||
(Orientation::Vertical, Relative::Back) => Absolute::Down,
|
(Orientation::Vertical, Relative::Back) => Absolute::Down,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Picks one of the two values in a tuple.
|
||||||
|
///
|
||||||
|
/// First one is `self` is `Front`, second one if `self` is `Back`.
|
||||||
|
pub fn pick<T>(self, (front, back): (T, T)) -> T {
|
||||||
|
match self {
|
||||||
|
Relative::Front => front,
|
||||||
|
Relative::Back => back,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the other relative direction.
|
||||||
|
pub fn swap(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Relative::Front => Relative::Back,
|
||||||
|
Relative::Back => Relative::Front,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the relative position of `a` to `b`.
|
||||||
|
///
|
||||||
|
/// If `a < b`, it would be `Front`.
|
||||||
|
/// If `a > b`, it would be `Back`.
|
||||||
|
/// If `a == b`, returns `None`.
|
||||||
|
pub fn a_to_b(a: usize, b: usize) -> Option<Self> {
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
match a.cmp(&b) {
|
||||||
|
Ordering::Less => Some(Relative::Front),
|
||||||
|
Ordering::Greater => Some(Relative::Back),
|
||||||
|
Ordering::Equal => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Absolute direction (up, down, left, right).
|
/// Absolute direction (up, down, left, right).
|
||||||
@ -251,4 +292,29 @@ impl Absolute {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the direction opposite `self`.
|
||||||
|
pub fn opposite(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Absolute::Left => Absolute::Right,
|
||||||
|
Absolute::Right => Absolute::Left,
|
||||||
|
Absolute::Up => Absolute::Down,
|
||||||
|
Absolute::Down => Absolute::Up,
|
||||||
|
Absolute::None => Absolute::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splits this absolute direction into an orientation and relative direction.
|
||||||
|
///
|
||||||
|
/// For example, `Right` will give `(Horizontal, Back)`.
|
||||||
|
pub fn split(self) -> (Orientation, Relative) {
|
||||||
|
match self {
|
||||||
|
Absolute::Left => (Orientation::Horizontal, Relative::Front),
|
||||||
|
Absolute::Right => (Orientation::Horizontal, Relative::Back),
|
||||||
|
Absolute::Up => (Orientation::Vertical, Relative::Front),
|
||||||
|
Absolute::Down => (Orientation::Vertical, Relative::Back),
|
||||||
|
// TODO: Remove `Absolute::None`
|
||||||
|
Absolute::None => panic!("None direction not supported here"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,6 +522,8 @@ pub enum Event {
|
|||||||
|
|
||||||
// Having a doc-hidden event prevents people from having exhaustive
|
// Having a doc-hidden event prevents people from having exhaustive
|
||||||
// matches, allowing us to add events in the future.
|
// matches, allowing us to add events in the future.
|
||||||
|
//
|
||||||
|
// In addition we may not want people to listen to the exit event?
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// The application is about to exit.
|
/// The application is about to exit.
|
||||||
Exit,
|
Exit,
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
use crate::backend::Backend;
|
use crate::backend::Backend;
|
||||||
use crate::direction::Orientation;
|
use crate::direction::Orientation;
|
||||||
|
use crate::rect::Rect;
|
||||||
use crate::theme::{
|
use crate::theme::{
|
||||||
BorderStyle, ColorStyle, Effect, PaletteColor, Style, Theme,
|
BorderStyle, ColorStyle, Effect, PaletteColor, Style, Theme,
|
||||||
};
|
};
|
||||||
use crate::utils::lines::simple::{prefix, suffix};
|
use crate::utils::lines::simple::{prefix, suffix};
|
||||||
use crate::with::With;
|
use crate::with::With;
|
||||||
use crate::Vec2;
|
use crate::Vec2;
|
||||||
|
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
@ -541,6 +543,13 @@ impl<'a, 'b> Printer<'a, 'b> {
|
|||||||
self.clone().with(|s| s.enabled &= enabled)
|
self.clone().with(|s| s.enabled &= enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new sub-printer for the given viewport.
|
||||||
|
///
|
||||||
|
/// This is a combination of offset + cropped.
|
||||||
|
pub fn windowed(&self, viewport: Rect) -> Self {
|
||||||
|
self.offset(viewport.top_left()).cropped(viewport.size())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a new sub-printer with a cropped area.
|
/// Returns a new sub-printer with a cropped area.
|
||||||
///
|
///
|
||||||
/// The new printer size will be the minimum of `size` and its current size.
|
/// The new printer size will be the minimum of `size` and its current size.
|
||||||
@ -601,6 +610,9 @@ impl<'a, 'b> Printer<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new sub-printer with a content offset.
|
/// Returns a new sub-printer with a content offset.
|
||||||
|
///
|
||||||
|
/// This is useful for parent views that only show a subset of their
|
||||||
|
/// child, like `ScrollView`.
|
||||||
pub fn content_offset<S>(&self, offset: S) -> Self
|
pub fn content_offset<S>(&self, offset: S) -> Self
|
||||||
where
|
where
|
||||||
S: Into<Vec2>,
|
S: Into<Vec2>,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Rectangles on the 2D character grid.
|
//! Rectangles on the 2D character grid.
|
||||||
|
use crate::direction::{Absolute, Orientation};
|
||||||
use crate::Vec2;
|
use crate::Vec2;
|
||||||
|
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
/// A non-empty rectangle on the 2D grid.
|
/// A non-empty rectangle on the 2D grid.
|
||||||
@ -7,6 +9,7 @@ use std::ops::Add;
|
|||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
/// Top-left corner, inclusive
|
/// Top-left corner, inclusive
|
||||||
top_left: Vec2,
|
top_left: Vec2,
|
||||||
|
|
||||||
/// Bottom-right corner, inclusive
|
/// Bottom-right corner, inclusive
|
||||||
bottom_right: Vec2,
|
bottom_right: Vec2,
|
||||||
}
|
}
|
||||||
@ -90,6 +93,30 @@ impl Rect {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the start and end coordinate of one side of this rectangle.
|
||||||
|
///
|
||||||
|
/// Both start and end are inclusive.
|
||||||
|
pub fn side(self, orientation: Orientation) -> (usize, usize) {
|
||||||
|
match orientation {
|
||||||
|
Orientation::Vertical => (self.top(), self.bottom()),
|
||||||
|
Orientation::Horizontal => (self.left(), self.right()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the coordinate of the given edge.
|
||||||
|
///
|
||||||
|
/// All edges are inclusive.
|
||||||
|
pub fn edge(self, side: Absolute) -> usize {
|
||||||
|
match side {
|
||||||
|
Absolute::Left => self.left(),
|
||||||
|
Absolute::Right => self.right(),
|
||||||
|
Absolute::Up => self.top(),
|
||||||
|
Absolute::Down => self.bottom(),
|
||||||
|
// TODO: Remove `None` from `Absolute` enum
|
||||||
|
Absolute::None => panic!("None is not a valid edge."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds the given offset to this rectangle.
|
/// Adds the given offset to this rectangle.
|
||||||
pub fn offset<V>(&mut self, offset: V)
|
pub fn offset<V>(&mut self, offset: V)
|
||||||
where
|
where
|
||||||
|
@ -87,6 +87,21 @@ impl XY<usize> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checked addition with a signed vec.
|
||||||
|
///
|
||||||
|
/// Will return `None` if any coordinates exceeds bounds.
|
||||||
|
pub fn checked_add<O: Into<XY<isize>>>(&self, other: O) -> Option<Self> {
|
||||||
|
let other = other.into();
|
||||||
|
self.zip_map(other, |s, o| {
|
||||||
|
if o > 0 {
|
||||||
|
s.checked_add(o as usize)
|
||||||
|
} else {
|
||||||
|
s.checked_sub((-o) as usize)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.both()
|
||||||
|
}
|
||||||
|
|
||||||
/// Term-by-term integer division that rounds up.
|
/// Term-by-term integer division that rounds up.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -83,9 +83,7 @@ pub trait View: Any + AnyView {
|
|||||||
/// View groups should implement this to forward the call to each children.
|
/// View groups should implement this to forward the call to each children.
|
||||||
///
|
///
|
||||||
/// Default implementation is a no-op.
|
/// Default implementation is a no-op.
|
||||||
fn call_on_any<'a>(&mut self, _: &Selector<'_>, _: AnyCb<'a>) {
|
fn call_on_any<'a>(&mut self, _: &Selector<'_>, _: AnyCb<'a>) {}
|
||||||
// TODO: FnMut -> FnOnce once it works
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Moves the focus to the view identified by the given selector.
|
/// Moves the focus to the view identified by the given selector.
|
||||||
///
|
///
|
||||||
@ -99,12 +97,10 @@ pub trait View: Any + AnyView {
|
|||||||
/// This view is offered focus. Will it take it?
|
/// This view is offered focus. Will it take it?
|
||||||
///
|
///
|
||||||
/// `source` indicates where the focus comes from.
|
/// `source` indicates where the focus comes from.
|
||||||
/// When the source is unclear, `Front` is usually used.
|
/// When the source is unclear (for example mouse events),
|
||||||
|
/// `Direction::none()` can be used.
|
||||||
///
|
///
|
||||||
/// Default implementation always return `false`.
|
/// Default implementation always return `false`.
|
||||||
///
|
|
||||||
/// If the source is `Direction::Abs(Absolute::None)`, it is _recommended_
|
|
||||||
/// not to change the current focus selection.
|
|
||||||
fn take_focus(&mut self, source: Direction) -> bool {
|
fn take_focus(&mut self, source: Direction) -> bool {
|
||||||
let _ = source;
|
let _ = source;
|
||||||
false
|
false
|
||||||
@ -125,6 +121,8 @@ pub trait View: Any + AnyView {
|
|||||||
/// Returns the type of this view.
|
/// Returns the type of this view.
|
||||||
///
|
///
|
||||||
/// Useful when you have a `&dyn View`.
|
/// Useful when you have a `&dyn View`.
|
||||||
|
///
|
||||||
|
/// View implementation don't usually have to override this.
|
||||||
fn type_name(&self) -> &'static str {
|
fn type_name(&self) -> &'static str {
|
||||||
std::any::type_name::<Self>()
|
std::any::type_name::<Self>()
|
||||||
}
|
}
|
||||||
|
269
cursive-core/src/views/fixed_layout.rs
Normal file
269
cursive-core/src/views/fixed_layout.rs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
use crate::direction::{Absolute, Direction, Relative};
|
||||||
|
use crate::event::{Event, EventResult, Key};
|
||||||
|
use crate::rect::Rect;
|
||||||
|
use crate::view::IntoBoxedView;
|
||||||
|
use crate::{Printer, Vec2, View, With};
|
||||||
|
|
||||||
|
/// Arranges its children in a fixed layout.
|
||||||
|
///
|
||||||
|
/// Usually meant to use an external layout engine.
|
||||||
|
pub struct FixedLayout {
|
||||||
|
children: Vec<Child>,
|
||||||
|
focus: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Child {
|
||||||
|
view: Box<dyn View>,
|
||||||
|
position: Rect,
|
||||||
|
}
|
||||||
|
|
||||||
|
new_default!(FixedLayout);
|
||||||
|
|
||||||
|
impl FixedLayout {
|
||||||
|
/// Returns a new, empty `FixedLayout`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
FixedLayout {
|
||||||
|
children: Vec::new(),
|
||||||
|
focus: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a child. Chainable variant.
|
||||||
|
pub fn child<V: IntoBoxedView>(self, position: Rect, view: V) -> Self {
|
||||||
|
self.with(|s| s.add_child(position, view))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a child.
|
||||||
|
pub fn add_child<V: IntoBoxedView>(&mut self, position: Rect, view: V) {
|
||||||
|
self.children.push(Child {
|
||||||
|
view: view.as_boxed_view(),
|
||||||
|
position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns index of focused inner view
|
||||||
|
pub fn get_focus_index(&self) -> usize {
|
||||||
|
self.focus
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attemps to set the focus on the given child.
|
||||||
|
///
|
||||||
|
/// Returns `Err(())` if `index >= self.len()`, or if the view at the
|
||||||
|
/// given index does not accept focus.
|
||||||
|
pub fn set_focus_index(&mut self, index: usize) -> Result<(), ()> {
|
||||||
|
if self
|
||||||
|
.children
|
||||||
|
.get_mut(index)
|
||||||
|
.map(|child| child.view.take_focus(Direction::none()))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
self.focus = index;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// How many children are in this view.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.children.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this view has no children.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.children.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to a child.
|
||||||
|
pub fn get_child(&self, i: usize) -> Option<&dyn View> {
|
||||||
|
self.children.get(i).map(|c| &*c.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to a child.
|
||||||
|
pub fn get_child_mut(&mut self, i: usize) -> Option<&mut dyn View> {
|
||||||
|
self.children.get_mut(i).map(|c| &mut *c.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the position for the given child.
|
||||||
|
pub fn set_child_position(&mut self, i: usize, position: Rect) {
|
||||||
|
self.children[i].position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a child.
|
||||||
|
///
|
||||||
|
/// If `i` is within bounds, the removed child will be returned.
|
||||||
|
pub fn remove_child(&mut self, i: usize) -> Option<Box<dyn View>> {
|
||||||
|
if i >= self.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.focus > i
|
||||||
|
|| (self.focus != 0 && self.focus == self.children.len() - 1)
|
||||||
|
{
|
||||||
|
self.focus -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(self.children.remove(i).view)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter_mut<'a>(
|
||||||
|
source: Direction,
|
||||||
|
children: &'a mut [Child],
|
||||||
|
) -> Box<dyn Iterator<Item = (usize, &mut Child)> + 'a> {
|
||||||
|
let children = children.iter_mut().enumerate();
|
||||||
|
match source {
|
||||||
|
Direction::Rel(Relative::Front) => Box::new(children),
|
||||||
|
Direction::Rel(Relative::Back) => Box::new(children.rev()),
|
||||||
|
Direction::Abs(abs) => {
|
||||||
|
// Sort children by the given direction
|
||||||
|
let mut children: Vec<_> = children.collect();
|
||||||
|
children.sort_by_key(|(_, c)| c.position.edge(abs));
|
||||||
|
Box::new(children.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_focus(&mut self, target: Absolute) -> EventResult {
|
||||||
|
let source = Direction::Abs(target.opposite());
|
||||||
|
let (orientation, rel) = target.split();
|
||||||
|
|
||||||
|
fn intersects(a: (usize, usize), b: (usize, usize)) -> bool {
|
||||||
|
a.1 >= b.0 && a.0 <= b.1
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_position = self.children[self.focus].position;
|
||||||
|
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)| {
|
||||||
|
// Only select children actually aligned with us
|
||||||
|
Some(rel)
|
||||||
|
== Relative::a_to_b(current_edge, c.position.edge(target))
|
||||||
|
&& intersects(
|
||||||
|
c.position.side(orientation.swap()),
|
||||||
|
current_side,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
for (i, c) in children {
|
||||||
|
if c.view.take_focus(source) {
|
||||||
|
self.focus = i;
|
||||||
|
return EventResult::Consumed(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventResult::Ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_focus_grab(&mut self, event: &Event) {
|
||||||
|
if let Event::Mouse {
|
||||||
|
offset,
|
||||||
|
position,
|
||||||
|
event,
|
||||||
|
} = *event
|
||||||
|
{
|
||||||
|
if !event.grabs_focus() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let position = match position.checked_sub(offset) {
|
||||||
|
None => return,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for FixedLayout {
|
||||||
|
fn draw(&self, printer: &Printer) {
|
||||||
|
for child in &self.children {
|
||||||
|
child.view.draw(&printer.windowed(child.position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(&mut self, _size: Vec2) {
|
||||||
|
// TODO: re-compute children positions?
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.view.layout(child.position.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
if self.is_empty() {
|
||||||
|
return EventResult::Ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.check_focus_grab(&event);
|
||||||
|
|
||||||
|
let child = &mut self.children[self.focus];
|
||||||
|
|
||||||
|
let result = child
|
||||||
|
.view
|
||||||
|
.on_event(event.relativized(child.position.top_left()));
|
||||||
|
|
||||||
|
match result {
|
||||||
|
EventResult::Ignored => match event {
|
||||||
|
Event::Key(Key::Tab) => unimplemented!(),
|
||||||
|
Event::Key(Key::Left) => self.move_focus(Absolute::Left),
|
||||||
|
Event::Key(Key::Right) => self.move_focus(Absolute::Right),
|
||||||
|
Event::Key(Key::Up) => self.move_focus(Absolute::Up),
|
||||||
|
Event::Key(Key::Down) => self.move_focus(Absolute::Down),
|
||||||
|
_ => EventResult::Ignored,
|
||||||
|
},
|
||||||
|
res => res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn important_area(&self, size: Vec2) -> Rect {
|
||||||
|
if self.is_empty() {
|
||||||
|
return Rect::from_size((0, 0), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
let child = &self.children[self.focus];
|
||||||
|
|
||||||
|
child.view.important_area(child.position.size())
|
||||||
|
+ child.position.top_left()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn required_size(&mut self, _constraint: Vec2) -> Vec2 {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.position.bottom_left() + (1, 1))
|
||||||
|
.fold(Vec2::zero(), Vec2::max)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_focus(&mut self, source: Direction) -> bool {
|
||||||
|
// TODO: what if source = None?
|
||||||
|
match source {
|
||||||
|
Direction::Abs(Absolute::None) => {
|
||||||
|
// For now, take focus if any view is focusable.
|
||||||
|
for child in &mut self.children {
|
||||||
|
if child.view.take_focus(source) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
source => {
|
||||||
|
for (i, c) in Self::iter_mut(source, &mut self.children) {
|
||||||
|
if c.view.take_focus(source) {
|
||||||
|
self.focus = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -319,7 +319,7 @@ impl LinearLayout {
|
|||||||
.any(View::needs_relayout)
|
.any(View::needs_relayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a cyclic mutable iterator starting with the child in focus
|
/// Returns a mutable iterator starting with the child in focus
|
||||||
fn iter_mut<'a>(
|
fn iter_mut<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
from_focus: bool,
|
from_focus: bool,
|
||||||
|
@ -69,6 +69,7 @@ mod dialog;
|
|||||||
mod dummy;
|
mod dummy;
|
||||||
mod edit_view;
|
mod edit_view;
|
||||||
mod enableable_view;
|
mod enableable_view;
|
||||||
|
mod fixed_layout;
|
||||||
mod hideable_view;
|
mod hideable_view;
|
||||||
mod last_size_view;
|
mod last_size_view;
|
||||||
mod layer;
|
mod layer;
|
||||||
@ -103,6 +104,7 @@ pub use self::dialog::{Dialog, DialogFocus};
|
|||||||
pub use self::dummy::DummyView;
|
pub use self::dummy::DummyView;
|
||||||
pub use self::edit_view::EditView;
|
pub use self::edit_view::EditView;
|
||||||
pub use self::enableable_view::EnableableView;
|
pub use self::enableable_view::EnableableView;
|
||||||
|
pub use self::fixed_layout::FixedLayout;
|
||||||
pub use self::hideable_view::HideableView;
|
pub use self::hideable_view::HideableView;
|
||||||
pub use self::last_size_view::LastSizeView;
|
pub use self::last_size_view::LastSizeView;
|
||||||
pub use self::layer::Layer;
|
pub use self::layer::Layer;
|
||||||
|
@ -437,6 +437,25 @@ impl<T> XY<Option<T>> {
|
|||||||
pub fn unwrap_or(self, other: XY<T>) -> XY<T> {
|
pub fn unwrap_or(self, other: XY<T>) -> XY<T> {
|
||||||
self.zip_map(other, Option::unwrap_or)
|
self.zip_map(other, Option::unwrap_or)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new `XY` if both components are present in `self`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use cursive_core::XY;
|
||||||
|
/// assert_eq!(XY::new(Some(1), None).both(), None);
|
||||||
|
/// assert_eq!(XY::new(Some(1), Some(2)).both(), Some(XY::new(1, 2)));
|
||||||
|
/// ```
|
||||||
|
pub fn both(self) -> Option<XY<T>> {
|
||||||
|
match self {
|
||||||
|
XY {
|
||||||
|
x: Some(x),
|
||||||
|
y: Some(y),
|
||||||
|
} => Some(XY::new(x, y)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XY<bool> {
|
impl XY<bool> {
|
||||||
|
15
examples/src/bin/fixed_layout.rs
Normal file
15
examples/src/bin/fixed_layout.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
fn main() {
|
||||||
|
let mut siv = cursive::default();
|
||||||
|
|
||||||
|
siv.add_layer(
|
||||||
|
cursive::views::Dialog::around(
|
||||||
|
cursive::views::FixedLayout::new().child(
|
||||||
|
cursive::Rect::from_size((0, 0), (10, 1)),
|
||||||
|
cursive::views::TextView::new("abc"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.button("Quit", |s| s.quit()),
|
||||||
|
);
|
||||||
|
|
||||||
|
siv.run();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user