tui_realm_stdlib/components/
paragraph.rs1use tuirealm::command::{Cmd, CmdResult};
10use tuirealm::props::{
11 Alignment, AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, Style,
12 TextModifiers, TextSpan,
13};
14use tuirealm::ratatui::text::Line as Spans;
15use tuirealm::ratatui::{
16 layout::Rect,
17 text::Span,
18 widgets::{Paragraph as TuiParagraph, Wrap},
19};
20use tuirealm::{Frame, MockComponent, State};
21
22#[derive(Default)]
28#[must_use]
29pub struct Paragraph {
30 props: Props,
31}
32
33impl Paragraph {
34 pub fn foreground(mut self, fg: Color) -> Self {
35 self.attr(Attribute::Foreground, AttrValue::Color(fg));
36 self
37 }
38
39 pub fn background(mut self, bg: Color) -> Self {
40 self.attr(Attribute::Background, AttrValue::Color(bg));
41 self
42 }
43
44 pub fn modifiers(mut self, m: TextModifiers) -> Self {
45 self.attr(Attribute::TextProps, AttrValue::TextModifiers(m));
46 self
47 }
48
49 pub fn borders(mut self, b: Borders) -> Self {
50 self.attr(Attribute::Borders, AttrValue::Borders(b));
51 self
52 }
53
54 pub fn alignment(mut self, a: Alignment) -> Self {
55 self.attr(Attribute::Alignment, AttrValue::Alignment(a));
56 self
57 }
58
59 pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
60 self.attr(Attribute::Title, AttrValue::Title((t.into(), a)));
61 self
62 }
63
64 pub fn text(mut self, s: impl IntoIterator<Item = TextSpan>) -> Self {
67 self.attr(
68 Attribute::Text,
69 AttrValue::Payload(PropPayload::Vec(
70 s.into_iter().map(PropValue::TextSpan).collect(),
71 )),
72 );
73 self
74 }
75
76 pub fn wrap(mut self, wrap: bool) -> Self {
77 self.attr(Attribute::TextWrap, AttrValue::Flag(wrap));
78 self
79 }
80}
81
82impl MockComponent for Paragraph {
83 fn view(&mut self, render: &mut Frame, area: Rect) {
84 if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
86 let payload = self
88 .props
89 .get_ref(Attribute::Text)
90 .and_then(|x| x.as_payload());
91 let text: Vec<Spans> = match payload {
92 Some(PropPayload::Vec(spans)) => spans
93 .iter()
94 .filter_map(|x| x.as_text_span())
96 .map(|x| {
97 let (fg, bg, modifiers) =
98 crate::utils::use_or_default_styles(&self.props, x);
99 Spans::from(vec![Span::styled(
100 &x.content,
101 Style::default().add_modifier(modifiers).fg(fg).bg(bg),
102 )])
103 })
104 .collect(),
105 _ => Vec::new(),
106 };
107 let alignment: Alignment = self
109 .props
110 .get_or(Attribute::Alignment, AttrValue::Alignment(Alignment::Left))
111 .unwrap_alignment();
112 let trim = self
114 .props
115 .get_or(Attribute::TextWrap, AttrValue::Flag(false))
116 .unwrap_flag();
117 let foreground = self
118 .props
119 .get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
120 .unwrap_color();
121 let background = self
122 .props
123 .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
124 .unwrap_color();
125 let modifiers = self
126 .props
127 .get_or(
128 Attribute::TextProps,
129 AttrValue::TextModifiers(TextModifiers::empty()),
130 )
131 .unwrap_text_modifiers();
132 let borders = self
133 .props
134 .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
135 .unwrap_borders();
136 let title = self
137 .props
138 .get_ref(Attribute::Title)
139 .and_then(|x| x.as_title());
140 let div = crate::utils::get_block(borders, title, true, None);
141 render.render_widget(
142 TuiParagraph::new(text)
143 .block(div)
144 .style(
145 Style::default()
146 .fg(foreground)
147 .bg(background)
148 .add_modifier(modifiers),
149 )
150 .alignment(alignment)
151 .wrap(Wrap { trim }),
152 area,
153 );
154 }
155 }
156
157 fn query(&self, attr: Attribute) -> Option<AttrValue> {
158 self.props.get(attr)
159 }
160
161 fn attr(&mut self, attr: Attribute, value: AttrValue) {
162 self.props.set(attr, value);
163 }
164
165 fn state(&self) -> State {
166 State::None
167 }
168
169 fn perform(&mut self, _cmd: Cmd) -> CmdResult {
170 CmdResult::None
171 }
172}
173
174#[cfg(test)]
175mod tests {
176
177 use super::*;
178
179 use pretty_assertions::assert_eq;
180
181 #[test]
182 fn test_components_paragraph() {
183 let component = Paragraph::default()
184 .background(Color::Blue)
185 .foreground(Color::Red)
186 .modifiers(TextModifiers::BOLD)
187 .alignment(Alignment::Center)
188 .text([
189 TextSpan::from("Press "),
190 TextSpan::from("<ESC>").fg(Color::Cyan).bold(),
191 TextSpan::from(" to quit"),
192 ])
193 .wrap(true)
194 .title("title", Alignment::Center);
195 assert_eq!(component.state(), State::None);
197 }
198
199 #[test]
200 fn various_text_types() {
201 let _ = Paragraph::default().text(vec![TextSpan::new("hello")]);
203 let _ = Paragraph::default().text([TextSpan::new("hello")]);
205 let _ = Paragraph::default().text(vec![TextSpan::new("hello")].into_boxed_slice());
207 let _ = Paragraph::default().text(["Hello"].map(TextSpan::new));
209 }
210}