2018-09-05 23:50:48 +00:00
|
|
|
use std::borrow::Cow;
|
|
|
|
|
2018-08-12 20:13:32 +00:00
|
|
|
use either::Either;
|
2018-05-25 11:21:36 +00:00
|
|
|
use itertools::{multipeek, MultiPeek};
|
2016-10-22 17:24:17 +00:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
2016-11-04 16:54:57 +00:00
|
|
|
use unicode_width::UnicodeWidthStr;
|
2016-10-14 17:44:52 +00:00
|
|
|
|
|
|
|
use buffer::Buffer;
|
2018-08-12 20:13:32 +00:00
|
|
|
use layout::{Alignment, Rect};
|
|
|
|
use style::Style;
|
2018-05-06 10:59:24 +00:00
|
|
|
use widgets::{Block, Widget};
|
2016-10-14 17:44:52 +00:00
|
|
|
|
2018-08-12 20:13:32 +00:00
|
|
|
/// A widget to display some text.
|
2016-11-04 16:54:57 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # extern crate tui;
|
2018-08-12 20:13:32 +00:00
|
|
|
/// # use tui::widgets::{Block, Borders, Paragraph, Text};
|
2016-11-06 20:41:32 +00:00
|
|
|
/// # use tui::style::{Style, Color};
|
2018-08-12 20:13:32 +00:00
|
|
|
/// # use tui::layout::{Alignment};
|
2016-11-04 16:54:57 +00:00
|
|
|
/// # fn main() {
|
2018-08-12 20:13:32 +00:00
|
|
|
/// let text = [
|
2018-09-06 00:12:28 +00:00
|
|
|
/// Text::Data("First line\n".into()),
|
|
|
|
/// Text::StyledData("Second line\n".into(), Style::default().fg(Color::Red))
|
2018-08-12 20:13:32 +00:00
|
|
|
/// ];
|
|
|
|
/// Paragraph::new(text.iter())
|
2017-12-26 15:55:13 +00:00
|
|
|
/// .block(Block::default().title("Paragraph").borders(Borders::ALL))
|
2016-11-06 20:41:32 +00:00
|
|
|
/// .style(Style::default().fg(Color::White).bg(Color::Black))
|
2018-08-12 20:13:32 +00:00
|
|
|
/// .alignment(Alignment::Center)
|
|
|
|
/// .wrap(true);
|
2016-11-04 16:54:57 +00:00
|
|
|
/// # }
|
|
|
|
/// ```
|
2018-08-12 20:13:32 +00:00
|
|
|
pub struct Paragraph<'a, 't, T>
|
|
|
|
where
|
|
|
|
T: Iterator<Item = &'t Text<'t>>,
|
|
|
|
{
|
2016-11-04 16:54:57 +00:00
|
|
|
/// A block to wrap the widget in
|
2016-10-14 17:44:52 +00:00
|
|
|
block: Option<Block<'a>>,
|
2016-11-06 17:49:57 +00:00
|
|
|
/// Widget style
|
|
|
|
style: Style,
|
2016-11-04 16:54:57 +00:00
|
|
|
/// Wrap the text or not
|
2016-10-22 17:24:17 +00:00
|
|
|
wrapping: bool,
|
2016-11-04 16:54:57 +00:00
|
|
|
/// The text to display
|
2018-08-12 20:13:32 +00:00
|
|
|
text: T,
|
2016-11-28 09:00:35 +00:00
|
|
|
/// Should we parse the text for embedded commands
|
|
|
|
raw: bool,
|
|
|
|
/// Scroll
|
|
|
|
scroll: u16,
|
2018-05-25 11:21:36 +00:00
|
|
|
/// Aligenment of the text
|
|
|
|
alignment: Alignment,
|
2016-10-14 17:44:52 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 20:13:32 +00:00
|
|
|
pub enum Text<'b> {
|
2018-09-05 23:50:48 +00:00
|
|
|
Data(Cow<'b, str>),
|
|
|
|
StyledData(Cow<'b, str>, Style),
|
2018-08-12 20:13:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 't, T> Paragraph<'a, 't, T>
|
|
|
|
where
|
|
|
|
T: Iterator<Item = &'t Text<'t>>,
|
|
|
|
{
|
|
|
|
pub fn new(text: T) -> Paragraph<'a, 't, T> {
|
2016-11-06 17:49:57 +00:00
|
|
|
Paragraph {
|
2016-10-14 17:44:52 +00:00
|
|
|
block: None,
|
2016-11-06 17:49:57 +00:00
|
|
|
style: Default::default(),
|
2016-10-22 17:24:17 +00:00
|
|
|
wrapping: false,
|
2016-11-28 09:00:35 +00:00
|
|
|
raw: false,
|
2018-08-12 20:13:32 +00:00
|
|
|
text,
|
2016-11-28 09:00:35 +00:00
|
|
|
scroll: 0,
|
2018-05-25 11:21:36 +00:00
|
|
|
alignment: Alignment::Left,
|
2016-10-14 17:44:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn block(mut self, block: Block<'a>) -> Paragraph<'a, 't, T> {
|
2016-10-14 17:44:52 +00:00
|
|
|
self.block = Some(block);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn style(mut self, style: Style) -> Paragraph<'a, 't, T> {
|
2016-11-06 17:49:57 +00:00
|
|
|
self.style = style;
|
2016-10-14 17:44:52 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn wrap(mut self, flag: bool) -> Paragraph<'a, 't, T> {
|
2016-10-22 17:24:17 +00:00
|
|
|
self.wrapping = flag;
|
|
|
|
self
|
|
|
|
}
|
2016-11-28 09:00:35 +00:00
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn raw(mut self, flag: bool) -> Paragraph<'a, 't, T> {
|
2016-11-28 09:00:35 +00:00
|
|
|
self.raw = flag;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn scroll(mut self, offset: u16) -> Paragraph<'a, 't, T> {
|
2016-11-28 09:00:35 +00:00
|
|
|
self.scroll = offset;
|
|
|
|
self
|
|
|
|
}
|
2018-05-25 11:21:36 +00:00
|
|
|
|
2018-09-01 12:05:33 +00:00
|
|
|
pub fn alignment(mut self, alignment: Alignment) -> Paragraph<'a, 't, T> {
|
2018-05-25 11:21:36 +00:00
|
|
|
self.alignment = alignment;
|
|
|
|
self
|
|
|
|
}
|
2016-10-22 17:24:17 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 20:13:32 +00:00
|
|
|
impl<'a, 't, T> Widget for Paragraph<'a, 't, T>
|
2017-09-11 05:58:37 +00:00
|
|
|
where
|
2018-08-12 20:13:32 +00:00
|
|
|
T: Iterator<Item = &'t Text<'t>>,
|
2016-11-28 09:00:35 +00:00
|
|
|
{
|
2018-08-12 22:27:56 +00:00
|
|
|
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
|
2016-10-26 12:32:45 +00:00
|
|
|
let text_area = match self.block {
|
2017-09-03 09:58:39 +00:00
|
|
|
Some(ref mut b) => {
|
2016-11-02 18:16:53 +00:00
|
|
|
b.draw(area, buf);
|
2016-10-26 12:32:45 +00:00
|
|
|
b.inner(area)
|
|
|
|
}
|
2018-08-12 22:27:56 +00:00
|
|
|
None => area,
|
2016-10-14 17:44:52 +00:00
|
|
|
};
|
2016-10-22 17:24:17 +00:00
|
|
|
|
|
|
|
if text_area.height < 1 {
|
2016-10-26 12:32:45 +00:00
|
|
|
return;
|
2016-10-22 17:24:17 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 17:49:57 +00:00
|
|
|
self.background(&text_area, buf, self.style.bg);
|
2016-11-02 16:08:52 +00:00
|
|
|
|
2018-08-12 20:13:32 +00:00
|
|
|
let style = self.style;
|
2018-08-12 22:27:56 +00:00
|
|
|
let styled = self.text.by_ref().flat_map(|t| match *t {
|
2018-09-05 23:50:48 +00:00
|
|
|
Text::Data(ref d) => {
|
|
|
|
let data: &'t str = d; // coerce to &str
|
|
|
|
Either::Left(UnicodeSegmentation::graphemes(data, true).map(|g| (g, style)))
|
2018-08-12 20:13:32 +00:00
|
|
|
}
|
2018-09-05 23:50:48 +00:00
|
|
|
Text::StyledData(ref d, s) => {
|
|
|
|
let data: &'t str = d; // coerce to &str
|
|
|
|
Either::Right(UnicodeSegmentation::graphemes(data, true).map(move |g| (g, s)))
|
2018-08-12 20:13:32 +00:00
|
|
|
}
|
|
|
|
});
|
2018-05-25 11:21:36 +00:00
|
|
|
let mut styled = multipeek(styled);
|
|
|
|
|
|
|
|
fn get_cur_line_len<'a, I: Iterator<Item = (&'a str, Style)>>(
|
|
|
|
styled: &mut MultiPeek<I>,
|
|
|
|
) -> u16 {
|
|
|
|
let mut line_len = 0;
|
|
|
|
while match &styled.peek() {
|
|
|
|
Some(&(x, _)) => x != "\n",
|
|
|
|
None => false,
|
|
|
|
} {
|
|
|
|
line_len += 1;
|
|
|
|
}
|
|
|
|
line_len
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut x = match self.alignment {
|
|
|
|
Alignment::Center => {
|
|
|
|
(text_area.width / 2).saturating_sub(get_cur_line_len(&mut styled) / 2)
|
2016-10-22 17:24:17 +00:00
|
|
|
}
|
2018-05-25 11:21:36 +00:00
|
|
|
Alignment::Right => (text_area.width).saturating_sub(get_cur_line_len(&mut styled)),
|
|
|
|
Alignment::Left => 0,
|
|
|
|
};
|
|
|
|
let mut y = 0;
|
|
|
|
|
|
|
|
let mut remove_leading_whitespaces = false;
|
2018-08-12 22:27:56 +00:00
|
|
|
while let Some((string, style)) = styled.next() {
|
|
|
|
if string == "\n" {
|
|
|
|
x = match self.alignment {
|
|
|
|
Alignment::Center => {
|
|
|
|
(text_area.width / 2).saturating_sub(get_cur_line_len(&mut styled) / 2)
|
2018-05-25 11:21:36 +00:00
|
|
|
}
|
2018-08-12 22:27:56 +00:00
|
|
|
|
|
|
|
Alignment::Right => {
|
|
|
|
(text_area.width).saturating_sub(get_cur_line_len(&mut styled))
|
|
|
|
}
|
|
|
|
Alignment::Left => 0,
|
|
|
|
};
|
|
|
|
y += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if x >= text_area.width && self.wrapping {
|
|
|
|
x = match self.alignment {
|
|
|
|
Alignment::Center => {
|
|
|
|
(text_area.width / 2).saturating_sub(get_cur_line_len(&mut styled) / 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
Alignment::Right => {
|
|
|
|
(text_area.width).saturating_sub(get_cur_line_len(&mut styled) + 1)
|
|
|
|
}
|
|
|
|
Alignment::Left => 0,
|
|
|
|
};
|
|
|
|
y += 1;
|
|
|
|
remove_leading_whitespaces = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if remove_leading_whitespaces && string == " " {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
remove_leading_whitespaces = false;
|
|
|
|
|
|
|
|
if y > text_area.height + self.scroll - 1 {
|
2018-05-25 11:21:36 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-08-12 22:27:56 +00:00
|
|
|
|
|
|
|
if y < self.scroll {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll)
|
|
|
|
.set_symbol(string)
|
|
|
|
.set_style(style);
|
|
|
|
x += string.width() as u16;
|
2016-10-14 17:44:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|