You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tui-rs/src/widgets/paragraph.rs

207 lines
5.7 KiB
Rust

use either::Either;
use itertools::{multipeek, MultiPeek};
use unicode_segmentation::UnicodeSegmentation;
8 years ago
use unicode_width::UnicodeWidthStr;
use buffer::Buffer;
use layout::{Alignment, Rect};
use style::Style;
6 years ago
use widgets::{Block, Widget};
/// A widget to display some text.
8 years ago
///
/// # Examples
///
/// ```
/// # extern crate tui;
/// # use tui::widgets::{Block, Borders, Paragraph, Text};
8 years ago
/// # use tui::style::{Style, Color};
/// # use tui::layout::{Alignment};
8 years ago
/// # fn main() {
/// let text = [
/// Text::Data("First line\n"),
/// Text::StyledData("Second line\n", Style::default().fg(Color::Red))
/// ];
/// Paragraph::new(text.iter())
/// .block(Block::default().title("Paragraph").borders(Borders::ALL))
8 years ago
/// .style(Style::default().fg(Color::White).bg(Color::Black))
/// .alignment(Alignment::Center)
/// .wrap(true);
8 years ago
/// # }
/// ```
pub struct Paragraph<'a, 't, T>
where
T: Iterator<Item = &'t Text<'t>>,
{
8 years ago
/// A block to wrap the widget in
block: Option<Block<'a>>,
/// Widget style
style: Style,
8 years ago
/// Wrap the text or not
wrapping: bool,
8 years ago
/// The text to display
text: T,
/// Should we parse the text for embedded commands
raw: bool,
/// Scroll
scroll: u16,
/// Aligenment of the text
alignment: Alignment,
}
pub enum Text<'b> {
Data(&'b str),
StyledData(&'b str, Style),
}
impl<'a, 't, T> Paragraph<'a, 't, T>
where
T: Iterator<Item = &'t Text<'t>>,
{
pub fn new(text: T) -> Paragraph<'a, 't, T> {
Paragraph {
block: None,
style: Default::default(),
wrapping: false,
raw: false,
text,
scroll: 0,
alignment: Alignment::Left,
}
}
pub fn block(&'a mut self, block: Block<'a>) -> &mut Paragraph<'a, 't, T> {
self.block = Some(block);
self
}
pub fn style(&mut self, style: Style) -> &mut Paragraph<'a, 't, T> {
self.style = style;
self
}
pub fn wrap(&mut self, flag: bool) -> &mut Paragraph<'a, 't, T> {
self.wrapping = flag;
self
}
pub fn raw(&mut self, flag: bool) -> &mut Paragraph<'a, 't, T> {
self.raw = flag;
self
}
pub fn scroll(&mut self, offset: u16) -> &mut Paragraph<'a, 't, T> {
self.scroll = offset;
self
}
pub fn alignment(&mut self, alignment: Alignment) -> &mut Paragraph<'a, 't, T> {
self.alignment = alignment;
self
}
}
impl<'a, 't, T> Widget for Paragraph<'a, 't, T>
7 years ago
where
T: Iterator<Item = &'t Text<'t>>,
{
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
let text_area = match self.block {
Some(ref mut b) => {
8 years ago
b.draw(area, buf);
b.inner(area)
}
None => area,
};
if text_area.height < 1 {
return;
}
self.background(&text_area, buf, self.style.bg);
let style = self.style;
let styled = self.text.by_ref().flat_map(|t| match *t {
Text::Data(d) => {
Either::Left(UnicodeSegmentation::graphemes(d, true).map(|g| (g, style)))
}
Text::StyledData(d, s) => {
Either::Right(UnicodeSegmentation::graphemes(d, true).map(move |g| (g, s)))
}
});
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)
}
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;
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)
}
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 {
break;
}
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;
}
}
}