2015-05-18 18:18:04 +00:00
|
|
|
use std::cmp;
|
|
|
|
|
2015-05-18 18:51:30 +00:00
|
|
|
use vec::Vec2;
|
2015-05-15 00:41:17 +00:00
|
|
|
use view::{View,DimensionRequest,SizeRequest};
|
|
|
|
use div::*;
|
2015-05-15 18:58:47 +00:00
|
|
|
use printer::Printer;
|
2015-05-15 00:41:17 +00:00
|
|
|
|
|
|
|
/// A simple view showing a fixed text
|
|
|
|
pub struct TextView {
|
|
|
|
content: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the number of lines required to display the given text with the
|
|
|
|
/// specified maximum line width.
|
2015-05-15 00:48:24 +00:00
|
|
|
fn get_line_span(line: &str, max_width: usize) -> usize {
|
2015-05-15 00:41:17 +00:00
|
|
|
let mut lines = 1;
|
|
|
|
let mut length = 0;
|
2015-05-15 00:48:24 +00:00
|
|
|
for l in line.split(" ").map(|word| word.len()) {
|
|
|
|
length += l;
|
|
|
|
if length > max_width {
|
|
|
|
length = l;
|
|
|
|
lines += 1;
|
|
|
|
}
|
|
|
|
}
|
2015-05-15 00:41:17 +00:00
|
|
|
lines
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextView {
|
|
|
|
/// Creates a new TextView with the given content.
|
|
|
|
pub fn new(content: &str) -> Self {
|
|
|
|
TextView {
|
|
|
|
content: content.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-23 23:46:38 +00:00
|
|
|
/// Replace the text in this view.
|
2015-05-23 17:33:29 +00:00
|
|
|
pub fn set_content(&mut self, content: &str) {
|
|
|
|
self.content = content.to_string();
|
|
|
|
}
|
|
|
|
|
2015-05-23 23:46:38 +00:00
|
|
|
/// Returns the current text in this view.
|
|
|
|
pub fn get_content(&self) -> &str {
|
|
|
|
&self.content
|
|
|
|
}
|
|
|
|
|
2015-05-15 00:41:17 +00:00
|
|
|
/// Returns the number of lines required to display the content
|
|
|
|
/// with the given width.
|
2015-05-15 00:48:24 +00:00
|
|
|
fn get_num_lines(&self, max_width: usize) -> usize {
|
2015-05-15 00:41:17 +00:00
|
|
|
self.content.split("\n")
|
2015-05-15 00:48:24 +00:00
|
|
|
.map(|line| get_line_span(line, max_width))
|
2015-05-15 00:41:17 +00:00
|
|
|
.fold(0, |sum, x| sum + x)
|
|
|
|
}
|
|
|
|
|
2015-05-15 00:48:24 +00:00
|
|
|
fn get_num_cols(&self, max_height: usize) -> usize {
|
|
|
|
(div_up_usize(self.content.len(), max_height)..self.content.len())
|
|
|
|
.find(|w| self.get_num_lines(*w) <= max_height)
|
2015-05-15 00:41:17 +00:00
|
|
|
.unwrap()
|
|
|
|
}
|
2015-05-18 18:18:04 +00:00
|
|
|
|
|
|
|
fn get_ideal_size(&self) -> Vec2 {
|
2015-05-18 22:31:55 +00:00
|
|
|
let mut max_width = 0;
|
2015-05-18 18:18:04 +00:00
|
|
|
let mut height = 0;
|
|
|
|
|
|
|
|
for line in self.content.split("\n") {
|
|
|
|
height += 1;
|
2015-05-18 22:31:55 +00:00
|
|
|
max_width = cmp::max(max_width, line.len() as u32);
|
2015-05-18 18:18:04 +00:00
|
|
|
}
|
|
|
|
|
2015-05-18 22:31:55 +00:00
|
|
|
Vec2::new(max_width, height)
|
2015-05-18 18:18:04 +00:00
|
|
|
}
|
2015-05-15 00:41:17 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 22:00:20 +00:00
|
|
|
struct LinesIterator<'a> {
|
|
|
|
line: &'a str,
|
|
|
|
start: usize,
|
|
|
|
width: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <'a> LinesIterator<'a> {
|
|
|
|
fn new(line: &'a str, width: usize) -> Self {
|
|
|
|
if line.len() == 0 {
|
|
|
|
LinesIterator {
|
|
|
|
line: " ",
|
|
|
|
width: width,
|
|
|
|
start: 0,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LinesIterator {
|
|
|
|
line: line,
|
|
|
|
width: width,
|
|
|
|
start: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <'a> Iterator for LinesIterator<'a> {
|
|
|
|
|
|
|
|
type Item = &'a str;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<&'a str> {
|
|
|
|
if self.start >= self.line.len() {
|
|
|
|
None
|
|
|
|
} else if self.start + self.width >= self.line.len() {
|
|
|
|
let start = self.start;
|
|
|
|
self.start = self.line.len();
|
|
|
|
Some(&self.line[start..])
|
|
|
|
} else {
|
|
|
|
let start = self.start;
|
2015-05-15 23:06:48 +00:00
|
|
|
let (end,skip_space) = match self.line[start..start+self.width].rfind(" ") {
|
2015-05-15 22:00:20 +00:00
|
|
|
// Hard break
|
2015-05-15 23:06:48 +00:00
|
|
|
None => (start + self.width, false),
|
|
|
|
Some(i) => (start+i, true),
|
2015-05-15 22:00:20 +00:00
|
|
|
};
|
|
|
|
self.start = end;
|
2015-05-15 23:06:48 +00:00
|
|
|
if skip_space {
|
|
|
|
self.start += 1;
|
|
|
|
}
|
2015-05-15 22:00:20 +00:00
|
|
|
Some(&self.line[start..end])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-15 00:41:17 +00:00
|
|
|
impl View for TextView {
|
2015-05-22 23:28:05 +00:00
|
|
|
fn draw(&mut self, printer: &Printer, _: bool) {
|
2015-05-19 22:54:11 +00:00
|
|
|
// We don't have a focused view
|
|
|
|
|
2015-05-15 22:00:20 +00:00
|
|
|
let lines = self.content.split("\n")
|
|
|
|
.flat_map(|line| LinesIterator::new(line, printer.size.x as usize));
|
|
|
|
for (i, line) in lines.enumerate() {
|
|
|
|
printer.print((0,i), line);
|
|
|
|
}
|
|
|
|
|
2015-05-15 00:41:17 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 18:58:47 +00:00
|
|
|
fn get_min_size(&self, size: SizeRequest) -> Vec2 {
|
2015-05-15 00:41:17 +00:00
|
|
|
match (size.w,size.h) {
|
2015-05-15 18:58:47 +00:00
|
|
|
(DimensionRequest::Unknown, DimensionRequest::Unknown) => Vec2::new(self.content.len() as u32, 1),
|
2015-05-15 00:41:17 +00:00
|
|
|
(DimensionRequest::Fixed(w),_) => {
|
|
|
|
let h = self.get_num_lines(w as usize) as u32;
|
2015-05-15 18:58:47 +00:00
|
|
|
Vec2::new(w, h)
|
2015-05-15 00:41:17 +00:00
|
|
|
},
|
|
|
|
(_,DimensionRequest::Fixed(h)) => {
|
|
|
|
let w = self.get_num_cols(h as usize) as u32;
|
2015-05-15 18:58:47 +00:00
|
|
|
Vec2::new(w, h)
|
2015-05-15 00:41:17 +00:00
|
|
|
},
|
2015-05-15 23:06:48 +00:00
|
|
|
(DimensionRequest::AtMost(w),_) => {
|
2015-05-18 18:18:04 +00:00
|
|
|
let ideal = self.get_ideal_size();
|
|
|
|
|
|
|
|
if w >= ideal.x {
|
|
|
|
ideal
|
2015-05-15 23:06:48 +00:00
|
|
|
} else {
|
|
|
|
let h = self.get_num_lines(w as usize) as u32;
|
|
|
|
Vec2::new(w, h)
|
|
|
|
}
|
|
|
|
},
|
2015-05-15 00:41:17 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|