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