icon_decoration.rs

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