Skip to repository content353 lines · 12.3 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:47:05.438Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
label_like.rs
1use crate::prelude::*;
2use gpui::{FontWeight, Rems, StyleRefinement, UnderlineStyle};
3use smallvec::SmallVec;
4
5/// Sets the size of a label
6#[derive(Debug, PartialEq, Clone, Copy, Default)]
7pub enum LabelSize {
8 /// The default size of a label.
9 #[default]
10 Default,
11 /// The large size of a label.
12 Large,
13 /// The small size of a label.
14 Small,
15 /// The extra small size of a label.
16 XSmall,
17 /// An arbitrary custom size specified in rems.
18 Custom(Rems),
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 (`…`) at the end 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 /// Styles the label to look like inline code.
69 fn inline_code(self, cx: &App) -> Self;
70}
71
72/// A label-like element that can be used to create a custom label when
73/// prebuilt labels are not sufficient. Use this sparingly, as it is
74/// unconstrained and may make the UI feel less consistent.
75///
76/// This is also used to build the prebuilt labels.
77#[derive(IntoElement)]
78pub struct LabelLike {
79 pub(super) base: Div,
80 size: LabelSize,
81 weight: Option<FontWeight>,
82 line_height_style: LineHeightStyle,
83 pub(crate) color: Color,
84 strikethrough: bool,
85 italic: bool,
86 children: SmallVec<[AnyElement; 2]>,
87 alpha: Option<f32>,
88 underline: bool,
89 single_line: bool,
90 truncate: bool,
91 truncate_start: bool,
92 truncate_middle: bool,
93}
94
95impl Default for LabelLike {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101impl LabelLike {
102 /// Creates a new, fully custom label.
103 /// Prefer using [`Label`] or [`HighlightedLabel`] where possible.
104 pub fn new() -> Self {
105 Self {
106 base: div(),
107 size: LabelSize::Default,
108 weight: None,
109 line_height_style: LineHeightStyle::default(),
110 color: Color::Default,
111 strikethrough: false,
112 italic: false,
113 children: SmallVec::new(),
114 alpha: None,
115 underline: false,
116 single_line: false,
117 truncate: false,
118 truncate_start: false,
119 truncate_middle: false,
120 }
121 }
122}
123
124// Style methods.
125impl LabelLike {
126 fn style(&mut self) -> &mut StyleRefinement {
127 self.base.style()
128 }
129
130 gpui::margin_style_methods!({
131 visibility: pub
132 });
133
134 /// Truncates overflowing text with an ellipsis (`…`) at the start if needed.
135 pub fn truncate_start(mut self) -> Self {
136 self.truncate_start = true;
137 self
138 }
139
140 /// Truncates overflowing text with an ellipsis (`…`) in the middle if needed.
141 /// Preserves the start and end of the text. Useful for filenames.
142 pub fn truncate_middle(mut self) -> Self {
143 self.truncate_middle = true;
144 self
145 }
146
147 /// Wraps the text and truncates it with an ellipsis (`…`) at the end of
148 /// the last visible line if it exceeds the given number of lines.
149 pub fn line_clamp(mut self, lines: usize) -> Self {
150 // `line_clamp` alone hard-cuts the text; the ellipsis on the last
151 // visible line is only rendered when a text overflow style is set.
152 self.base = self.base.line_clamp(lines).text_ellipsis();
153 self
154 }
155}
156
157impl LabelCommon for LabelLike {
158 fn size(mut self, size: LabelSize) -> Self {
159 self.size = size;
160 self
161 }
162
163 fn weight(mut self, weight: FontWeight) -> Self {
164 self.weight = Some(weight);
165 self
166 }
167
168 fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
169 self.line_height_style = line_height_style;
170 self
171 }
172
173 fn color(mut self, color: Color) -> Self {
174 self.color = color;
175 self
176 }
177
178 fn strikethrough(mut self) -> Self {
179 self.strikethrough = true;
180 self
181 }
182
183 fn italic(mut self) -> Self {
184 self.italic = true;
185 self
186 }
187
188 fn underline(mut self) -> Self {
189 self.underline = true;
190 self
191 }
192
193 fn alpha(mut self, alpha: f32) -> Self {
194 self.alpha = Some(alpha);
195 self
196 }
197
198 /// Truncates overflowing text with an ellipsis (`…`) at the end if needed.
199 fn truncate(mut self) -> Self {
200 self.truncate = true;
201 self
202 }
203
204 fn single_line(mut self) -> Self {
205 self.single_line = true;
206 self
207 }
208
209 fn buffer_font(mut self, cx: &App) -> Self {
210 let font = theme::theme_settings(cx).buffer_font(cx).clone();
211 self.weight = Some(font.weight);
212 self.base = self.base.font(font);
213 self
214 }
215
216 fn inline_code(mut self, cx: &App) -> Self {
217 self.base = self
218 .base
219 .font(theme::theme_settings(cx).buffer_font(cx).clone())
220 .bg(cx.theme().colors().element_background)
221 .rounded_sm()
222 .px_0p5();
223 self
224 }
225}
226
227impl ParentElement for LabelLike {
228 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
229 self.children.extend(elements)
230 }
231}
232
233impl RenderOnce for LabelLike {
234 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
235 let mut color = self.color.color(cx);
236 if let Some(alpha) = self.alpha {
237 color.fade_out(1.0 - alpha);
238 }
239
240 self.base
241 .map(|this| match self.size {
242 LabelSize::Large => this.text_ui_lg(cx),
243 LabelSize::Default => this.text_ui(cx),
244 LabelSize::Small => this.text_ui_sm(cx),
245 LabelSize::XSmall => this.text_ui_xs(cx),
246 LabelSize::Custom(size) => this.text_size(size),
247 })
248 .when(self.line_height_style == LineHeightStyle::UiLabel, |this| {
249 this.line_height(relative(1.))
250 })
251 .when(self.italic, |this| this.italic())
252 .when(self.underline, |mut this| {
253 this.text_style().underline = Some(UnderlineStyle {
254 thickness: px(1.),
255 color: Some(cx.theme().colors().text_muted.opacity(0.4)),
256 wavy: false,
257 });
258 this
259 })
260 .when(self.strikethrough, |this| this.line_through())
261 .when(self.single_line, |this| this.whitespace_nowrap())
262 .when(self.truncate, |this| {
263 this.min_w_0()
264 .overflow_x_hidden()
265 .whitespace_nowrap()
266 .text_ellipsis()
267 })
268 .when(self.truncate_start, |this| {
269 this.min_w_0()
270 .overflow_x_hidden()
271 .whitespace_nowrap()
272 .text_ellipsis_start()
273 })
274 .when(self.truncate_middle, |this| {
275 this.min_w_0()
276 .overflow_x_hidden()
277 .whitespace_nowrap()
278 .text_ellipsis_middle()
279 })
280 .text_color(color)
281 .font_weight(
282 self.weight
283 .unwrap_or(theme::theme_settings(cx).ui_font(cx).weight),
284 )
285 .children(self.children)
286 }
287}
288
289impl Component for LabelLike {
290 fn scope() -> ComponentScope {
291 ComponentScope::Typography
292 }
293
294 fn name() -> &'static str {
295 "LabelLike"
296 }
297
298 fn description() -> &'static str {
299 "A flexible, customizable label-like component \
300 that serves as a base for other label types."
301 }
302
303 fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
304 v_flex()
305 .gap_6()
306 .children(vec![
307 example_group_with_title(
308 "Sizes",
309 vec![
310 single_example("Default", LabelLike::new().child("Default size").into_any_element()),
311 single_example("Large", LabelLike::new().size(LabelSize::Large).child("Large size").into_any_element()),
312 single_example("Small", LabelLike::new().size(LabelSize::Small).child("Small size").into_any_element()),
313 single_example("XSmall", LabelLike::new().size(LabelSize::XSmall).child("Extra small size").into_any_element()),
314 ],
315 ),
316 example_group_with_title(
317 "Styles",
318 vec![
319 single_example("Bold", LabelLike::new().weight(FontWeight::BOLD).child("Bold text").into_any_element()),
320 single_example("Italic", LabelLike::new().italic().child("Italic text").into_any_element()),
321 single_example("Underline", LabelLike::new().underline().child("Underlined text").into_any_element()),
322 single_example("Strikethrough", LabelLike::new().strikethrough().child("Strikethrough text").into_any_element()),
323 single_example("Inline Code", LabelLike::new().inline_code(cx).child("const value = 42;").into_any_element()),
324 ],
325 ),
326 example_group_with_title(
327 "Colors",
328 vec![
329 single_example("Default", LabelLike::new().child("Default color").into_any_element()),
330 single_example("Accent", LabelLike::new().color(Color::Accent).child("Accent color").into_any_element()),
331 single_example("Error", LabelLike::new().color(Color::Error).child("Error color").into_any_element()),
332 single_example("Alpha", LabelLike::new().alpha(0.5).child("50% opacity").into_any_element()),
333 ],
334 ),
335 example_group_with_title(
336 "Line Height",
337 vec![
338 single_example("Default", LabelLike::new().child("Default line height\nMulti-line text").into_any_element()),
339 single_example("UI Label", LabelLike::new().line_height_style(LineHeightStyle::UiLabel).child("UI label line height\nMulti-line text").into_any_element()),
340 ],
341 ),
342 example_group_with_title(
343 "Special Cases",
344 vec![
345 single_example("Single Line", LabelLike::new().single_line().child("This is a very long text that should be displayed in a single line").into_any_element()),
346 single_example("Truncate", LabelLike::new().truncate().child("This is a very long text that should be truncated with an ellipsis").into_any_element()),
347 ],
348 ),
349 ])
350 .into_any_element()
351 }
352}
353