label.rs

  1use gpui2::{Hsla, WindowContext};
  2use smallvec::SmallVec;
  3
  4use crate::prelude::*;
  5use crate::theme::theme;
  6
  7#[derive(Default, PartialEq, Copy, Clone)]
  8pub enum LabelColor {
  9    #[default]
 10    Default,
 11    Muted,
 12    Created,
 13    Modified,
 14    Deleted,
 15    Disabled,
 16    Hidden,
 17    Placeholder,
 18    Accent,
 19}
 20
 21impl LabelColor {
 22    pub fn hsla(&self, cx: &WindowContext) -> Hsla {
 23        let theme = theme(cx);
 24
 25        match self {
 26            Self::Default => theme.middle.base.default.foreground,
 27            Self::Muted => theme.middle.variant.default.foreground,
 28            Self::Created => theme.middle.positive.default.foreground,
 29            Self::Modified => theme.middle.warning.default.foreground,
 30            Self::Deleted => theme.middle.negative.default.foreground,
 31            Self::Disabled => theme.middle.base.disabled.foreground,
 32            Self::Hidden => theme.middle.variant.default.foreground,
 33            Self::Placeholder => theme.middle.base.disabled.foreground,
 34            Self::Accent => theme.middle.accent.default.foreground,
 35        }
 36    }
 37}
 38
 39#[derive(Default, PartialEq, Copy, Clone)]
 40pub enum LabelSize {
 41    #[default]
 42    Default,
 43    Small,
 44}
 45
 46#[derive(Element, Clone)]
 47pub struct Label {
 48    label: String,
 49    color: LabelColor,
 50    size: LabelSize,
 51    highlight_indices: Vec<usize>,
 52    strikethrough: bool,
 53}
 54
 55impl Label {
 56    pub fn new<L>(label: L) -> Self
 57    where
 58        L: Into<String>,
 59    {
 60        Self {
 61            label: label.into(),
 62            color: LabelColor::Default,
 63            size: LabelSize::Default,
 64            highlight_indices: Vec::new(),
 65            strikethrough: false,
 66        }
 67    }
 68
 69    pub fn color(mut self, color: LabelColor) -> Self {
 70        self.color = color;
 71        self
 72    }
 73
 74    pub fn size(mut self, size: LabelSize) -> Self {
 75        self.size = size;
 76        self
 77    }
 78
 79    pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
 80        self.highlight_indices = indices;
 81        self
 82    }
 83
 84    pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
 85        self.strikethrough = strikethrough;
 86        self
 87    }
 88
 89    fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
 90        let theme = theme(cx);
 91
 92        let highlight_color = theme.lowest.accent.default.foreground;
 93
 94        let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
 95
 96        let mut runs: SmallVec<[Run; 8]> = SmallVec::new();
 97
 98        for (char_ix, char) in self.label.char_indices() {
 99            let mut color = self.color.hsla(cx);
100
101            if let Some(highlight_ix) = highlight_indices.peek() {
102                if char_ix == *highlight_ix {
103                    color = highlight_color;
104
105                    highlight_indices.next();
106                }
107            }
108
109            let last_run = runs.last_mut();
110
111            let start_new_run = if let Some(last_run) = last_run {
112                if color == last_run.color {
113                    last_run.text.push(char);
114                    false
115                } else {
116                    true
117                }
118            } else {
119                true
120            };
121
122            if start_new_run {
123                runs.push(Run {
124                    text: char.to_string(),
125                    color,
126                });
127            }
128        }
129
130        div()
131            .flex()
132            .when(self.strikethrough, |this| {
133                this.relative().child(
134                    div()
135                        .absolute()
136                        .top_px()
137                        .my_auto()
138                        .w_full()
139                        .h_px()
140                        .fill(LabelColor::Hidden.hsla(cx)),
141                )
142            })
143            .children(runs.into_iter().map(|run| {
144                let mut div = div();
145
146                if self.size == LabelSize::Small {
147                    div = div.text_xs();
148                } else {
149                    div = div.text_sm();
150                }
151
152                div.text_color(run.color).child(run.text)
153            }))
154    }
155}
156
157/// A run of text that receives the same style.
158struct Run {
159    pub text: String,
160    pub color: Hsla,
161}