1#![allow(missing_docs)]
2
3use gpui::{relative, AnyElement, FontWeight, StyleRefinement, Styled, UnderlineStyle};
4use settings::Settings;
5use smallvec::SmallVec;
6use theme::ThemeSettings;
7
8use crate::prelude::*;
9
10#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
11pub enum LabelSize {
12 #[default]
13 Default,
14 Large,
15 Small,
16 XSmall,
17}
18
19#[derive(Default, PartialEq, Copy, Clone)]
20pub enum LineHeightStyle {
21 #[default]
22 TextLabel,
23 /// Sets the line height to 1.
24 UiLabel,
25}
26
27/// A common set of traits all labels must implement.
28pub trait LabelCommon {
29 /// Sets the size of the label using a [`LabelSize`].
30 fn size(self, size: LabelSize) -> Self;
31
32 /// Sets the font weight of the label.
33 fn weight(self, weight: FontWeight) -> Self;
34
35 /// Sets the line height style of the label using a [`LineHeightStyle`].
36 fn line_height_style(self, line_height_style: LineHeightStyle) -> Self;
37
38 /// Sets the color of the label using a [`Color`].
39 fn color(self, color: Color) -> Self;
40
41 /// Sets the strikethrough property of the label.
42 fn strikethrough(self, strikethrough: bool) -> Self;
43
44 /// Sets the italic property of the label.
45 fn italic(self, italic: bool) -> Self;
46
47 /// Sets the underline property of the label
48 fn underline(self, underline: bool) -> Self;
49
50 /// Sets the alpha property of the label, overwriting the alpha value of the color.
51 fn alpha(self, alpha: f32) -> Self;
52
53 /// Truncates overflowing text with an ellipsis (`…`) if needed.
54 fn text_ellipsis(self) -> Self;
55
56 /// Sets the label to render as a single line.
57 fn single_line(self) -> Self;
58}
59
60#[derive(IntoElement)]
61pub struct LabelLike {
62 pub(super) base: Div,
63 size: LabelSize,
64 weight: Option<FontWeight>,
65 line_height_style: LineHeightStyle,
66 pub(crate) color: Color,
67 strikethrough: bool,
68 italic: bool,
69 children: SmallVec<[AnyElement; 2]>,
70 alpha: Option<f32>,
71 underline: bool,
72 single_line: bool,
73 text_ellipsis: bool,
74}
75
76impl Default for LabelLike {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl LabelLike {
83 pub fn new() -> Self {
84 Self {
85 base: div(),
86 size: LabelSize::Default,
87 weight: None,
88 line_height_style: LineHeightStyle::default(),
89 color: Color::Default,
90 strikethrough: false,
91 italic: false,
92 children: SmallVec::new(),
93 alpha: None,
94 underline: false,
95 single_line: false,
96 text_ellipsis: false,
97 }
98 }
99}
100
101// Style methods.
102impl LabelLike {
103 fn style(&mut self) -> &mut StyleRefinement {
104 self.base.style()
105 }
106
107 gpui::margin_style_methods!({
108 visibility: pub
109 });
110}
111
112impl LabelCommon for LabelLike {
113 fn size(mut self, size: LabelSize) -> Self {
114 self.size = size;
115 self
116 }
117
118 fn weight(mut self, weight: FontWeight) -> Self {
119 self.weight = Some(weight);
120 self
121 }
122
123 fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
124 self.line_height_style = line_height_style;
125 self
126 }
127
128 fn color(mut self, color: Color) -> Self {
129 self.color = color;
130 self
131 }
132
133 fn strikethrough(mut self, strikethrough: bool) -> Self {
134 self.strikethrough = strikethrough;
135 self
136 }
137
138 fn italic(mut self, italic: bool) -> Self {
139 self.italic = italic;
140 self
141 }
142
143 fn underline(mut self, underline: bool) -> Self {
144 self.underline = underline;
145 self
146 }
147
148 fn alpha(mut self, alpha: f32) -> Self {
149 self.alpha = Some(alpha);
150 self
151 }
152
153 fn text_ellipsis(mut self) -> Self {
154 self.text_ellipsis = true;
155 self
156 }
157
158 fn single_line(mut self) -> Self {
159 self.single_line = true;
160 self
161 }
162}
163
164impl ParentElement for LabelLike {
165 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
166 self.children.extend(elements)
167 }
168}
169
170impl RenderOnce for LabelLike {
171 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
172 let mut color = self.color.color(cx);
173 if let Some(alpha) = self.alpha {
174 color.fade_out(1.0 - alpha);
175 }
176
177 self.base
178 .map(|this| match self.size {
179 LabelSize::Large => this.text_ui_lg(cx),
180 LabelSize::Default => this.text_ui(cx),
181 LabelSize::Small => this.text_ui_sm(cx),
182 LabelSize::XSmall => this.text_ui_xs(cx),
183 })
184 .when(self.line_height_style == LineHeightStyle::UiLabel, |this| {
185 this.line_height(relative(1.))
186 })
187 .when(self.italic, |this| this.italic())
188 .when(self.underline, |mut this| {
189 this.text_style()
190 .get_or_insert_with(Default::default)
191 .underline = Some(UnderlineStyle {
192 thickness: px(1.),
193 color: None,
194 wavy: false,
195 });
196 this
197 })
198 .when(self.strikethrough, |this| this.line_through())
199 .when(self.single_line, |this| this.whitespace_nowrap())
200 .when(self.text_ellipsis, |this| {
201 this.overflow_x_hidden().text_ellipsis()
202 })
203 .text_color(color)
204 .font_weight(
205 self.weight
206 .unwrap_or(ThemeSettings::get_global(cx).ui_font.weight),
207 )
208 .children(self.children)
209 }
210}