1use gpui2::{Hsla, WindowContext};
2use smallvec::SmallVec;
3
4use crate::prelude::*;
5use crate::theme::theme;
6
7#[derive(Default, PartialEq, Copy, Clone)]
8pub enum LabelColor {
9 #[default]
10 Default,
11 Muted,
12 Created,
13 Modified,
14 Deleted,
15 Disabled,
16 Hidden,
17 Placeholder,
18 Accent,
19}
20
21impl LabelColor {
22 pub fn hsla(&self, cx: &WindowContext) -> Hsla {
23 let theme = theme(cx);
24
25 match self {
26 Self::Default => theme.middle.base.default.foreground,
27 Self::Muted => theme.middle.variant.default.foreground,
28 Self::Created => theme.middle.positive.default.foreground,
29 Self::Modified => theme.middle.warning.default.foreground,
30 Self::Deleted => theme.middle.negative.default.foreground,
31 Self::Disabled => theme.middle.base.disabled.foreground,
32 Self::Hidden => theme.middle.variant.default.foreground,
33 Self::Placeholder => theme.middle.base.disabled.foreground,
34 Self::Accent => theme.middle.accent.default.foreground,
35 }
36 }
37}
38
39#[derive(Default, PartialEq, Copy, Clone)]
40pub enum LabelSize {
41 #[default]
42 Default,
43 Small,
44}
45
46#[derive(Element, Clone)]
47pub struct Label {
48 label: String,
49 color: LabelColor,
50 size: LabelSize,
51 highlight_indices: Vec<usize>,
52 strikethrough: bool,
53}
54
55impl Label {
56 pub fn new<L>(label: L) -> Self
57 where
58 L: Into<String>,
59 {
60 Self {
61 label: label.into(),
62 color: LabelColor::Default,
63 size: LabelSize::Default,
64 highlight_indices: Vec::new(),
65 strikethrough: false,
66 }
67 }
68
69 pub fn color(mut self, color: LabelColor) -> Self {
70 self.color = color;
71 self
72 }
73
74 pub fn size(mut self, size: LabelSize) -> Self {
75 self.size = size;
76 self
77 }
78
79 pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
80 self.highlight_indices = indices;
81 self
82 }
83
84 pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
85 self.strikethrough = strikethrough;
86 self
87 }
88
89 fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
90 let theme = theme(cx);
91
92 let highlight_color = theme.lowest.accent.default.foreground;
93
94 let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
95
96 let mut runs: SmallVec<[Run; 8]> = SmallVec::new();
97
98 for (char_ix, char) in self.label.char_indices() {
99 let mut color = self.color.hsla(cx);
100
101 if let Some(highlight_ix) = highlight_indices.peek() {
102 if char_ix == *highlight_ix {
103 color = highlight_color;
104
105 highlight_indices.next();
106 }
107 }
108
109 let last_run = runs.last_mut();
110
111 let start_new_run = if let Some(last_run) = last_run {
112 if color == last_run.color {
113 last_run.text.push(char);
114 false
115 } else {
116 true
117 }
118 } else {
119 true
120 };
121
122 if start_new_run {
123 runs.push(Run {
124 text: char.to_string(),
125 color,
126 });
127 }
128 }
129
130 div()
131 .flex()
132 .when(self.strikethrough, |this| {
133 this.relative().child(
134 div()
135 .absolute()
136 .top_px()
137 .my_auto()
138 .w_full()
139 .h_px()
140 .fill(LabelColor::Hidden.hsla(cx)),
141 )
142 })
143 .children(runs.into_iter().map(|run| {
144 let mut div = div();
145
146 if self.size == LabelSize::Small {
147 div = div.text_xs();
148 } else {
149 div = div.text_sm();
150 }
151
152 div.text_color(run.color).child(run.text)
153 }))
154 }
155}
156
157/// A run of text that receives the same style.
158struct Run {
159 pub text: String,
160 pub color: Hsla,
161}