avatar.rs

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