Use proper bounds for dialogs

Buttons height was not removed from the budget for get_min_size
This commit is contained in:
Alexandre Bury 2016-07-09 18:36:07 -07:00
parent 455a2a817c
commit 9b2dec2a7f
2 changed files with 63 additions and 85 deletions

View File

@ -24,7 +24,8 @@ enum Focus {
/// ///
/// ``` /// ```
/// # use cursive::view::{Dialog,TextView}; /// # use cursive::view::{Dialog,TextView};
/// let dialog = Dialog::new(TextView::new("Hello!")).button("Ok", |s| s.quit()); /// let dialog = Dialog::new(TextView::new("Hello!"))
/// .button("Ok", |s| s.quit());
/// ``` /// ```
pub struct Dialog { pub struct Dialog {
title: String, title: String,
@ -108,8 +109,8 @@ impl Dialog {
impl View for Dialog { impl View for Dialog {
fn draw(&mut self, printer: &Printer) { fn draw(&mut self, printer: &Printer) {
// This will be the height used by the buttons. // This will be the buttons_height used by the buttons.
let mut height = 0; let mut buttons_height = 0;
// Current horizontal position of the next button we'll draw. // Current horizontal position of the next button we'll draw.
// Sum of the sizes + len-1 for margins // Sum of the sizes + len-1 for margins
@ -123,7 +124,9 @@ impl View for Dialog {
}; };
let overhead = self.padding + self.borders; let overhead = self.padding + self.borders;
let mut offset = overhead.left + let mut offset = overhead.left +
self.align.h.get_offset(width, printer.size.x - overhead.horizontal()); self.align
.h
.get_offset(width, printer.size.x - overhead.horizontal());
let y = printer.size.y - self.padding.bottom - self.borders.bottom - 1; let y = printer.size.y - self.padding.bottom - self.borders.bottom - 1;
for (i, button) in self.buttons.iter_mut().enumerate() { for (i, button) in self.buttons.iter_mut().enumerate() {
@ -135,16 +138,19 @@ impl View for Dialog {
// Keep 1 blank between two buttons // Keep 1 blank between two buttons
offset += size.x + 1; offset += size.x + 1;
// Also keep 1 blank above the buttons // Also keep 1 blank above the buttons
height = max(height, size.y + 1); buttons_height = max(buttons_height, size.y + 1);
} }
// What do we have left? // What do we have left?
let inner_size = printer.size - Vec2::new(0, height) - self.borders.combined() - let inner_size = printer.size - Vec2::new(0, buttons_height) -
self.borders.combined() -
self.padding.combined(); self.padding.combined();
self.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(), self.content
inner_size, .draw(&printer.sub_printer(self.borders.top_left() +
self.focus == Focus::Content)); self.padding.top_left(),
inner_size,
self.focus == Focus::Content));
printer.print_box(Vec2::new(0, 0), printer.size); printer.print_box(Vec2::new(0, 0), printer.size);
@ -154,16 +160,17 @@ impl View for Dialog {
printer.print((x - 2, 0), ""); printer.print((x - 2, 0), "");
printer.print((x + len, 0), ""); printer.print((x + len, 0), "");
printer.with_color(ColorStyle::TitlePrimary, |p| p.print((x, 0), &self.title)); printer.with_color(ColorStyle::TitlePrimary,
|p| p.print((x, 0), &self.title));
} }
} }
fn get_min_size(&mut self, req: Vec2) -> Vec2 { fn get_min_size(&mut self, req: Vec2) -> Vec2 {
// Padding and borders are not available for kids. // Padding and borders are not available for kids.
let content_req = req - (self.padding.combined() + self.borders.combined()); let nomans_land = self.padding.combined() + self.borders.combined();
let content_size = self.content.get_min_size(content_req);
// Buttons are not flexible, so their size doesn't depend on ours.
let mut buttons_size = Vec2::new(0, 0); let mut buttons_size = Vec2::new(0, 0);
if !self.buttons.is_empty() { if !self.buttons.is_empty() {
buttons_size.x += self.buttons.len() - 1; buttons_size.x += self.buttons.len() - 1;
@ -174,6 +181,10 @@ impl View for Dialog {
buttons_size.y = max(buttons_size.y, s.y + 1); buttons_size.y = max(buttons_size.y, s.y + 1);
} }
// We also remove one row for the buttons.
let content_req = req - (nomans_land + Vec2::new(0, buttons_size.y));
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.
// On the X axis, we take the max. // On the X axis, we take the max.
let mut inner_size = Vec2::new(max(content_size.x, buttons_size.x), let mut inner_size = Vec2::new(max(content_size.x, buttons_size.x),
@ -245,7 +256,7 @@ impl View for Dialog {
// 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
.len() => { .len() => {
self.focus = Focus::Button(i + 1); self.focus = Focus::Button(i + 1);
EventResult::Consumed(None) EventResult::Consumed(None)
} }

View File

@ -1,5 +1,3 @@
use std::cmp::max;
use vec::Vec2; use vec::Vec2;
use view::View; use view::View;
use printer::Printer; use printer::Printer;
@ -39,24 +37,6 @@ fn strip_last_newline(content: &str) -> &str {
} }
} }
/// Returns the number of lines required to display the given text with the
/// specified maximum line width.
fn get_line_span(line: &str, max_width: usize) -> usize {
// TODO: this method is stupid. Look at LinesIterator and do the same
// (Or use a common function? Better!)
let mut lines = 1;
let mut length = 0;
for l in line.split(' ').map(|word| word.width()) {
length += l;
if length > max_width {
length = l;
lines += 1;
}
length += 1;
}
lines
}
impl TextView { impl TextView {
/// Creates a new TextView with the given content. /// Creates a new TextView with the given content.
pub fn new(content: &str) -> Self { pub fn new(content: &str) -> Self {
@ -104,36 +84,16 @@ impl TextView {
&self.content &self.content
} }
/// Returns the number of lines required to display the content
/// with the given width.
fn get_num_lines(&self, max_width: usize) -> usize {
self.content
.split('\n')
.map(|line| get_line_span(line, max_width))
.fold(0, |sum, x| sum + x)
}
// In the absence of any constraint, what size would we like?
fn get_ideal_size(&self) -> Vec2 {
let mut max_width = 0;
let mut height = 0;
for line in self.content.split('\n') {
height += 1;
max_width = max(max_width, line.width());
}
Vec2::new(max_width, height)
}
fn is_cache_valid(&self, size: Vec2) -> bool { fn is_cache_valid(&self, size: Vec2) -> bool {
match self.last_size { match self.last_size {
None => false, None => false,
Some(last) => if last.x != size.x { Some(last) => {
false if last.x != size.x {
} else { false
(last.y < self.rows.len()) == (size.y < self.rows.len()) } else {
}, (last.y < self.rows.len()) == (size.y < self.rows.len())
}
}
} }
} }
@ -145,11 +105,16 @@ impl TextView {
if self.rows.len() > size.y { if self.rows.len() > size.y {
scrollbar = 2; scrollbar = 2;
// If we're too high, include a scrollbar // If we're too high, include a scrollbar
self.rows = LinesIterator::new(&self.content, size.x - scrollbar) self.rows = LinesIterator::new(&self.content,
size.x - scrollbar)
.collect(); .collect();
} }
self.width = self.rows.iter().map(|row| row.width).max().map(|w| w + scrollbar); self.width = self.rows
.iter()
.map(|row| row.width)
.max()
.map(|w| w + scrollbar);
self.last_size = Some(size); self.last_size = Some(size);
} }
@ -208,10 +173,14 @@ impl<'a> Iterator for LinesIterator<'a> {
} }
// Keep adding indivisible tokens // Keep adding indivisible tokens
let head_bytes = match head_bytes(content.split(' '), self.width, " ") { let head_bytes =
0 => head_bytes(content.graphemes(true), self.width, ""), match head_bytes(content.split(' '), self.width, " ") {
other => { self.start += 1; other }, 0 => head_bytes(content.graphemes(true), self.width, ""),
}; other => {
self.start += 1;
other
}
};
self.start += head_bytes; self.start += head_bytes;
@ -228,9 +197,8 @@ impl View for TextView {
let h = self.rows.len(); let h = self.rows.len();
let offset = self.align.v.get_offset(h, printer.size.y); let offset = self.align.v.get_offset(h, printer.size.y);
let printer = &printer.sub_printer(Vec2::new(0, offset), let printer =
printer.size, &printer.sub_printer(Vec2::new(0, offset), printer.size, true);
true);
self.scrollbase.draw(printer, |printer, i| { self.scrollbase.draw(printer, |printer, i| {
let row = &self.rows[i]; let row = &self.rows[i];
@ -253,9 +221,7 @@ impl View for TextView {
self.scrollbase.scroll_up(1) self.scrollbase.scroll_up(1)
} }
Event::Key(Key::Down) if self.scrollbase Event::Key(Key::Down) if self.scrollbase
.can_scroll_down() => { .can_scroll_down() => self.scrollbase.scroll_down(1),
self.scrollbase.scroll_down(1)
}
Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10), Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10), Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
_ => return EventResult::Ignored, _ => return EventResult::Ignored,
@ -284,23 +250,24 @@ impl View for TextView {
} }
} }
fn head_bytes<'a, I: Iterator<Item=&'a str>>(iter: I, width: usize, overhead: &str) fn head_bytes<'a, I: Iterator<Item = &'a str>>(iter: I, width: usize,
-> usize { overhead: &str)
-> usize {
let overhead_width = overhead.width(); let overhead_width = overhead.width();
let overhead_len = overhead.len(); let overhead_len = overhead.len();
let sum = iter.scan(0, |w, token| { let sum = iter.scan(0, |w, token| {
*w += token.width(); *w += token.width();
if *w > width { if *w > width {
None None
} else { } else {
// Add a space // Add a space
*w += overhead_width; *w += overhead_width;
Some(token) Some(token)
} }
}) })
.map(|token| token.len() + overhead_len) .map(|token| token.len() + overhead_len)
.fold(0, |a, b| a + b); .fold(0, |a, b| a + b);
// We counted overhead_len once too many times, // We counted overhead_len once too many times,
// but only if the iterator was non empty. // but only if the iterator was non empty.