use gpui::{ App, Hsla, IntoElement, ParentElement as _, RenderOnce, SharedString, StyleRefinement, Styled as _, Window, }; use gpui_native_theme::{ActiveTheme as _, Size, Theme}; use crate::common::{Sizable, StyledExt, h_flex}; /// How loud a badge is. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum BadgeVariant { /// The default; quiet muted fill for counts. #[default] Neutral, /// The accent ramp; something active or newly added. Accent, /// The success ramp. Success, /// The warning ramp. Warning, /// The destructive ramp. Danger, } impl BadgeVariant { fn tokens(self, theme: &Theme) -> (Hsla, Hsla) { match self { Self::Neutral => (theme.colors.muted, theme.colors.muted_foreground), Self::Accent => (theme.ramps.primary.base, theme.colors.primary_foreground), Self::Success => (theme.ramps.success.base, theme.colors.success_foreground), Self::Warning => (theme.ramps.warning.base, theme.colors.warning_foreground), Self::Danger => ( theme.ramps.destructive.base, theme.colors.destructive_foreground, ), } } } /// A small pill for a count or a state. #[derive(IntoElement)] pub struct Badge { label: SharedString, variant: BadgeVariant, size: Size, style: StyleRefinement, } impl Badge { /// Creates a badge showing `label` — a count or state word the caller formats. pub fn new(label: L) -> Self where L: Into, { Self { label: label.into(), variant: BadgeVariant::default(), size: Size::default(), style: StyleRefinement::default(), } } /// Sets how loud the badge is. pub fn variant(mut self, variant: BadgeVariant) -> Self { self.variant = variant; self } } impl Sizable for Badge { fn apply_size(&mut self, size: Size) { self.size = size; } } impl gpui::Styled for Badge { fn style(&mut self) -> &mut gpui::StyleRefinement { &mut self.style } } impl RenderOnce for Badge { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let theme = cx.theme(); let metrics = theme.sizes.resolve(self.size); let label_size = metrics.caption_size; let (background, foreground) = self.variant.tokens(theme); h_flex() .flex_none() .justify_center() .rounded(theme.radius.full) .bg(background) .text_color(foreground) .text_size(label_size) .px(metrics.dense_padding_x) .py_0p5() .child(self.label) .refine_style(&self.style) } }