avatar.rs

 1use crate::prelude::*;
 2use gpui::{img, Div, Hsla, ImageData, ImageSource, Img, IntoElement, Styled};
 3use std::sync::Arc;
 4
 5#[derive(Debug, Default, PartialEq, Clone)]
 6pub enum Shape {
 7    #[default]
 8    Circle,
 9    RoundedRectangle,
10}
11
12#[derive(IntoElement)]
13pub struct Avatar {
14    image: Img,
15    border_color: Option<Hsla>,
16    is_available: Option<bool>,
17}
18
19impl RenderOnce for Avatar {
20    type Rendered = Div;
21
22    fn render(mut self, cx: &mut WindowContext) -> Self::Rendered {
23        if self.image.style().corner_radii.top_left.is_none() {
24            self = self.shape(Shape::Circle);
25        }
26
27        let size = cx.rem_size();
28
29        div()
30            .size(size + px(2.))
31            .map(|mut div| {
32                div.style().corner_radii = self.image.style().corner_radii.clone();
33                div
34            })
35            .when_some(self.border_color, |this, color| {
36                this.border().border_color(color)
37            })
38            .child(
39                self.image
40                    .size(size)
41                    // todo!(Pull the avatar fallback background from the theme.)
42                    .bg(gpui::red()),
43            )
44            .children(self.is_available.map(|is_free| {
45                // HACK: non-integer sizes result in oval indicators.
46                let indicator_size = (size * 0.4).round();
47
48                div()
49                    .absolute()
50                    .z_index(1)
51                    .bg(if is_free { gpui::green() } else { gpui::red() })
52                    .size(indicator_size)
53                    .rounded(indicator_size)
54                    .bottom_0()
55                    .right_0()
56            }))
57    }
58}
59
60impl Avatar {
61    pub fn uri(src: impl Into<SharedString>) -> Self {
62        Self::source(src.into().into())
63    }
64
65    pub fn data(src: Arc<ImageData>) -> Self {
66        Self::source(src.into())
67    }
68
69    pub fn source(src: ImageSource) -> Self {
70        Self {
71            image: img(src),
72            is_available: None,
73            border_color: None,
74        }
75    }
76
77    pub fn shape(mut self, shape: Shape) -> Self {
78        self.image = match shape {
79            Shape::Circle => self.image.rounded_full(),
80            Shape::RoundedRectangle => self.image.rounded_md(),
81        };
82        self
83    }
84
85    pub fn grayscale(mut self, grayscale: bool) -> Self {
86        self.image = self.image.grayscale(grayscale);
87        self
88    }
89
90    pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
91        self.border_color = Some(color.into());
92        self
93    }
94
95    pub fn availability_indicator(mut self, is_available: impl Into<Option<bool>>) -> Self {
96        self.is_available = is_available.into();
97        self
98    }
99}