Skip to main content

tui_realm_stdlib/components/
label.rs

1use tuirealm::command::{Cmd, CmdResult};
2use tuirealm::component::Component;
3use tuirealm::props::{
4    AttrValue, Attribute, Color, HorizontalAlignment, Props, QueryResult, Style, TextModifiers,
5};
6use tuirealm::ratatui::Frame;
7use tuirealm::ratatui::layout::Rect;
8use tuirealm::ratatui::widgets::Paragraph;
9use tuirealm::state::State;
10
11use crate::prop_ext::CommonProps;
12
13/// A Label. It represents a single-line, single-style text without any container support.
14///
15/// If multi-style text is wanted, use [`Span`](super::Span).
16/// If multi-style, mutli-line, with container support is wanted, use [`Paragraph`](super::Paragraph).
17#[derive(Default)]
18#[must_use]
19pub struct Label {
20    common: CommonProps,
21    props: Props,
22}
23
24impl Label {
25    /// Set the main foreground color.
26    pub fn foreground(mut self, fg: Color) -> Self {
27        self.attr(Attribute::Foreground, AttrValue::Color(fg));
28        self
29    }
30
31    /// Set the main background color.
32    pub fn background(mut self, bg: Color) -> Self {
33        self.attr(Attribute::Background, AttrValue::Color(bg));
34        self
35    }
36
37    /// Set the main text modifiers.
38    pub fn modifiers(mut self, m: TextModifiers) -> Self {
39        self.attr(Attribute::TextProps, AttrValue::TextModifiers(m));
40        self
41    }
42
43    /// Set the main style.
44    ///
45    /// This option will overwrite any previous [`foreground`](Self::foreground), [`background`](Self::background) and [`modifiers`](Self::modifiers)!
46    pub fn style(mut self, style: Style) -> Self {
47        self.attr(Attribute::Style, AttrValue::Style(style));
48        self
49    }
50
51    /// Set the Text content.
52    pub fn text<S: Into<String>>(mut self, t: S) -> Self {
53        // TODO: we should consider using Span
54        self.attr(Attribute::Text, AttrValue::String(t.into()));
55        self
56    }
57
58    /// Set the horizontal text alignment.
59    pub fn alignment_horizontal(mut self, alignment: HorizontalAlignment) -> Self {
60        self.attr(
61            Attribute::AlignmentHorizontal,
62            AttrValue::AlignmentHorizontal(alignment),
63        );
64        self
65    }
66}
67
68impl Component for Label {
69    fn view(&mut self, render: &mut Frame, area: Rect) {
70        if !self.common.display {
71            return;
72        }
73
74        // Make text
75        let text = self
76            .props
77            .get(Attribute::Text)
78            .and_then(|v| v.as_string())
79            .map_or("", |v| v.as_str());
80        let alignment: HorizontalAlignment = self
81            .props
82            .get(Attribute::AlignmentHorizontal)
83            .and_then(AttrValue::as_alignment_horizontal)
84            .unwrap_or(HorizontalAlignment::Left);
85        render.render_widget(
86            Paragraph::new(text)
87                .style(self.common.style)
88                .alignment(alignment),
89            area,
90        );
91    }
92
93    fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
94        if let Some(value) = self.common.get_for_query(attr) {
95            return Some(value);
96        }
97
98        self.props.get_for_query(attr)
99    }
100
101    fn attr(&mut self, attr: Attribute, value: AttrValue) {
102        if let Some(value) = self.common.set(attr, value) {
103            self.props.set(attr, value);
104        }
105    }
106
107    fn state(&self) -> State {
108        State::None
109    }
110
111    fn perform(&mut self, cmd: Cmd) -> CmdResult {
112        CmdResult::Invalid(cmd)
113    }
114}
115
116#[cfg(test)]
117mod tests {
118
119    use pretty_assertions::assert_eq;
120
121    use super::*;
122
123    #[test]
124    fn test_components_label() {
125        let component: Label = Label::default()
126            .alignment_horizontal(HorizontalAlignment::Center)
127            .background(Color::Red)
128            .foreground(Color::Yellow)
129            .modifiers(TextModifiers::BOLD)
130            .text("foobar");
131
132        assert_eq!(component.state(), State::None);
133    }
134
135    #[test]
136    fn test_various_text_inputs() {
137        let _ = Label::default().text("str");
138        let _ = Label::default().text(String::from("String"));
139        // explicitly test references to string working
140        #[allow(clippy::needless_borrows_for_generic_args)]
141        let _ = Label::default().text(&String::from("&String"));
142    }
143}