mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Use proper bounds for dialogs
Buttons height was not removed from the budget for get_min_size
This commit is contained in:
parent
455a2a817c
commit
9b2dec2a7f
@ -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,16 +138,19 @@ 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(),
|
||||
inner_size,
|
||||
self.focus == Focus::Content));
|
||||
self.content
|
||||
.draw(&printer.sub_printer(self.borders.top_left() +
|
||||
self.padding.top_left(),
|
||||
inner_size,
|
||||
self.focus == Focus::Content));
|
||||
|
||||
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 + 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),
|
||||
@ -245,7 +256,7 @@ impl View for Dialog {
|
||||
// Left and Right move to other buttons
|
||||
Event::Key(Key::Right) if i + 1 <
|
||||
self.buttons
|
||||
.len() => {
|
||||
.len() => {
|
||||
self.focus = Focus::Button(i + 1);
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
@ -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 {
|
||||
false
|
||||
} else {
|
||||
(last.y < self.rows.len()) == (size.y < self.rows.len())
|
||||
},
|
||||
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,10 +173,14 @@ impl<'a> Iterator for LinesIterator<'a> {
|
||||
}
|
||||
|
||||
// Keep adding indivisible tokens
|
||||
let head_bytes = match head_bytes(content.split(' '), self.width, " ") {
|
||||
0 => head_bytes(content.graphemes(true), self.width, ""),
|
||||
other => { self.start += 1; other },
|
||||
};
|
||||
let head_bytes =
|
||||
match head_bytes(content.split(' '), self.width, " ") {
|
||||
0 => head_bytes(content.graphemes(true), self.width, ""),
|
||||
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,23 +250,24 @@ impl View for TextView {
|
||||
}
|
||||
}
|
||||
|
||||
fn head_bytes<'a, I: Iterator<Item=&'a str>>(iter: I, width: usize, overhead: &str)
|
||||
-> usize {
|
||||
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();
|
||||
|
||||
let sum = iter.scan(0, |w, token| {
|
||||
*w += token.width();
|
||||
if *w > width {
|
||||
None
|
||||
} else {
|
||||
// Add a space
|
||||
*w += overhead_width;
|
||||
Some(token)
|
||||
}
|
||||
})
|
||||
.map(|token| token.len() + overhead_len)
|
||||
.fold(0, |a, b| a + b);
|
||||
*w += token.width();
|
||||
if *w > width {
|
||||
None
|
||||
} else {
|
||||
// Add a space
|
||||
*w += overhead_width;
|
||||
Some(token)
|
||||
}
|
||||
})
|
||||
.map(|token| token.len() + overhead_len)
|
||||
.fold(0, |a, b| a + b);
|
||||
|
||||
// We counted overhead_len once too many times,
|
||||
// but only if the iterator was non empty.
|
||||
|
Loading…
Reference in New Issue
Block a user