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) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
self.last_length = size.x;
|
self.last_length = size.x;
|
||||||
|
// println_stderr!("Promised: {}", size.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, _: Direction) -> bool {
|
fn take_focus(&mut self, _: Direction) -> bool {
|
||||||
|
@ -26,6 +26,7 @@ struct Child {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Child {
|
impl Child {
|
||||||
|
// Compute and caches the required size.
|
||||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||||
self.size = self.view.required_size(req);
|
self.size = self.view.required_size(req);
|
||||||
self.size
|
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 {
|
impl LinearLayout {
|
||||||
/// Creates a new layout with the given orientation.
|
/// Creates a new layout with the given orientation.
|
||||||
pub fn new(orientation: direction::Orientation) -> Self {
|
pub fn new(orientation: direction::Orientation) -> Self {
|
||||||
@ -197,6 +209,7 @@ impl View for LinearLayout {
|
|||||||
let o = self.orientation;
|
let o = self.orientation;
|
||||||
|
|
||||||
for child in &mut self.children {
|
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.size.set_axis_from(o.swap(), &size);
|
||||||
child.view.layout(size.with_axis_from(o, &child.size));
|
child.view.layout(size.with_axis_from(o, &child.size));
|
||||||
}
|
}
|
||||||
@ -225,8 +238,8 @@ impl View for LinearLayout {
|
|||||||
return ideal;
|
return ideal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, so maybe it didn't.
|
// Ok, so maybe it didn't. Budget cuts, everyone.
|
||||||
// Budget cuts, everyone.
|
// Let's pretend we have almost no space in this direction.
|
||||||
let budget_req = req.with_axis(self.orientation, 1);
|
let budget_req = req.with_axis(self.orientation, 1);
|
||||||
// println_stderr!("Budget req: {:?}", budget_req);
|
// println_stderr!("Budget req: {:?}", budget_req);
|
||||||
|
|
||||||
@ -240,8 +253,15 @@ impl View for LinearLayout {
|
|||||||
// println_stderr!("Desperate: {:?}", desperate);
|
// println_stderr!("Desperate: {:?}", desperate);
|
||||||
|
|
||||||
// This is the lowest we'll ever go. It better fit at least.
|
// This is the lowest we'll ever go. It better fit at least.
|
||||||
|
let orientation = self.orientation;
|
||||||
if !desperate.fits_in(req) {
|
if !desperate.fits_in(req) {
|
||||||
// Just give up...
|
// 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
|
// TODO: print some error message or something
|
||||||
// println_stderr!("Seriously? {:?} > {:?}???", desperate, req);
|
// println_stderr!("Seriously? {:?} > {:?}???", desperate, req);
|
||||||
// self.cache = Some(SizeCache::build(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::any::Any;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::ScrollBase;
|
use view::ScrollBase;
|
||||||
use view::Selector;
|
use view::Selector;
|
||||||
@ -132,16 +134,16 @@ impl ListView {
|
|||||||
fn move_focus(&mut self, n: usize, source: direction::Direction)
|
fn move_focus(&mut self, n: usize, source: direction::Direction)
|
||||||
-> EventResult {
|
-> EventResult {
|
||||||
let i = if let Some(i) =
|
let i = if let Some(i) =
|
||||||
source.relative(direction::Orientation::Vertical)
|
source.relative(direction::Orientation::Vertical)
|
||||||
.and_then(|rel| {
|
.and_then(|rel| {
|
||||||
// The iterator starts at the focused element.
|
// The iterator starts at the focused element.
|
||||||
// We don't want that one.
|
// We don't want that one.
|
||||||
self.iter_mut(true, rel)
|
self.iter_mut(true, rel)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.filter_map(|p| try_focus(p, source))
|
.filter_map(|p| try_focus(p, source))
|
||||||
.take(n)
|
.take(n)
|
||||||
.last()
|
.last()
|
||||||
}) {
|
}) {
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
@ -181,28 +183,30 @@ impl View for ListView {
|
|||||||
let offset = self.children
|
let offset = self.children
|
||||||
.iter()
|
.iter()
|
||||||
.map(Child::label)
|
.map(Child::label)
|
||||||
.map(str::len)
|
.map(UnicodeWidthStr::width)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0) + 1;
|
.unwrap_or(0) + 1;
|
||||||
|
|
||||||
self.scrollbase.draw(printer, |printer, i| {
|
// println_stderr!("Offset: {}", offset);
|
||||||
match self.children[i] {
|
|
||||||
Child::Row(ref label, ref view) => {
|
self.scrollbase.draw(printer, |printer, i| match self.children[i] {
|
||||||
printer.print((0, 0), label);
|
Child::Row(ref label, ref view) => {
|
||||||
view.draw(&printer.offset((offset, 0), i == self.focus));
|
printer.print((0, 0), label);
|
||||||
}
|
view.draw(&printer.offset((offset, 0), i == self.focus));
|
||||||
Child::Delimiter => (),
|
|
||||||
}
|
}
|
||||||
|
Child::Delimiter => (),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
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()
|
.iter()
|
||||||
.map(Child::label)
|
.map(Child::label)
|
||||||
.map(str::len)
|
.map(UnicodeWidthStr::width)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let view_size = self.children
|
let view_size = self.children
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.filter_map(Child::view)
|
.filter_map(Child::view)
|
||||||
@ -211,26 +215,34 @@ impl View for ListView {
|
|||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
if self.children.len() > req.y {
|
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 {
|
} 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) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
self.scrollbase.set_heights(size.y, self.children.len());
|
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()
|
.iter()
|
||||||
.map(Child::label)
|
.map(Child::label)
|
||||||
.map(str::len)
|
.map(UnicodeWidthStr::width)
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let mut available = size.x - label_size - 1;
|
|
||||||
|
|
||||||
if self.children.len() > size.y {
|
let spacing = 1;
|
||||||
available -= 2;
|
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) {
|
for child in self.children.iter_mut().filter_map(Child::view) {
|
||||||
child.layout(Vec2::new(available, 1));
|
child.layout(Vec2::new(available, 1));
|
||||||
|
@ -340,6 +340,9 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
ColorStyle::Highlight
|
ColorStyle::Highlight
|
||||||
};
|
};
|
||||||
let x = printer.size.x;
|
let x = printer.size.x;
|
||||||
|
if x == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
printer.with_color(style, |printer| {
|
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`.
|
/// Returns a new `XY` of tuples made by zipping `self` and `other`.
|
||||||
pub fn zip<U>(self, other: XY<U>) -> XY<(T, U)> {
|
pub fn zip<U>(self, other: XY<U>) -> XY<(T, U)> {
|
||||||
XY::new((self.x, other.x), (self.y, other.y))
|
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`.
|
/// Returns a new `XY` with the axis `o` set to `value`.
|
||||||
pub fn with_axis(&self, o: Orientation, value: T) -> Self {
|
pub fn with_axis(&self, o: Orientation, value: T) -> Self {
|
||||||
let mut new = self.clone();
|
let mut new = self.clone();
|
||||||
|
Loading…
Reference in New Issue
Block a user