tui_realm_stdlib/components/
span.rs

1//! ## Span
2//!
3//! `Span` represents a read-only text component without any container, but with the possibility to define multiple text parts.
4//! The main difference with `Label` is that the Span allows different styles inside the same component for the texsts.
5
6use tuirealm::command::{Cmd, CmdResult};
7use tuirealm::props::{
8    Alignment, AttrValue, Attribute, Color, PropPayload, PropValue, Props, Style, TextModifiers,
9    TextSpan,
10};
11use tuirealm::ratatui::text::Line as Spans;
12use tuirealm::ratatui::{
13    layout::Rect,
14    text::{Span as TuiSpan, Text},
15    widgets::Paragraph,
16};
17use tuirealm::{Frame, MockComponent, State};
18
19// -- Component
20
21/// ## Span
22///
23/// represents a read-only text component without any container, but with multy-style text parts
24#[derive(Default)]
25pub struct Span {
26    props: Props,
27}
28
29impl Span {
30    pub fn foreground(mut self, fg: Color) -> Self {
31        self.attr(Attribute::Foreground, AttrValue::Color(fg));
32        self
33    }
34
35    pub fn background(mut self, bg: Color) -> Self {
36        self.attr(Attribute::Background, AttrValue::Color(bg));
37        self
38    }
39
40    pub fn modifiers(mut self, m: TextModifiers) -> Self {
41        self.attr(Attribute::TextProps, AttrValue::TextModifiers(m));
42        self
43    }
44
45    pub fn alignment(mut self, a: Alignment) -> Self {
46        self.attr(Attribute::Alignment, AttrValue::Alignment(a));
47        self
48    }
49
50    pub fn spans(mut self, s: &[TextSpan]) -> Self {
51        self.attr(
52            Attribute::Text,
53            AttrValue::Payload(PropPayload::Vec(
54                s.iter().cloned().map(PropValue::TextSpan).collect(),
55            )),
56        );
57        self
58    }
59}
60
61impl MockComponent for Span {
62    fn view(&mut self, render: &mut Frame, area: Rect) {
63        // Make a Span
64        if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
65            // Make text
66            let foreground = self
67                .props
68                .get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
69                .unwrap_color();
70            let background = self
71                .props
72                .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
73                .unwrap_color();
74            let spans: Vec<TuiSpan> =
75                match self.props.get(Attribute::Text).map(|x| x.unwrap_payload()) {
76                    Some(PropPayload::Vec(spans)) => spans
77                        .iter()
78                        .cloned()
79                        .map(|x| x.unwrap_text_span())
80                        .map(|x| {
81                            // Keep colors and modifiers, or use default
82                            let (fg, bg, modifiers) =
83                                crate::utils::use_or_default_styles(&self.props, &x);
84                            TuiSpan::styled(
85                                x.content,
86                                Style::default().add_modifier(modifiers).fg(fg).bg(bg),
87                            )
88                        })
89                        .collect(),
90                    _ => Vec::new(),
91                };
92            let text: Text = Text::from(Spans::from(spans));
93            // Text properties
94            let alignment: Alignment = self
95                .props
96                .get_or(Attribute::Alignment, AttrValue::Alignment(Alignment::Left))
97                .unwrap_alignment();
98            render.render_widget(
99                Paragraph::new(text)
100                    .alignment(alignment)
101                    .style(Style::default().bg(background).fg(foreground)),
102                area,
103            );
104        }
105    }
106
107    fn query(&self, attr: Attribute) -> Option<AttrValue> {
108        self.props.get(attr)
109    }
110
111    fn attr(&mut self, attr: Attribute, value: AttrValue) {
112        self.props.set(attr, value)
113    }
114
115    fn state(&self) -> State {
116        State::None
117    }
118
119    fn perform(&mut self, _cmd: Cmd) -> CmdResult {
120        CmdResult::None
121    }
122}
123
124#[cfg(test)]
125mod tests {
126
127    use super::*;
128
129    use pretty_assertions::assert_eq;
130
131    #[test]
132    fn test_components_span() {
133        let component = Span::default()
134            .background(Color::Blue)
135            .foreground(Color::Red)
136            .modifiers(TextModifiers::BOLD)
137            .alignment(Alignment::Center)
138            .spans(&[
139                TextSpan::from("Press "),
140                TextSpan::from("<ESC>").fg(Color::Cyan).bold(),
141                TextSpan::from(" to quit"),
142            ]);
143        // Get value
144        assert_eq!(component.state(), State::None);
145    }
146}