avatar.rs

  1use crate::prelude::*;
  2
  3use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
  4
  5/// The shape of an [`Avatar`].
  6#[derive(Debug, Default, PartialEq, Clone)]
  7pub enum AvatarShape {
  8    /// The avatar is shown in a circle.
  9    #[default]
 10    Circle,
 11    /// The avatar is shown in a rectangle with rounded corners.
 12    RoundedRectangle,
 13}
 14
 15/// An element that renders a user avatar with customizable appearance options.
 16///
 17/// # Examples
 18///
 19/// ```
 20/// use ui::{Avatar, AvatarShape};
 21///
 22/// Avatar::new("path/to/image.png")
 23///     .shape(AvatarShape::Circle)
 24///     .grayscale(true)
 25///     .border_color(gpui::red());
 26/// ```
 27#[derive(IntoElement)]
 28pub struct Avatar {
 29    image: Img,
 30    size: Option<AbsoluteLength>,
 31    border_color: Option<Hsla>,
 32    indicator: Option<AnyElement>,
 33}
 34
 35impl Avatar {
 36    pub fn new(src: impl Into<ImageSource>) -> Self {
 37        Avatar {
 38            image: img(src),
 39            size: None,
 40            border_color: None,
 41            indicator: None,
 42        }
 43    }
 44
 45    /// Sets the shape of the avatar image.
 46    ///
 47    /// This method allows the shape of the avatar to be specified using an [`AvatarShape`].
 48    /// It modifies the corner radius of the image to match the specified shape.
 49    ///
 50    /// # Examples
 51    ///
 52    /// ```
 53    /// use ui::{Avatar, AvatarShape};
 54    ///
 55    /// Avatar::new("path/to/image.png").shape(AvatarShape::Circle);
 56    /// ```
 57    pub fn shape(mut self, shape: AvatarShape) -> Self {
 58        self.image = match shape {
 59            AvatarShape::Circle => self.image.rounded_full(),
 60            AvatarShape::RoundedRectangle => self.image.rounded_md(),
 61        };
 62        self
 63    }
 64
 65    /// Applies a grayscale filter to the avatar image.
 66    ///
 67    /// # Examples
 68    ///
 69    /// ```
 70    /// use ui::{Avatar, AvatarShape};
 71    ///
 72    /// let avatar = Avatar::new("path/to/image.png").grayscale(true);
 73    /// ```
 74    pub fn grayscale(mut self, grayscale: bool) -> Self {
 75        self.image = self.image.grayscale(grayscale);
 76        self
 77    }
 78
 79    pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
 80        self.border_color = Some(color.into());
 81        self
 82    }
 83
 84    /// Size overrides the avatar size. By default they are 1rem.
 85    pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
 86        self.size = size.into().map(Into::into);
 87        self
 88    }
 89
 90    pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
 91        self.indicator = indicator.into().map(IntoElement::into_any_element);
 92        self
 93    }
 94}
 95
 96impl RenderOnce for Avatar {
 97    fn render(mut self, cx: &mut WindowContext) -> impl IntoElement {
 98        if self.image.style().corner_radii.top_left.is_none() {
 99            self = self.shape(AvatarShape::Circle);
100        }
101
102        let border_width = if self.border_color.is_some() {
103            px(2.)
104        } else {
105            px(0.)
106        };
107
108        let image_size = self.size.unwrap_or_else(|| rems(1.).into());
109        let container_size = image_size.to_pixels(cx.rem_size()) + border_width * 2.;
110
111        div()
112            .size(container_size)
113            .map(|mut div| {
114                div.style().corner_radii = self.image.style().corner_radii.clone();
115                div
116            })
117            .when_some(self.border_color, |this, color| {
118                this.border(border_width).border_color(color)
119            })
120            .child(
121                self.image
122                    .size(image_size)
123                    .bg(cx.theme().colors().ghost_element_background),
124            )
125            .children(self.indicator.map(|indicator| div().child(indicator)))
126    }
127}