1use gpui::{Animation, AnimationExt, FontWeight, pulsating_between};
2use std::time::Duration;
3use ui::prelude::*;
4
5#[derive(IntoElement)]
6pub struct AnimatedLabel {
7 base: Label,
8 text: SharedString,
9}
10
11impl AnimatedLabel {
12 pub fn new(text: impl Into<SharedString>) -> Self {
13 let text = text.into();
14 AnimatedLabel {
15 base: Label::new(text.clone()),
16 text,
17 }
18 }
19}
20
21impl LabelCommon for AnimatedLabel {
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
78impl RenderOnce for AnimatedLabel {
79 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
80 let text = self.text.clone();
81
82 self.base
83 .color(Color::Muted)
84 .with_animations(
85 "animated-label",
86 vec![
87 Animation::new(Duration::from_secs(1)),
88 Animation::new(Duration::from_secs(1)).repeat(),
89 ],
90 move |mut label, animation_ix, delta| {
91 match animation_ix {
92 0 => {
93 let chars_to_show = (delta * text.len() as f32).ceil() as usize;
94 let text = SharedString::from(text[0..chars_to_show].to_string());
95 label.set_text(text);
96 }
97 1 => match delta {
98 d if d < 0.25 => label.set_text(text.clone()),
99 d if d < 0.5 => label.set_text(format!("{}.", text)),
100 d if d < 0.75 => label.set_text(format!("{}..", text)),
101 _ => label.set_text(format!("{}...", text)),
102 },
103 _ => {}
104 }
105 label
106 },
107 )
108 .with_animation(
109 "pulsating-label",
110 Animation::new(Duration::from_secs(2))
111 .repeat()
112 .with_easing(pulsating_between(0.6, 1.)),
113 |label, delta| label.map_element(|label| label.alpha(delta)),
114 )
115 }
116}