1use crate::prelude::*;
2
3use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
4
5/// An element that renders a user avatar with customizable appearance options.
6///
7/// # Examples
8///
9/// ```
10/// use ui::{Avatar, AvatarShape};
11///
12/// Avatar::new("path/to/image.png")
13/// .shape(AvatarShape::Circle)
14/// .grayscale(true)
15/// .border_color(gpui::red());
16/// ```
17#[derive(IntoElement)]
18pub struct Avatar {
19 image: Img,
20 size: Option<AbsoluteLength>,
21 border_color: Option<Hsla>,
22 indicator: Option<AnyElement>,
23}
24
25impl Avatar {
26 /// Creates a new avatar element with the specified image source.
27 pub fn new(src: impl Into<ImageSource>) -> Self {
28 Avatar {
29 image: img(src),
30 size: None,
31 border_color: None,
32 indicator: None,
33 }
34 }
35
36 /// Applies a grayscale filter to the avatar image.
37 ///
38 /// # Examples
39 ///
40 /// ```
41 /// use ui::{Avatar, AvatarShape};
42 ///
43 /// let avatar = Avatar::new("path/to/image.png").grayscale(true);
44 /// ```
45 pub fn grayscale(mut self, grayscale: bool) -> Self {
46 self.image = self.image.grayscale(grayscale);
47 self
48 }
49
50 /// Sets the border color of the avatar.
51 ///
52 /// This might be used to match the border to the background color of
53 /// the parent element to create the illusion of cropping another
54 /// shape underneath (for example in face piles.)
55 pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
56 self.border_color = Some(color.into());
57 self
58 }
59
60 /// Size overrides the avatar size. By default they are 1rem.
61 pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
62 self.size = size.into().map(Into::into);
63 self
64 }
65
66 /// Sets the current indicator to be displayed on the avatar, if any.
67 pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
68 self.indicator = indicator.into().map(IntoElement::into_any_element);
69 self
70 }
71}
72
73impl RenderOnce for Avatar {
74 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
75 let border_width = if self.border_color.is_some() {
76 px(2.)
77 } else {
78 px(0.)
79 };
80
81 let image_size = self.size.unwrap_or_else(|| rems(1.).into());
82 let container_size = image_size.to_pixels(cx.rem_size()) + border_width * 2.;
83
84 div()
85 .size(container_size)
86 .rounded_full()
87 .when_some(self.border_color, |this, color| {
88 this.border(border_width).border_color(color)
89 })
90 .child(
91 self.image
92 .size(image_size)
93 .rounded_full()
94 .bg(cx.theme().colors().ghost_element_background),
95 )
96 .children(self.indicator.map(|indicator| div().child(indicator)))
97 }
98}