1use crate::prelude::*;
2use gpui::{Animation, AnimationExt, FontWeight, pulsating_between};
3use std::time::Duration;
4
5#[derive(IntoElement)]
6pub struct LoadingLabel {
7 base: Label,
8 text: SharedString,
9}
10
11impl LoadingLabel {
12 pub fn new(text: impl Into<SharedString>) -> Self {
13 let text = text.into();
14 LoadingLabel {
15 base: Label::new(text.clone()),
16 text,
17 }
18 }
19}
20
21impl LabelCommon for LoadingLabel {
22 fn size(mut self, size: LabelSize) -> Self {
23 self.base = self.base.size(size);
24 self
25 }
26
27 fn weight(mut self, weight: FontWeight) -> Self {
28 self.base = self.base.weight(weight);
29 self
30 }
31
32 fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
33 self.base = self.base.line_height_style(line_height_style);
34 self
35 }
36
37 fn color(mut self, color: Color) -> Self {
38 self.base = self.base.color(color);
39 self
40 }
41
42 fn strikethrough(mut self) -> Self {
43 self.base = self.base.strikethrough();
44 self
45 }
46
47 fn italic(mut self) -> Self {
48 self.base = self.base.italic();
49 self
50 }
51
52 fn alpha(mut self, alpha: f32) -> Self {
53 self.base = self.base.alpha(alpha);
54 self
55 }
56
57 fn underline(mut self) -> Self {
58 self.base = self.base.underline();
59 self
60 }
61
62 fn truncate(mut self) -> Self {
63 self.base = self.base.truncate();
64 self
65 }
66
67 fn single_line(mut self) -> Self {
68 self.base = self.base.single_line();
69 self
70 }
71
72 fn buffer_font(mut self, cx: &App) -> Self {
73 self.base = self.base.buffer_font(cx);
74 self
75 }
76
77 fn inline_code(mut self, cx: &App) -> Self {
78 self.base = self.base.inline_code(cx);
79 self
80 }
81}
82
83impl RenderOnce for LoadingLabel {
84 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
85 let text = self.text.clone();
86
87 self.base
88 .color(Color::Muted)
89 .with_animations(
90 "loading_label",
91 vec![
92 Animation::new(Duration::from_secs(1)),
93 Animation::new(Duration::from_secs(1)).repeat(),
94 ],
95 move |mut label, animation_ix, delta| {
96 match animation_ix {
97 0 => {
98 let chars_to_show = (delta * text.len() as f32).ceil() as usize;
99 let text = SharedString::from(text[0..chars_to_show].to_string());
100 label.set_text(text);
101 }
102 1 => match delta {
103 d if d < 0.25 => label.set_text(text.clone()),
104 d if d < 0.5 => label.set_text(format!("{}.", text)),
105 d if d < 0.75 => label.set_text(format!("{}..", text)),
106 _ => label.set_text(format!("{}...", text)),
107 },
108 _ => {}
109 }
110 label
111 },
112 )
113 .with_animation(
114 "pulsating-label",
115 Animation::new(Duration::from_secs(2))
116 .repeat()
117 .with_easing(pulsating_between(0.6, 1.)),
118 |label, delta| label.map_element(|label| label.alpha(delta)),
119 )
120 }
121}