1use crate::{LabelLike, prelude::*};
2use gpui::StyleRefinement;
3
4/// A struct representing a label element in the UI.
5///
6/// The `Label` struct stores the label text and common properties for a label element.
7/// It provides methods for modifying these properties.
8///
9/// # Examples
10///
11/// ```
12/// use ui::prelude::*;
13///
14/// Label::new("Hello, World!");
15/// ```
16///
17/// **A colored label**, for example labeling a dangerous action:
18///
19/// ```
20/// use ui::prelude::*;
21///
22/// let my_label = Label::new("Delete").color(Color::Error);
23/// ```
24///
25/// **A label with a strikethrough**, for example labeling something that has been deleted:
26///
27/// ```
28/// use ui::prelude::*;
29///
30/// let my_label = Label::new("Deleted").strikethrough();
31/// ```
32#[derive(IntoElement, RegisterComponent)]
33pub struct Label {
34 base: LabelLike,
35 label: SharedString,
36}
37
38impl Label {
39 /// Creates a new [`Label`] with the given text.
40 ///
41 /// # Examples
42 ///
43 /// ```
44 /// use ui::prelude::*;
45 ///
46 /// let my_label = Label::new("Hello, World!");
47 /// ```
48 pub fn new(label: impl Into<SharedString>) -> Self {
49 Self {
50 base: LabelLike::new(),
51 label: label.into(),
52 }
53 }
54
55 /// Sets the text of the [`Label`].
56 pub fn set_text(&mut self, text: impl Into<SharedString>) {
57 self.label = text.into();
58 }
59
60 /// Truncates the label from the start, keeping the end visible.
61 pub fn truncate_start(mut self) -> Self {
62 self.base = self.base.truncate_start();
63 self
64 }
65}
66
67// Style methods.
68impl Label {
69 fn style(&mut self) -> &mut StyleRefinement {
70 self.base.base.style()
71 }
72
73 gpui::margin_style_methods!({
74 visibility: pub
75 });
76
77 pub fn flex_1(mut self) -> Self {
78 self.style().flex_grow = Some(1.);
79 self.style().flex_shrink = Some(1.);
80 self.style().flex_basis = Some(gpui::relative(0.).into());
81 self
82 }
83
84 pub fn flex_none(mut self) -> Self {
85 self.style().flex_grow = Some(0.);
86 self.style().flex_shrink = Some(0.);
87 self
88 }
89
90 pub fn flex_grow(mut self) -> Self {
91 self.style().flex_grow = Some(1.);
92 self
93 }
94
95 pub fn flex_shrink(mut self) -> Self {
96 self.style().flex_shrink = Some(1.);
97 self
98 }
99
100 pub fn flex_shrink_0(mut self) -> Self {
101 self.style().flex_shrink = Some(0.);
102 self
103 }
104}
105
106impl LabelCommon for Label {
107 /// Sets the size of the label using a [`LabelSize`].
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use ui::prelude::*;
113 ///
114 /// let my_label = Label::new("Hello, World!").size(LabelSize::Small);
115 /// ```
116 fn size(mut self, size: LabelSize) -> Self {
117 self.base = self.base.size(size);
118 self
119 }
120
121 /// Sets the weight of the label using a [`FontWeight`].
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// use gpui::FontWeight;
127 /// use ui::prelude::*;
128 ///
129 /// let my_label = Label::new("Hello, World!").weight(FontWeight::BOLD);
130 /// ```
131 fn weight(mut self, weight: gpui::FontWeight) -> Self {
132 self.base = self.base.weight(weight);
133 self
134 }
135
136 /// Sets the line height style of the label using a [`LineHeightStyle`].
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use ui::prelude::*;
142 ///
143 /// let my_label = Label::new("Hello, World!").line_height_style(LineHeightStyle::UiLabel);
144 /// ```
145 fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
146 self.base = self.base.line_height_style(line_height_style);
147 self
148 }
149
150 /// Sets the color of the label using a [`Color`].
151 ///
152 /// # Examples
153 ///
154 /// ```
155 /// use ui::prelude::*;
156 ///
157 /// let my_label = Label::new("Hello, World!").color(Color::Accent);
158 /// ```
159 fn color(mut self, color: Color) -> Self {
160 self.base = self.base.color(color);
161 self
162 }
163
164 /// Sets the strikethrough property of the label.
165 ///
166 /// # Examples
167 ///
168 /// ```
169 /// use ui::prelude::*;
170 ///
171 /// let my_label = Label::new("Hello, World!").strikethrough();
172 /// ```
173 fn strikethrough(mut self) -> Self {
174 self.base = self.base.strikethrough();
175 self
176 }
177
178 /// Sets the italic property of the label.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use ui::prelude::*;
184 ///
185 /// let my_label = Label::new("Hello, World!").italic();
186 /// ```
187 fn italic(mut self) -> Self {
188 self.base = self.base.italic();
189 self
190 }
191
192 /// Sets the alpha property of the color of label.
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// use ui::prelude::*;
198 ///
199 /// let my_label = Label::new("Hello, World!").alpha(0.5);
200 /// ```
201 fn alpha(mut self, alpha: f32) -> Self {
202 self.base = self.base.alpha(alpha);
203 self
204 }
205
206 fn underline(mut self) -> Self {
207 self.base = self.base.underline();
208 self
209 }
210
211 /// Truncates overflowing text with an ellipsis (`…`) if needed.
212 fn truncate(mut self) -> Self {
213 self.base = self.base.truncate();
214 self
215 }
216
217 fn single_line(mut self) -> Self {
218 self.label = SharedString::from(self.label.replace('\n', "⏎"));
219 self.base = self.base.single_line();
220 self
221 }
222
223 fn buffer_font(mut self, cx: &App) -> Self {
224 self.base = self.base.buffer_font(cx);
225 self
226 }
227
228 /// Styles the label to look like inline code.
229 fn inline_code(mut self, cx: &App) -> Self {
230 self.base = self.base.inline_code(cx);
231 self
232 }
233}
234
235impl RenderOnce for Label {
236 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
237 self.base.child(self.label)
238 }
239}
240
241impl Component for Label {
242 fn scope() -> ComponentScope {
243 ComponentScope::Typography
244 }
245
246 fn description() -> Option<&'static str> {
247 Some("A text label component that supports various styles, sizes, and formatting options.")
248 }
249
250 fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
251 Some(
252 v_flex()
253 .gap_6()
254 .children(vec![
255 example_group_with_title(
256 "Sizes",
257 vec![
258 single_example("Default", Label::new("Project Explorer").into_any_element()),
259 single_example("Small", Label::new("File: main.rs").size(LabelSize::Small).into_any_element()),
260 single_example("Large", Label::new("Welcome to Zed").size(LabelSize::Large).into_any_element()),
261 ],
262 ),
263 example_group_with_title(
264 "Colors",
265 vec![
266 single_example("Default", Label::new("Status: Ready").into_any_element()),
267 single_example("Accent", Label::new("New Update Available").color(Color::Accent).into_any_element()),
268 single_example("Error", Label::new("Build Failed").color(Color::Error).into_any_element()),
269 ],
270 ),
271 example_group_with_title(
272 "Styles",
273 vec![
274 single_example("Default", Label::new("Normal Text").into_any_element()),
275 single_example("Bold", Label::new("Important Notice").weight(gpui::FontWeight::BOLD).into_any_element()),
276 single_example("Italic", Label::new("Code Comment").italic().into_any_element()),
277 single_example("Strikethrough", Label::new("Deprecated Feature").strikethrough().into_any_element()),
278 single_example("Underline", Label::new("Clickable Link").underline().into_any_element()),
279 single_example("Inline Code", Label::new("fn main() {}").inline_code(cx).into_any_element()),
280 ],
281 ),
282 example_group_with_title(
283 "Line Height Styles",
284 vec![
285 single_example("Default", Label::new("Multi-line\nText\nExample").into_any_element()),
286 single_example("UI Label", Label::new("Compact\nUI\nLabel").line_height_style(LineHeightStyle::UiLabel).into_any_element()),
287 ],
288 ),
289 example_group_with_title(
290 "Special Cases",
291 vec![
292 single_example("Single Line", Label::new("Line 1\nLine 2\nLine 3").single_line().into_any_element()),
293 single_example("Regular Truncation", div().max_w_24().child(Label::new("This is a very long file name that should be truncated: very_long_file_name_with_many_words.rs").truncate()).into_any_element()),
294 single_example("Start Truncation", div().max_w_24().child(Label::new("zed/crates/ui/src/components/label/truncate/label/label.rs").truncate_start()).into_any_element()),
295 ],
296 ),
297 ])
298 .into_any_element()
299 )
300 }
301}