mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
More checks against small viewports
Prevents panics when the terminal is resized.
This commit is contained in:
parent
8dc420c3ed
commit
7d9cb03ffb
@ -343,6 +343,7 @@ impl View for EditView {
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.last_length = size.x;
|
||||
// println_stderr!("Promised: {}", size.x);
|
||||
}
|
||||
|
||||
fn take_focus(&mut self, _: Direction) -> bool {
|
||||
|
@ -26,6 +26,7 @@ struct Child {
|
||||
}
|
||||
|
||||
impl Child {
|
||||
// Compute and caches the required size.
|
||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||
self.size = self.view.required_size(req);
|
||||
self.size
|
||||
@ -36,6 +37,17 @@ impl Child {
|
||||
}
|
||||
}
|
||||
|
||||
fn cap<'a, I: Iterator<Item = &'a mut usize>>(iter: I, max: usize) {
|
||||
let mut available = max;
|
||||
for item in iter {
|
||||
if *item > available {
|
||||
*item = available;
|
||||
}
|
||||
|
||||
available -= *item;
|
||||
}
|
||||
}
|
||||
|
||||
impl LinearLayout {
|
||||
/// Creates a new layout with the given orientation.
|
||||
pub fn new(orientation: direction::Orientation) -> Self {
|
||||
@ -197,6 +209,7 @@ impl View for LinearLayout {
|
||||
let o = self.orientation;
|
||||
|
||||
for child in &mut self.children {
|
||||
// Every item has the same size orthogonal to the layout
|
||||
child.size.set_axis_from(o.swap(), &size);
|
||||
child.view.layout(size.with_axis_from(o, &child.size));
|
||||
}
|
||||
@ -225,8 +238,8 @@ impl View for LinearLayout {
|
||||
return ideal;
|
||||
}
|
||||
|
||||
// Ok, so maybe it didn't.
|
||||
// Budget cuts, everyone.
|
||||
// Ok, so maybe it didn't. Budget cuts, everyone.
|
||||
// Let's pretend we have almost no space in this direction.
|
||||
let budget_req = req.with_axis(self.orientation, 1);
|
||||
// println_stderr!("Budget req: {:?}", budget_req);
|
||||
|
||||
@ -240,8 +253,15 @@ impl View for LinearLayout {
|
||||
// println_stderr!("Desperate: {:?}", desperate);
|
||||
|
||||
// This is the lowest we'll ever go. It better fit at least.
|
||||
let orientation = self.orientation;
|
||||
if !desperate.fits_in(req) {
|
||||
// Just give up...
|
||||
// TODO: hard-cut
|
||||
cap(self.children
|
||||
.iter_mut()
|
||||
.map(|c| c.size.get_mut(orientation)),
|
||||
*req.get(self.orientation));
|
||||
|
||||
// TODO: print some error message or something
|
||||
// println_stderr!("Seriously? {:?} > {:?}???", desperate, req);
|
||||
// self.cache = Some(SizeCache::build(desperate, req));
|
||||
|
@ -6,6 +6,8 @@ use event::{Callback, Event, EventResult, Key};
|
||||
|
||||
use std::any::Any;
|
||||
use std::rc::Rc;
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use vec::Vec2;
|
||||
use view::ScrollBase;
|
||||
use view::Selector;
|
||||
@ -132,16 +134,16 @@ impl ListView {
|
||||
fn move_focus(&mut self, n: usize, source: direction::Direction)
|
||||
-> EventResult {
|
||||
let i = if let Some(i) =
|
||||
source.relative(direction::Orientation::Vertical)
|
||||
.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))
|
||||
.take(n)
|
||||
.last()
|
||||
}) {
|
||||
source.relative(direction::Orientation::Vertical)
|
||||
.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))
|
||||
.take(n)
|
||||
.last()
|
||||
}) {
|
||||
i
|
||||
} else {
|
||||
return EventResult::Ignored;
|
||||
@ -181,28 +183,30 @@ impl View for ListView {
|
||||
let offset = self.children
|
||||
.iter()
|
||||
.map(Child::label)
|
||||
.map(str::len)
|
||||
.map(UnicodeWidthStr::width)
|
||||
.max()
|
||||
.unwrap_or(0) + 1;
|
||||
|
||||
self.scrollbase.draw(printer, |printer, i| {
|
||||
match self.children[i] {
|
||||
Child::Row(ref label, ref view) => {
|
||||
printer.print((0, 0), label);
|
||||
view.draw(&printer.offset((offset, 0), i == self.focus));
|
||||
}
|
||||
Child::Delimiter => (),
|
||||
// println_stderr!("Offset: {}", offset);
|
||||
|
||||
self.scrollbase.draw(printer, |printer, i| match self.children[i] {
|
||||
Child::Row(ref label, ref view) => {
|
||||
printer.print((0, 0), label);
|
||||
view.draw(&printer.offset((offset, 0), i == self.focus));
|
||||
}
|
||||
Child::Delimiter => (),
|
||||
});
|
||||
}
|
||||
|
||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||
let label_size = self.children
|
||||
// We'll show 2 columns: the labels, and the views.
|
||||
let label_width = self.children
|
||||
.iter()
|
||||
.map(Child::label)
|
||||
.map(str::len)
|
||||
.map(UnicodeWidthStr::width)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
let view_size = self.children
|
||||
.iter_mut()
|
||||
.filter_map(Child::view)
|
||||
@ -211,26 +215,34 @@ impl View for ListView {
|
||||
.unwrap_or(0);
|
||||
|
||||
if self.children.len() > req.y {
|
||||
Vec2::new(label_size + 1 + view_size + 2, req.y)
|
||||
Vec2::new(label_width + 1 + view_size + 2, req.y)
|
||||
} else {
|
||||
Vec2::new(label_size + 1 + view_size, self.children.len())
|
||||
Vec2::new(label_width + 1 + view_size, self.children.len())
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.scrollbase.set_heights(size.y, self.children.len());
|
||||
|
||||
let label_size = self.children
|
||||
// We'll show 2 columns: the labels, and the views.
|
||||
let label_width = self.children
|
||||
.iter()
|
||||
.map(Child::label)
|
||||
.map(str::len)
|
||||
.map(UnicodeWidthStr::width)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let mut available = size.x - label_size - 1;
|
||||
|
||||
if self.children.len() > size.y {
|
||||
available -= 2;
|
||||
}
|
||||
let spacing = 1;
|
||||
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
|
||||
|
||||
let available = if label_width + spacing + scrollbar_width > size.x {
|
||||
// We have no space for the kids! :(
|
||||
0
|
||||
} else {
|
||||
size.x - label_width - spacing - scrollbar_width
|
||||
};
|
||||
|
||||
// println_stderr!("Available: {}", available);
|
||||
|
||||
for child in self.children.iter_mut().filter_map(Child::view) {
|
||||
child.layout(Vec2::new(available, 1));
|
||||
|
@ -340,6 +340,9 @@ impl<T: 'static> View for SelectView<T> {
|
||||
ColorStyle::Highlight
|
||||
};
|
||||
let x = printer.size.x;
|
||||
if x == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
printer.with_color(style, |printer| {
|
||||
|
10
src/xy.rs
10
src/xy.rs
@ -45,6 +45,14 @@ impl<T> XY<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value on the given axis.
|
||||
pub fn get_mut(&mut self, o: Orientation) -> &mut T {
|
||||
match o {
|
||||
Orientation::Horizontal => &mut self.x,
|
||||
Orientation::Vertical => &mut self.y,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `XY` of tuples made by zipping `self` and `other`.
|
||||
pub fn zip<U>(self, other: XY<U>) -> XY<(T, U)> {
|
||||
XY::new((self.x, other.x), (self.y, other.y))
|
||||
@ -56,7 +64,7 @@ impl<T> XY<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Clone> XY<T> {
|
||||
impl<T: Clone> XY<T> {
|
||||
/// Returns a new `XY` with the axis `o` set to `value`.
|
||||
pub fn with_axis(&self, o: Orientation, value: T) -> Self {
|
||||
let mut new = self.clone();
|
||||
|
Loading…
Reference in New Issue
Block a user