Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:47:05.438Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

label_like.rs

353 lines · 12.3 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.