radicle_term/
label.rs

1use std::fmt;
2
3use crate::{cell::Cell, Color, Constraint, Element, Filled, Line, Paint, Size, Style};
4
5/// A styled string that does not contain any `'\n'` and implements [`Element`] and [`Cell`].
6#[derive(Clone, Default, Debug)]
7pub struct Label(Paint<String>);
8
9impl Label {
10    /// Create a new label.
11    pub fn new(s: &str) -> Self {
12        Self(Paint::new(cleanup(s)))
13    }
14
15    /// Create a blank label.
16    pub fn blank() -> Self {
17        Self(Paint::default())
18    }
19
20    /// Is this label empty.
21    pub fn is_blank(&self) -> bool {
22        self.content().is_empty()
23    }
24
25    /// Get unstyled content.
26    pub fn content(&self) -> &str {
27        self.0.content()
28    }
29
30    /// Create a single space label
31    pub fn space() -> Self {
32        Self(Paint::new(" ".to_owned()))
33    }
34
35    /// Box the label.
36    pub fn boxed(self) -> Box<dyn Element> {
37        Box::new(self)
38    }
39
40    /// Color the label's foreground.
41    pub fn fg(self, color: Color) -> Self {
42        Self(self.0.fg(color))
43    }
44
45    /// Color the label's background.
46    pub fn bg(self, color: Color) -> Self {
47        Self(self.0.bg(color))
48    }
49
50    /// Style a label.
51    pub fn style(self, style: Style) -> Self {
52        Self(self.0.with_style(style))
53    }
54
55    /// Get inner paint object.
56    pub fn paint(&self) -> &Paint<String> {
57        &self.0
58    }
59
60    /// Return a filled cell from this label.
61    pub fn filled(self, color: Color) -> Filled<Self> {
62        Filled { item: self, color }
63    }
64
65    /// Wrap into a line.
66    pub fn to_line(self) -> Line {
67        Line::from(self)
68    }
69}
70
71impl Element for Label {
72    fn size(&self, _parent: Constraint) -> Size {
73        Size::new(self.0.width(), 1)
74    }
75
76    fn render(&self, _parent: Constraint) -> Vec<Line> {
77        vec![Line::new(self.clone())]
78    }
79}
80
81impl fmt::Display for Label {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(f, "{}", self.0)
84    }
85}
86
87impl Cell for Label {
88    type Padded = Self;
89    type Truncated = Self;
90
91    fn background(&self) -> Color {
92        self.paint().style.background
93    }
94
95    fn pad(&self, width: usize) -> Self::Padded {
96        Self(self.0.pad(width))
97    }
98
99    fn truncate(&self, width: usize, delim: &str) -> Self::Truncated {
100        Self(self.0.truncate(width, delim))
101    }
102
103    fn width(&self) -> usize {
104        Cell::width(&self.0)
105    }
106}
107
108impl<D: fmt::Display> From<Paint<D>> for Label {
109    fn from(paint: Paint<D>) -> Self {
110        Self(Paint {
111            item: cleanup(paint.item.to_string().as_str()),
112            style: paint.style,
113        })
114    }
115}
116
117impl From<String> for Label {
118    fn from(value: String) -> Self {
119        Self::new(value.as_str())
120    }
121}
122
123impl From<&str> for Label {
124    fn from(value: &str) -> Self {
125        Self::new(value)
126    }
127}
128
129/// Create a new label from a [`Paint`] object.
130pub fn label(s: impl Into<Paint<String>>) -> Label {
131    Label::from(s.into())
132}
133
134/// Cleanup the input string for display as a label.
135fn cleanup(input: &str) -> String {
136    input.chars().filter(|c| *c != '\n' && *c != '\r').collect()
137}