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 text_ellipsis(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 text_ellipsis: 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 text_ellipsis: 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 fn text_ellipsis(mut self) -> Self {
170 self.text_ellipsis = true;
171 self
172 }
173
174 fn single_line(mut self) -> Self {
175 self.single_line = true;
176 self
177 }
178
179 fn buffer_font(mut self, cx: &App) -> Self {
180 self.base = self
181 .base
182 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone());
183 self
184 }
185}
186
187impl ParentElement for LabelLike {
188 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
189 self.children.extend(elements)
190 }
191}
192
193impl RenderOnce for LabelLike {
194 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
195 let mut color = self.color.color(cx);
196 if let Some(alpha) = self.alpha {
197 color.fade_out(1.0 - alpha);
198 }
199
200 self.base
201 .map(|this| match self.size {
202 LabelSize::Large => this.text_ui_lg(cx),
203 LabelSize::Default => this.text_ui(cx),
204 LabelSize::Small => this.text_ui_sm(cx),
205 LabelSize::XSmall => this.text_ui_xs(cx),
206 })
207 .when(self.line_height_style == LineHeightStyle::UiLabel, |this| {
208 this.line_height(relative(1.))
209 })
210 .when(self.italic, |this| this.italic())
211 .when(self.underline, |mut this| {
212 this.text_style()
213 .get_or_insert_with(Default::default)
214 .underline = Some(UnderlineStyle {
215 thickness: px(1.),
216 color: None,
217 wavy: false,
218 });
219 this
220 })
221 .when(self.strikethrough, |this| this.line_through())
222 .when(self.single_line, |this| this.whitespace_nowrap())
223 .when(self.text_ellipsis, |this| {
224 this.overflow_x_hidden().text_ellipsis()
225 })
226 .text_color(color)
227 .font_weight(
228 self.weight
229 .unwrap_or(ThemeSettings::get_global(cx).ui_font.weight),
230 )
231 .children(self.children)
232 }
233}