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 div()
133 .flex()
134 .when(self.strikethrough, |this| {
135 this.relative().child(
136 div()
137 .absolute()
138 .top_px()
139 .my_auto()
140 .w_full()
141 .h_px()
142 .bg(Color::Hidden.color(cx)),
143 )
144 })
145 .map(|this| match self.size {
146 LabelSize::Default => this.text_ui(),
147 LabelSize::Small => this.text_ui_sm(),
148 })
149 .child(StyledText::new(self.label).with_highlights(&cx.text_style(), highlights))
150 }
151}
152
153impl HighlightedLabel {
154 /// shows a label with the given characters highlighted.
155 /// characters are identified by utf8 byte position.
156 pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
157 Self {
158 label: label.into(),
159 size: LabelSize::Default,
160 color: Color::Default,
161 highlight_indices,
162 strikethrough: false,
163 }
164 }
165
166 pub fn size(mut self, size: LabelSize) -> Self {
167 self.size = size;
168 self
169 }
170
171 pub fn color(mut self, color: Color) -> Self {
172 self.color = color;
173 self
174 }
175
176 pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
177 self.strikethrough = strikethrough;
178 self
179 }
180}