mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 11:16:03 +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};
|
/// # 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)
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user