1use std::ops::Range;
2
3use crate::prelude::*;
4use crate::styled_ext::StyledExt;
5use gpui::{relative, Div, HighlightStyle, IntoElement, StyledText, WindowContext};
6
7#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
8pub enum LabelSize {
9 #[default]
10 Default,
11 Small,
12}
13
14#[derive(Default, PartialEq, Copy, Clone)]
15pub enum LineHeightStyle {
16 #[default]
17 TextLabel,
18 /// Sets the line height to 1
19 UILabel,
20}
21
22#[derive(IntoElement, Clone)]
23pub struct Label {
24 label: SharedString,
25 size: LabelSize,
26 line_height_style: LineHeightStyle,
27 color: Color,
28 strikethrough: bool,
29}
30
31impl RenderOnce for Label {
32 type Rendered = Div;
33
34 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
35 div()
36 .when(self.strikethrough, |this| {
37 this.relative().child(
38 div()
39 .absolute()
40 .top_1_2()
41 .w_full()
42 .h_px()
43 .bg(Color::Hidden.color(cx)),
44 )
45 })
46 .map(|this| match self.size {
47 LabelSize::Default => this.text_ui(),
48 LabelSize::Small => this.text_ui_sm(),
49 })
50 .when(self.line_height_style == LineHeightStyle::UILabel, |this| {
51 this.line_height(relative(1.))
52 })
53 .text_color(self.color.color(cx))
54 .child(self.label.clone())
55 }
56}
57
58impl Label {
59 pub fn new(label: impl Into<SharedString>) -> Self {
60 Self {
61 label: label.into(),
62 size: LabelSize::Default,
63 line_height_style: LineHeightStyle::default(),
64 color: Color::Default,
65 strikethrough: false,
66 }
67 }
68
69 pub fn size(mut self, size: LabelSize) -> Self {
70 self.size = size;
71 self
72 }
73
74 pub fn color(mut self, color: Color) -> Self {
75 self.color = color;
76 self
77 }
78
79 pub fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
80 self.line_height_style = line_height_style;
81 self
82 }
83
84 pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
85 self.strikethrough = strikethrough;
86 self
87 }
88}
89
90#[derive(IntoElement)]
91pub struct HighlightedLabel {
92 label: SharedString,
93 size: LabelSize,
94 color: Color,
95 highlight_indices: Vec<usize>,
96 strikethrough: bool,
97}
98
99impl RenderOnce for HighlightedLabel {
100 type Rendered = Div;
101
102 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
103 let highlight_color = cx.theme().colors().text_accent;
104
105 let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
106 let mut highlights: Vec<(Range<usize>, HighlightStyle)> = Vec::new();
107
108 while let Some(start_ix) = highlight_indices.next() {
109 let mut end_ix = start_ix;
110
111 loop {
112 end_ix = end_ix + self.label[end_ix..].chars().next().unwrap().len_utf8();
113 if let Some(&next_ix) = highlight_indices.peek() {
114 if next_ix == end_ix {
115 end_ix = next_ix;
116 highlight_indices.next();
117 continue;
118 }
119 }
120 break;
121 }
122
123 highlights.push((
124 start_ix..end_ix,
125 HighlightStyle {
126 color: Some(highlight_color),
127 ..Default::default()
128 },
129 ));
130 }
131
132 let mut text_style = cx.text_style().clone();
133 text_style.color = self.color.color(cx);
134
135 div()
136 .flex()
137 .when(self.strikethrough, |this| {
138 this.relative().child(
139 div()
140 .absolute()
141 .top_px()
142 .my_auto()
143 .w_full()
144 .h_px()
145 .bg(Color::Hidden.color(cx)),
146 )
147 })
148 .map(|this| match self.size {
149 LabelSize::Default => this.text_ui(),
150 LabelSize::Small => this.text_ui_sm(),
151 })
152 .child(StyledText::new(self.label).with_highlights(&text_style, highlights))
153 }
154}
155
156impl HighlightedLabel {
157 /// shows a label with the given characters highlighted.
158 /// characters are identified by utf8 byte position.
159 pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
160 Self {
161 label: label.into(),
162 size: LabelSize::Default,
163 color: Color::Default,
164 highlight_indices,
165 strikethrough: false,
166 }
167 }
168
169 pub fn size(mut self, size: LabelSize) -> Self {
170 self.size = size;
171 self
172 }
173
174 pub fn color(mut self, color: Color) -> Self {
175 self.color = color;
176 self
177 }
178
179 pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
180 self.strikethrough = strikethrough;
181 self
182 }
183}