1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use std::fmt;

use crate::{cell::Cell, Color, Constraint, Element, Filled, Line, Paint, Size, Style};

/// A styled string that does not contain any `'\n'` and implements [`Element`] and [`Cell`].
#[derive(Clone, Default, Debug)]
pub struct Label(Paint<String>);

impl Label {
    /// Create a new label.
    pub fn new(s: &str) -> Self {
        Self(Paint::new(cleanup(s)))
    }

    /// Create a blank label.
    pub fn blank() -> Self {
        Self(Paint::default())
    }

    /// Is this label empty.
    pub fn is_blank(&self) -> bool {
        self.content().is_empty()
    }

    /// Get unstyled content.
    pub fn content(&self) -> &str {
        self.0.content()
    }

    /// Create a single space label
    pub fn space() -> Self {
        Self(Paint::new(" ".to_owned()))
    }

    /// Box the label.
    pub fn boxed(self) -> Box<dyn Element> {
        Box::new(self)
    }

    /// Color the label's foreground.
    pub fn fg(self, color: Color) -> Self {
        Self(self.0.fg(color))
    }

    /// Color the label's background.
    pub fn bg(self, color: Color) -> Self {
        Self(self.0.bg(color))
    }

    /// Style a label.
    pub fn style(self, style: Style) -> Self {
        Self(self.0.with_style(style))
    }

    /// Get inner paint object.
    pub fn paint(&self) -> &Paint<String> {
        &self.0
    }

    /// Return a filled cell from this label.
    pub fn filled(self, color: Color) -> Filled<Self> {
        Filled { item: self, color }
    }

    /// Wrap into a line.
    pub fn to_line(self) -> Line {
        Line::from(self)
    }
}

impl Element for Label {
    fn size(&self, _parent: Constraint) -> Size {
        Size::new(self.0.width(), 1)
    }

    fn render(&self, _parent: Constraint) -> Vec<Line> {
        vec![Line::new(self.clone())]
    }
}

impl fmt::Display for Label {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Cell for Label {
    type Padded = Self;
    type Truncated = Self;

    fn background(&self) -> Color {
        self.paint().style.background
    }

    fn pad(&self, width: usize) -> Self::Padded {
        Self(self.0.pad(width))
    }

    fn truncate(&self, width: usize, delim: &str) -> Self::Truncated {
        Self(self.0.truncate(width, delim))
    }

    fn width(&self) -> usize {
        Cell::width(&self.0)
    }
}

impl<D: fmt::Display> From<Paint<D>> for Label {
    fn from(paint: Paint<D>) -> Self {
        Self(Paint {
            item: cleanup(paint.item.to_string().as_str()),
            style: paint.style,
        })
    }
}

impl From<String> for Label {
    fn from(value: String) -> Self {
        Self::new(value.as_str())
    }
}

impl From<&str> for Label {
    fn from(value: &str) -> Self {
        Self::new(value)
    }
}

/// Create a new label from a [`Paint`] object.
pub fn label(s: impl Into<Paint<String>>) -> Label {
    Label::from(s.into())
}

/// Cleanup the input string for display as a label.
fn cleanup(input: &str) -> String {
    input.chars().filter(|c| *c != '\n' && *c != '\r').collect()
}