icon_decoration.rs

  1use std::sync::Arc;
  2
  3use gpui::{Hsla, IntoElement, Point, svg};
  4use strum::{EnumIter, EnumString, IntoStaticStr};
  5
  6use crate::prelude::*;
  7
  8const ICON_DECORATION_SIZE: Pixels = px(11.);
  9
 10/// An icon silhouette used to knockout the background of an element for an icon
 11/// to sit on top of it, emulating a stroke/border.
 12#[derive(Debug, PartialEq, Eq, Copy, Clone, EnumIter, EnumString, IntoStaticStr)]
 13#[strum(serialize_all = "snake_case")]
 14pub enum KnockoutIconName {
 15    XFg,
 16    XBg,
 17    DotFg,
 18    DotBg,
 19    TriangleFg,
 20    TriangleBg,
 21    ArchiveFg,
 22    ArchiveBg,
 23}
 24
 25impl KnockoutIconName {
 26    /// Returns the path to this icon.
 27    pub fn path(&self) -> Arc<str> {
 28        let file_stem: &'static str = self.into();
 29        format!("icons/knockouts/{file_stem}.svg").into()
 30    }
 31}
 32
 33#[derive(Debug, PartialEq, Eq, Copy, Clone, EnumIter, EnumString)]
 34pub enum IconDecorationKind {
 35    X,
 36    Dot,
 37    Triangle,
 38    Archive,
 39}
 40
 41impl IconDecorationKind {
 42    fn fg(&self) -> KnockoutIconName {
 43        match self {
 44            Self::X => KnockoutIconName::XFg,
 45            Self::Dot => KnockoutIconName::DotFg,
 46            Self::Triangle => KnockoutIconName::TriangleFg,
 47            Self::Archive => KnockoutIconName::ArchiveFg,
 48        }
 49    }
 50
 51    fn bg(&self) -> KnockoutIconName {
 52        match self {
 53            Self::X => KnockoutIconName::XBg,
 54            Self::Dot => KnockoutIconName::DotBg,
 55            Self::Triangle => KnockoutIconName::TriangleBg,
 56            Self::Archive => KnockoutIconName::ArchiveBg,
 57        }
 58    }
 59}
 60
 61/// The decoration for an icon.
 62///
 63/// For example, this can show an indicator, an "x", or a diagonal strikethrough
 64/// to indicate something is disabled.
 65#[derive(IntoElement)]
 66pub struct IconDecoration {
 67    kind: IconDecorationKind,
 68    color: Hsla,
 69    knockout_color: Hsla,
 70    knockout_hover_color: Hsla,
 71    size: Pixels,
 72    position: Point<Pixels>,
 73    group_name: Option<SharedString>,
 74}
 75
 76impl IconDecoration {
 77    /// Creates a new [`IconDecoration`].
 78    pub fn new(kind: IconDecorationKind, knockout_color: Hsla, cx: &App) -> Self {
 79        let color = cx.theme().colors().icon;
 80        let position = Point::default();
 81
 82        Self {
 83            kind,
 84            color,
 85            knockout_color,
 86            knockout_hover_color: knockout_color,
 87            size: ICON_DECORATION_SIZE,
 88            position,
 89            group_name: None,
 90        }
 91    }
 92
 93    /// Sets the kind of decoration.
 94    pub fn kind(mut self, kind: IconDecorationKind) -> Self {
 95        self.kind = kind;
 96        self
 97    }
 98
 99    /// Sets the color of the decoration.
100    pub fn color(mut self, color: Hsla) -> Self {
101        self.color = color;
102        self
103    }
104
105    /// Sets the color of the decoration's knockout
106    ///
107    /// Match this to the background of the element the icon will be rendered
108    /// on.
109    pub fn knockout_color(mut self, color: Hsla) -> Self {
110        self.knockout_color = color;
111        self
112    }
113
114    /// Sets the color of the decoration that is used on hover.
115    pub fn knockout_hover_color(mut self, color: Hsla) -> Self {
116        self.knockout_hover_color = color;
117        self
118    }
119
120    /// Sets the position of the decoration.
121    pub fn position(mut self, position: Point<Pixels>) -> Self {
122        self.position = position;
123        self
124    }
125
126    /// Sets the size of the decoration.
127    pub fn size(mut self, size: Pixels) -> Self {
128        self.size = size;
129        self
130    }
131
132    /// Sets the name of the group the decoration belongs to
133    pub fn group_name(mut self, name: Option<SharedString>) -> Self {
134        self.group_name = name;
135        self
136    }
137}
138
139impl RenderOnce for IconDecoration {
140    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
141        let size = self.size;
142
143        let foreground = svg()
144            .absolute()
145            .bottom_0()
146            .right_0()
147            .size(size)
148            .path(self.kind.fg().path())
149            .text_color(self.color);
150
151        let background = svg()
152            .absolute()
153            .bottom_0()
154            .right_0()
155            .size(size)
156            .path(self.kind.bg().path())
157            .text_color(self.knockout_color)
158            .map(|this| match self.group_name {
159                Some(group_name) => this.group_hover(group_name, |style| {
160                    style.text_color(self.knockout_hover_color)
161                }),
162                None => this.hover(|style| style.text_color(self.knockout_hover_color)),
163            });
164
165        div()
166            .size(size)
167            .flex_none()
168            .absolute()
169            .bottom(self.position.y)
170            .right(self.position.x)
171            .child(background)
172            .child(foreground)
173    }
174}