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