tui_temp_fork/widgets/
paragraph.rs1use either::Either;
2use unicode_segmentation::UnicodeSegmentation;
3use unicode_width::UnicodeWidthStr;
4
5use crate::buffer::Buffer;
6use crate::layout::{Alignment, Rect};
7use crate::style::Style;
8use crate::widgets::reflow::{LineComposer, LineTruncator, Styled, WordWrapper};
9use crate::widgets::{Block, Text, Widget};
10
11fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) -> u16 {
12 match alignment {
13 Alignment::Center => (text_area_width / 2).saturating_sub(line_width / 2),
14 Alignment::Right => text_area_width.saturating_sub(line_width),
15 Alignment::Left => 0,
16 }
17}
18
19pub struct Paragraph<'a, 't, T>
40where
41 T: Iterator<Item = &'t Text<'t>>,
42{
43 block: Option<Block<'a>>,
45 style: Style,
47 wrapping: bool,
49 text: T,
51 raw: bool,
53 scroll: u16,
55 alignment: Alignment,
57}
58
59impl<'a, 't, T> Paragraph<'a, 't, T>
60where
61 T: Iterator<Item = &'t Text<'t>>,
62{
63 pub fn new(text: T) -> Paragraph<'a, 't, T> {
64 Paragraph {
65 block: None,
66 style: Default::default(),
67 wrapping: false,
68 raw: false,
69 text,
70 scroll: 0,
71 alignment: Alignment::Left,
72 }
73 }
74
75 pub fn block(mut self, block: Block<'a>) -> Paragraph<'a, 't, T> {
76 self.block = Some(block);
77 self
78 }
79
80 pub fn style(mut self, style: Style) -> Paragraph<'a, 't, T> {
81 self.style = style;
82 self
83 }
84
85 pub fn wrap(mut self, flag: bool) -> Paragraph<'a, 't, T> {
86 self.wrapping = flag;
87 self
88 }
89
90 pub fn raw(mut self, flag: bool) -> Paragraph<'a, 't, T> {
91 self.raw = flag;
92 self
93 }
94
95 pub fn scroll(mut self, offset: u16) -> Paragraph<'a, 't, T> {
96 self.scroll = offset;
97 self
98 }
99
100 pub fn alignment(mut self, alignment: Alignment) -> Paragraph<'a, 't, T> {
101 self.alignment = alignment;
102 self
103 }
104}
105
106impl<'a, 't, 'b, T> Widget for Paragraph<'a, 't, T>
107where
108 T: Iterator<Item = &'t Text<'t>>,
109{
110 fn draw(&mut self, area: Rect, buf: &mut Buffer) {
111 let text_area = match self.block {
112 Some(ref mut b) => {
113 b.draw(area, buf);
114 b.inner(area)
115 }
116 None => area,
117 };
118
119 if text_area.height < 1 {
120 return;
121 }
122
123 self.background(text_area, buf, self.style.bg);
124
125 let style = self.style;
126 let mut styled = self.text.by_ref().flat_map(|t| match *t {
127 Text::Raw(ref d) => {
128 let data: &'t str = d; Either::Left(UnicodeSegmentation::graphemes(data, true).map(|g| Styled(g, style)))
130 }
131 Text::Styled(ref d, s) => {
132 let data: &'t str = d; Either::Right(UnicodeSegmentation::graphemes(data, true).map(move |g| Styled(g, s)))
134 }
135 });
136
137 let mut line_composer: Box<dyn LineComposer> = if self.wrapping {
138 Box::new(WordWrapper::new(&mut styled, text_area.width))
139 } else {
140 Box::new(LineTruncator::new(&mut styled, text_area.width))
141 };
142 let mut y = 0;
143 while let Some((current_line, current_line_width)) = line_composer.next_line() {
144 if y >= self.scroll {
145 let mut x = get_line_offset(current_line_width, text_area.width, self.alignment);
146 for Styled(symbol, style) in current_line {
147 buf.get_mut(text_area.left() + x, text_area.top() + y - self.scroll)
148 .set_symbol(symbol)
149 .set_style(*style);
150 x += symbol.width() as u16;
151 }
152 }
153 y += 1;
154 if y >= text_area.height + self.scroll {
155 break;
156 }
157 }
158 }
159}