Skip to main content

guise/
indicator.rs

1//! `Indicator` — a dot or count badge overlaid on a child element.
2
3use gpui::prelude::*;
4use gpui::{div, px, AnyElement, App, FontWeight, IntoElement, SharedString, Window};
5
6use crate::theme::{theme, ColorName};
7
8/// A corner indicator over any child. The Mantine `Indicator`.
9#[derive(IntoElement)]
10pub struct Indicator {
11    child: AnyElement,
12    label: Option<SharedString>,
13    color: ColorName,
14    disabled: bool,
15}
16
17impl Indicator {
18    pub fn new(child: impl IntoElement) -> Self {
19        Indicator {
20            child: child.into_any_element(),
21            label: None,
22            color: ColorName::Red,
23            disabled: false,
24        }
25    }
26
27    /// Show a count/text instead of a plain dot.
28    pub fn label(mut self, label: impl Into<SharedString>) -> Self {
29        self.label = Some(label.into());
30        self
31    }
32
33    pub fn color(mut self, color: ColorName) -> Self {
34        self.color = color;
35        self
36    }
37
38    /// Hide the indicator (but keep the child).
39    pub fn disabled(mut self, disabled: bool) -> Self {
40        self.disabled = disabled;
41        self
42    }
43}
44
45impl RenderOnce for Indicator {
46    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
47        let t = theme(cx);
48        let accent = t.color(self.color, t.primary_shade());
49        let fg = accent.contrasting().hsla();
50        let bg = accent.hsla();
51
52        let mut root = div().relative().child(self.child);
53        if !self.disabled {
54            let dot = match self.label {
55                Some(label) => div()
56                    .absolute()
57                    .top(px(-4.0))
58                    .right(px(-4.0))
59                    .h(px(16.0))
60                    .min_w(px(16.0))
61                    .px(px(4.0))
62                    .flex()
63                    .items_center()
64                    .justify_center()
65                    .rounded(px(16.0))
66                    .bg(bg)
67                    .text_color(fg)
68                    .text_size(px(10.0))
69                    .font_weight(FontWeight::BOLD)
70                    .child(label),
71                None => div()
72                    .absolute()
73                    .top(px(-2.0))
74                    .right(px(-2.0))
75                    .w(px(10.0))
76                    .h(px(10.0))
77                    .rounded(px(10.0))
78                    .bg(bg),
79            };
80            root = root.child(dot);
81        }
82        root
83    }
84}