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};
/// 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 {
title: String,
@ -108,8 +109,8 @@ impl Dialog {
impl View for Dialog {
fn draw(&mut self, printer: &Printer) {
// This will be the height used by the buttons.
let mut height = 0;
// This will be the buttons_height used by the buttons.
let mut buttons_height = 0;
// Current horizontal position of the next button we'll draw.
// Sum of the sizes + len-1 for margins
@ -123,7 +124,9 @@ impl View for Dialog {
};
let overhead = self.padding + self.borders;
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;
for (i, button) in self.buttons.iter_mut().enumerate() {
@ -135,14 +138,17 @@ impl View for Dialog {
// Keep 1 blank between two buttons
offset += size.x + 1;
// 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?
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.content.draw(&printer.sub_printer(self.borders.top_left() + self.padding.top_left(),
self.content
.draw(&printer.sub_printer(self.borders.top_left() +
self.padding.top_left(),
inner_size,
self.focus == Focus::Content));
@ -154,16 +160,17 @@ impl View for Dialog {
printer.print((x - 2, 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 {
// Padding and borders are not available for kids.
let content_req = req - (self.padding.combined() + self.borders.combined());
let content_size = self.content.get_min_size(content_req);
let nomans_land = self.padding.combined() + self.borders.combined();
// Buttons are not flexible, so their size doesn't depend on ours.
let mut buttons_size = Vec2::new(0, 0);
if !self.buttons.is_empty() {
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);
}
// 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 X axis, we take the max.
let mut inner_size = Vec2::new(max(content_size.x, buttons_size.x),

View File

@ -1,5 +1,3 @@
use std::cmp::max;
use vec::Vec2;
use view::View;
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 {
/// Creates a new TextView with the given content.
pub fn new(content: &str) -> Self {
@ -104,36 +84,16 @@ impl TextView {
&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 {
match self.last_size {
None => false,
Some(last) => if last.x != size.x {
Some(last) => {
if last.x != size.x {
false
} else {
(last.y < self.rows.len()) == (size.y < self.rows.len())
},
}
}
}
}
@ -145,11 +105,16 @@ impl TextView {
if self.rows.len() > size.y {
scrollbar = 2;
// 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();
}
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);
}
@ -208,9 +173,13 @@ impl<'a> Iterator for LinesIterator<'a> {
}
// Keep adding indivisible tokens
let head_bytes = match head_bytes(content.split(' '), self.width, " ") {
let head_bytes =
match head_bytes(content.split(' '), self.width, " ") {
0 => head_bytes(content.graphemes(true), self.width, ""),
other => { self.start += 1; other },
other => {
self.start += 1;
other
}
};
self.start += head_bytes;
@ -228,9 +197,8 @@ impl View for TextView {
let h = self.rows.len();
let offset = self.align.v.get_offset(h, printer.size.y);
let printer = &printer.sub_printer(Vec2::new(0, offset),
printer.size,
true);
let printer =
&printer.sub_printer(Vec2::new(0, offset), printer.size, true);
self.scrollbase.draw(printer, |printer, i| {
let row = &self.rows[i];
@ -253,9 +221,7 @@ impl View for TextView {
self.scrollbase.scroll_up(1)
}
Event::Key(Key::Down) if self.scrollbase
.can_scroll_down() => {
self.scrollbase.scroll_down(1)
}
.can_scroll_down() => self.scrollbase.scroll_down(1),
Event::Key(Key::PageDown) => self.scrollbase.scroll_down(10),
Event::Key(Key::PageUp) => self.scrollbase.scroll_up(10),
_ => return EventResult::Ignored,
@ -284,7 +250,8 @@ 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,
overhead: &str)
-> usize {
let overhead_width = overhead.width();
let overhead_len = overhead.len();