Don't crash on insufficient space

This commit is contained in:
Alexandre Bury 2016-07-16 22:05:28 -07:00
parent ab3a55abaf
commit 8750185f8c
7 changed files with 103 additions and 23 deletions

View File

@ -67,6 +67,9 @@ impl HAlign {
/// `container`, printing at the resulting offset will align the view as /// `container`, printing at the resulting offset will align the view as
/// desired. /// desired.
pub fn get_offset(&self, content: usize, container: usize) -> usize { pub fn get_offset(&self, content: usize, container: usize) -> usize {
if container < content {
0
} else {
match *self { match *self {
HAlign::Left => 0, HAlign::Left => 0,
HAlign::Center => (container - content) / 2, HAlign::Center => (container - content) / 2,
@ -74,6 +77,7 @@ impl HAlign {
} }
} }
} }
}
impl VAlign { impl VAlign {
/// Returns the offset required to position a view. /// Returns the offset required to position a view.
@ -82,6 +86,9 @@ impl VAlign {
/// `container`, printing at the resulting offset will align the view as /// `container`, printing at the resulting offset will align the view as
/// desired. /// desired.
pub fn get_offset(&self, content: usize, container: usize) -> usize { pub fn get_offset(&self, content: usize, container: usize) -> usize {
if container < content {
0
} else {
match *self { match *self {
VAlign::Top => 0, VAlign::Top => 0,
VAlign::Center => (container - content) / 2, VAlign::Center => (container - content) / 2,
@ -89,3 +96,4 @@ impl VAlign {
} }
} }
} }
}

View File

@ -127,11 +127,19 @@ impl View for Dialog {
.fold(0, |a, b| a + b) + self.buttons.len() - 1 .fold(0, |a, b| a + b) + self.buttons.len() - 1
}; };
let overhead = self.padding + self.borders; let overhead = self.padding + self.borders;
if printer.size.x < overhead.horizontal() {
return;
}
let mut offset = overhead.left + let mut offset = overhead.left +
self.align self.align
.h .h
.get_offset(width, printer.size.x - overhead.horizontal()); .get_offset(width, printer.size.x - overhead.horizontal());
let y = printer.size.y - self.padding.bottom - self.borders.bottom - 1;
let overhead_bottom = self.padding.bottom + self.borders.bottom + 1;
if overhead_bottom > printer.size.y {
return;
}
let y = printer.size.y - overhead_bottom;
for (i, button) in self.buttons.iter().enumerate() { for (i, button) in self.buttons.iter().enumerate() {
let size = button.size; let size = button.size;
@ -146,9 +154,13 @@ impl View for Dialog {
} }
// What do we have left? // What do we have left?
let inner_size = printer.size - Vec2::new(0, buttons_height) - let taken = Vec2::new(0, buttons_height) +
self.borders.combined() - self.borders.combined() +
self.padding.combined(); self.padding.combined();
if !taken.fits_in(printer.size) {
return;
}
let inner_size = printer.size - taken;
self.content self.content
.draw(&printer.sub_printer(self.borders.top_left() + .draw(&printer.sub_printer(self.borders.top_left() +
@ -186,7 +198,13 @@ impl View for Dialog {
} }
// We also remove one row for the buttons. // We also remove one row for the buttons.
let content_req = req - (nomans_land + Vec2::new(0, buttons_size.y)); let taken = nomans_land + Vec2::new(0, buttons_size.y);
if !taken.fits_in(req) {
// Bad!!
return taken;
}
let content_req = req - taken;
let content_size = self.content.get_min_size(content_req); let content_size = self.content.get_min_size(content_req);
// On the Y axis, we add buttons and content. // On the Y axis, we add buttons and content.
@ -207,7 +225,12 @@ impl View for Dialog {
fn layout(&mut self, mut size: Vec2) { fn layout(&mut self, mut size: Vec2) {
// Padding and borders are taken, sorry. // Padding and borders are taken, sorry.
// TODO: handle border-less themes? // TODO: handle border-less themes?
size = size - (self.borders.combined() + self.padding.combined()); let taken = self.borders.combined() + self.padding.combined();
size = if taken.fits_in(size) {
size - taken
} else {
Vec2::zero()
};
// Buttons are kings, we give them everything they want. // Buttons are kings, we give them everything they want.
let mut buttons_height = 0; let mut buttons_height = 0;
@ -218,6 +241,9 @@ impl View for Dialog {
} }
// Poor content will have to make do with what's left. // Poor content will have to make do with what's left.
if buttons_height > size.y {
buttons_height = size.y;
}
self.content.layout(size - Vec2::new(0, buttons_height)); self.content.layout(size - Vec2::new(0, buttons_height));
} }

View File

@ -239,11 +239,13 @@ impl View for LinearLayout {
// Just give up... // Just give up...
// 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));
self.cache = None;
return desperate; return desperate;
} }
// This here is how much we're generously offered // This here is how much we're generously offered
// (We just checked that req >= desperate, so the substraction is safe
let mut available = self.orientation.get(&(req - desperate)); let mut available = self.orientation.get(&(req - desperate));
// println_stderr!("Available: {:?}", available); // println_stderr!("Available: {:?}", available);
@ -252,7 +254,11 @@ impl View for LinearLayout {
let mut overweight: Vec<(usize, usize)> = sizes.iter() let mut overweight: Vec<(usize, usize)> = sizes.iter()
.map(|v| self.orientation.get(v)) .map(|v| self.orientation.get(v))
.zip(min_sizes.iter().map(|v| self.orientation.get(v))) .zip(min_sizes.iter().map(|v| self.orientation.get(v)))
.map(|(a, b)| a - b) .map(|(a, b)| if a > b {
a - b
} else {
0
})
.enumerate() .enumerate()
.collect(); .collect();
// println_stderr!("Overweight: {:?}", overweight); // println_stderr!("Overweight: {:?}", overweight);

View File

@ -54,10 +54,16 @@ impl Offset {
/// Computes a single-dimension offset requred to draw a view. /// Computes a single-dimension offset requred to draw a view.
pub fn compute_offset(&self, size: usize, available: usize, parent: usize) pub fn compute_offset(&self, size: usize, available: usize, parent: usize)
-> usize { -> usize {
if size > available {
0
} else {
match *self { match *self {
Offset::Center => (available - size) / 2, Offset::Center => (available - size) / 2,
Offset::Absolute(offset) => min(offset, available - size), Offset::Absolute(offset) => min(offset, available - size),
Offset::Parent(offset) => min(parent + offset, available - size), Offset::Parent(offset) => {
min(parent + offset, available - size)
}
}
} }
} }
} }

View File

@ -130,10 +130,14 @@ impl ScrollBase {
pub fn draw<F>(&self, printer: &Printer, line_drawer: F) pub fn draw<F>(&self, printer: &Printer, line_drawer: F)
where F: Fn(&Printer, usize) where F: Fn(&Printer, usize)
{ {
if self.view_height == 0 {
return;
}
// Print the content in a sub_printer // Print the content in a sub_printer
let max_y = min(self.view_height, let max_y = min(self.view_height,
self.content_height - self.start_line); self.content_height - self.start_line);
let w = if self.scrollable() { let w = if self.scrollable() {
if printer.size.x < 2 { return; }
printer.size.x - 2 + self.scrollbar_padding // TODO: 2 printer.size.x - 2 + self.scrollbar_padding // TODO: 2
} else { } else {
printer.size.x printer.size.x

View File

@ -22,8 +22,9 @@ impl<T: View> ShadowView<T> {
} }
} }
fn padding(&self) -> (usize, usize) { fn padding(&self) -> Vec2 {
(1 + self.left_padding as usize, 1 + self.top_padding as usize) Vec2::new(1 + self.left_padding as usize,
1 + self.top_padding as usize)
} }
/// If set, adds an empty column to the left of the view. /// If set, adds an empty column to the left of the view.
@ -47,17 +48,23 @@ impl<T: View> ViewWrapper for ShadowView<T> {
wrap_impl!(&self.view); wrap_impl!(&self.view);
fn wrap_get_min_size(&mut self, req: Vec2) -> Vec2 { fn wrap_get_min_size(&mut self, req: Vec2) -> Vec2 {
let offset = self.padding(); // Make sure req >= offset
let offset = self.padding().or_min(req);
self.view.get_min_size(req - offset) + offset self.view.get_min_size(req - offset) + offset
} }
fn wrap_layout(&mut self, size: Vec2) { fn wrap_layout(&mut self, size: Vec2) {
let offset = self.padding(); let offset = self.padding().or_min(size);
self.view.layout(size - offset); self.view.layout(size - offset);
} }
fn wrap_draw(&self, printer: &Printer) { fn wrap_draw(&self, printer: &Printer) {
if printer.size.y == 0 || printer.size.x == 0 {
// Nothing to do if there's no place to draw.
return;
}
// Skip the first row/column // Skip the first row/column
let printer = let printer =
&printer.sub_printer(Vec2::new(self.left_padding as usize, &printer.sub_printer(Vec2::new(self.left_padding as usize,

View File

@ -120,15 +120,30 @@ impl TextView {
fn compute_rows(&mut self, size: Vec2) { fn compute_rows(&mut self, size: Vec2) {
if !self.is_cache_valid(size) { if !self.is_cache_valid(size) {
self.last_size = None;
// Recompute // Recompute
if size.x == 0 {
// Nothing we can do at this poing.
return;
}
self.rows = LinesIterator::new(&self.content, size.x).collect(); self.rows = LinesIterator::new(&self.content, size.x).collect();
let mut scrollbar = 0; let mut scrollbar = 0;
if self.scrollable && self.rows.len() > size.y { if self.scrollable && self.rows.len() > size.y {
scrollbar = 2; scrollbar = 2;
if size.x < scrollbar {
// Again, this is a lost cause.
return;
}
// If we're too high, include a scrollbar // If we're too high, include a scrollbar
self.rows = LinesIterator::new(&self.content, self.rows = LinesIterator::new(&self.content,
size.x - scrollbar) size.x - scrollbar)
.collect(); .collect();
if self.rows.is_empty() && !self.content.is_empty() {
return;
}
} }
// Desired width, including the scrollbar. // Desired width, including the scrollbar.
@ -148,6 +163,8 @@ impl TextView {
my_size.y = size.y; my_size.y = size.y;
} }
// println_stderr!("my: {:?} | si: {:?}", my_size, size);
self.last_size = Some(SizeCache::build(my_size, size)); self.last_size = Some(SizeCache::build(my_size, size));
} }
} }
@ -214,6 +231,12 @@ impl<'a> Iterator for LinesIterator<'a> {
} }
}; };
if head_bytes == 0 {
// This mean we can't even get a single char?
// Sucks. Let's bail.
return None;
}
self.start += head_bytes; self.start += head_bytes;
Some(Row { Some(Row {