From 0419fa71579fd2df360e7bcae450fa0b6966ff16 Mon Sep 17 00:00:00 2001 From: Florian Dehau Date: Sat, 22 Oct 2016 19:24:17 +0200 Subject: [PATCH] Update text widget (templating and wrapping) --- src/widgets/text.rs | 132 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 6 deletions(-) diff --git a/src/widgets/text.rs b/src/widgets/text.rs index e11e03c..23f4830 100644 --- a/src/widgets/text.rs +++ b/src/widgets/text.rs @@ -1,4 +1,4 @@ -use std::cmp::min; +use unicode_segmentation::UnicodeSegmentation; use widgets::{Widget, Block}; use buffer::Buffer; @@ -9,6 +9,7 @@ pub struct Text<'a> { block: Option>, fg: Color, bg: Color, + wrapping: bool, text: &'a str, colors: &'a [(u16, u16, u16, Color, Color)], } @@ -19,6 +20,7 @@ impl<'a> Default for Text<'a> { block: None, fg: Color::Reset, bg: Color::Reset, + wrapping: false, text: "", colors: &[], } @@ -50,6 +52,100 @@ impl<'a> Text<'a> { self.colors = colors; self } + + pub fn wrap(&mut self, flag: bool) -> &mut Text<'a> { + self.wrapping = flag; + self + } +} + +struct Parser<'a> { + text: Vec<&'a str>, + mark: bool, + color_string: String, + color: Color, + escaping: bool, + coloring: bool, +} + +impl<'a> Parser<'a> { + fn new(text: &'a str) -> Parser<'a> { + Parser { + text: UnicodeSegmentation::graphemes(text, true).rev().collect::>(), + mark: false, + color_string: String::from(""), + color: Color::Reset, + escaping: false, + coloring: false, + } + } + + fn update_color(&mut self) { + self.color = match &*self.color_string { + "black" => Color::Black, + "red" => Color::Red, + "green" => Color::Green, + "yellow" => Color::Yellow, + "magenta" => Color::Magenta, + "cyan" => Color::Cyan, + "gray" => Color::Gray, + "dark_gray" => Color::DarkGray, + "light_red" => Color::LightRed, + "light_green" => Color::LightGreen, + "light_yellow" => Color::LightYellow, + "light_magenta" => Color::LightMagenta, + "light_cyan" => Color::LightCyan, + "white" => Color::White, + _ => Color::Reset, + } + } + + fn reset(&mut self) { + self.coloring = false; + self.mark = false; + self.color = Color::Reset; + self.color_string = String::from(""); + } +} + +impl<'a> Iterator for Parser<'a> { + type Item = (&'a str, Color); + fn next(&mut self) -> Option<(&'a str, Color)> { + match self.text.pop() { + Some(s) => { + if s == "\\" { + if self.escaping { + Some((s, self.color)) + } else { + self.escaping = true; + self.next() + } + } else if s == "{" { + if self.escaping { + self.escaping = false; + Some((s, self.color)) + } else { + self.color = Color::Reset; + self.mark = true; + self.next() + } + } else if s == "}" && self.mark { + self.reset(); + self.next() + } else if s == " " && self.mark { + self.coloring = true; + self.update_color(); + self.next() + } else if self.mark && !self.coloring { + self.color_string.push_str(s); + self.next() + } else { + Some((s, self.color)) + } + } + None => None, + } + } } impl<'a> Widget<'a> for Text<'a> { @@ -58,17 +154,41 @@ impl<'a> Widget<'a> for Text<'a> { Some(ref b) => (b.buffer(area), b.inner(*area)), None => (Buffer::empty(*area), *area), }; - let mut lines = self.text.lines().collect::>(); + + if text_area.height < 1 { + return buf; + } + let margin_x = text_area.x - area.x; let margin_y = text_area.y - area.y; - let height = min(lines.len(), text_area.height as usize); - for line in lines.iter_mut().take(height) { - buf.set_string(margin_x, margin_y, line, self.fg, self.bg); + let mut x = 0; + let mut y = 0; + for (s, c) in Parser::new(self.text) { + if s == "\n" { + x = 0; + y += 1; + continue; + } + if x >= text_area.width { + if self.wrapping { + x = 0; + y += 1; + } + continue; + } + + if y > text_area.height - 1 { + break; + } + + buf.update_cell(margin_x + x, margin_y + y, s, c, self.bg); + x += 1; } + for &(x, y, width, fg, bg) in self.colors { for i in 0..width { buf.set_fg(x + i, y + margin_y, fg); - buf.set_bg(x + i, y + margin_y, fg); + buf.set_bg(x + i, y + margin_y, bg); } } buf