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 /// Sets the font to the buffer's
60 fn buffer_font(self, cx: &App) -> Self;
61}
62
63#[derive(IntoElement)]
64pub struct LabelLike {
65 pub(super) base: Div,
66 size: LabelSize,
67 weight: Option<FontWeight>,
68 line_height_style: LineHeightStyle,
69 pub(crate) color: Color,
70 strikethrough: bool,
71 italic: bool,
72 children: SmallVec<[AnyElement; 2]>,
73 alpha: Option<f32>,
74 underline: bool,
75 single_line: bool,
76 text_ellipsis: bool,
77}
78
79impl Default for LabelLike {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85impl LabelLike {
86 pub fn new() -> Self {
87 Self {
88 base: div(),
89 size: LabelSize::Default,
90 weight: None,
91 line_height_style: LineHeightStyle::default(),
92 color: Color::Default,
93 strikethrough: false,
94 italic: false,
95 children: SmallVec::new(),
96 alpha: None,
97 underline: false,
98 single_line: false,
99 text_ellipsis: false,
100 }
101 }
102}
103
104// Style methods.
105impl LabelLike {
106 fn style(&mut self) -> &mut StyleRefinement {
107 self.base.style()
108 }
109
110 gpui::margin_style_methods!({
111 visibility: pub
112 });
113}
114
115impl LabelCommon for LabelLike {
116 fn size(mut self, size: LabelSize) -> Self {
117 self.size = size;
118 self
119 }
120
121 fn weight(mut self, weight: FontWeight) -> Self {
122 self.weight = Some(weight);
123 self
124 }
125
126 fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
127 self.line_height_style = line_height_style;
128 self
129 }
130
131 fn color(mut self, color: Color) -> Self {
132 self.color = color;
133 self
134 }
135
136 fn strikethrough(mut self, strikethrough: bool) -> Self {
137 self.strikethrough = strikethrough;
138 self
139 }
140
141 fn italic(mut self, italic: bool) -> Self {
142 self.italic = italic;
143 self
144 }
145
146 fn underline(mut self, underline: bool) -> Self {
147 self.underline = underline;
148 self
149 }
150
151 fn alpha(mut self, alpha: f32) -> Self {
152 self.alpha = Some(alpha);
153 self
154 }
155
156 fn text_ellipsis(mut self) -> Self {
157 self.text_ellipsis = true;
158 self
159 }
160
161 fn single_line(mut self) -> Self {
162 self.single_line = true;
163 self
164 }
165
166 fn buffer_font(mut self, cx: &App) -> Self {
167 self.base = self
168 .base
169 .font(theme::ThemeSettings::get_global(cx).buffer_font.clone());
170 self
171 }
172}
173
174impl ParentElement for LabelLike {
175 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
176 self.children.extend(elements)
177 }
178}
179
180impl RenderOnce for LabelLike {
181 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
182 let mut color = self.color.color(cx);
183 if let Some(alpha) = self.alpha {
184 color.fade_out(1.0 - alpha);
185 }
186
187 self.base
188 .map(|this| match self.size {
189 LabelSize::Large => this.text_ui_lg(cx),
190 LabelSize::Default => this.text_ui(cx),
191 LabelSize::Small => this.text_ui_sm(cx),
192 LabelSize::XSmall => this.text_ui_xs(cx),
193 })
194 .when(self.line_height_style == LineHeightStyle::UiLabel, |this| {
195 this.line_height(relative(1.))
196 })
197 .when(self.italic, |this| this.italic())
198 .when(self.underline, |mut this| {
199 this.text_style()
200 .get_or_insert_with(Default::default)
201 .underline = Some(UnderlineStyle {
202 thickness: px(1.),
203 color: None,
204 wavy: false,
205 });
206 this
207 })
208 .when(self.strikethrough, |this| this.line_through())
209 .when(self.single_line, |this| this.whitespace_nowrap())
210 .when(self.text_ellipsis, |this| {
211 this.overflow_x_hidden().text_ellipsis()
212 })
213 .text_color(color)
214 .font_weight(
215 self.weight
216 .unwrap_or(ThemeSettings::get_global(cx).ui_font.weight),
217 )
218 .children(self.children)
219 }
220}