gpui_component/
spinner.rs

1use std::time::Duration;
2
3use crate::{Icon, IconName, Sizable, Size};
4use gpui::{
5    div, ease_in_out, percentage, prelude::FluentBuilder as _, Animation, AnimationExt as _, App,
6    Hsla, IntoElement, ParentElement, RenderOnce, Styled as _, Transformation, Window,
7};
8
9/// A cycling loading spinner.
10#[derive(IntoElement)]
11pub struct Spinner {
12    size: Size,
13    icon: Icon,
14    speed: Duration,
15    color: Option<Hsla>,
16}
17
18impl Spinner {
19    /// Create a new loading spinner.
20    pub fn new() -> Self {
21        Self {
22            size: Size::Medium,
23            speed: Duration::from_secs_f64(0.8),
24            icon: Icon::new(IconName::Loader),
25            color: None,
26        }
27    }
28
29    /// Set specified icon for the spinner.
30    ///
31    /// Default is [`IconName::Loader`].
32    ///
33    /// Please ensure the icon used is suitable for a loading spinner.
34    pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
35        self.icon = icon.into();
36        self
37    }
38
39    /// Set the icon color.
40    pub fn color(mut self, color: Hsla) -> Self {
41        self.color = Some(color);
42        self
43    }
44}
45
46impl Sizable for Spinner {
47    fn with_size(mut self, size: impl Into<Size>) -> Self {
48        self.size = size.into();
49        self
50    }
51}
52
53impl RenderOnce for Spinner {
54    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
55        div()
56            .child(
57                self.icon
58                    .with_size(self.size)
59                    .when_some(self.color, |this, color| this.text_color(color))
60                    .with_animation(
61                        "circle",
62                        Animation::new(self.speed).repeat().with_easing(ease_in_out),
63                        |this, delta| this.transform(Transformation::rotate(percentage(delta))),
64                    ),
65            )
66            .into_element()
67    }
68}