Skip to main content

egui_components/
badge.rs

1//! `Badge` widget — small inline pill / chip.
2
3use crate::common::Variant;
4use egui::{vec2, Color32, FontId, Response, Sense, Stroke, Ui, Widget, WidgetText};
5use egui_components_theme::{Theme, ThemeColor};
6
7pub struct Badge {
8    label: WidgetText,
9    variant: Variant,
10    outlined: bool,
11}
12
13impl Badge {
14    pub fn new(label: impl Into<WidgetText>) -> Self {
15        Self {
16            label: label.into(),
17            variant: Variant::Primary,
18            outlined: false,
19        }
20    }
21    pub fn variant(mut self, v: Variant) -> Self {
22        self.variant = v;
23        self
24    }
25    pub fn outlined(mut self) -> Self {
26        self.outlined = true;
27        self
28    }
29}
30
31impl Widget for Badge {
32    fn ui(self, ui: &mut Ui) -> Response {
33        let theme = Theme::get(ui.ctx());
34        let m = theme.metrics;
35        let pad_x = 8.0;
36        let pad_y = 2.0;
37        let font = FontId::proportional(m.font_size_xs);
38        let galley = self.label.into_galley(
39            ui,
40            Some(egui::TextWrapMode::Extend),
41            f32::INFINITY,
42            font,
43        );
44        let desired = vec2(galley.size().x + pad_x * 2.0, galley.size().y + pad_y * 2.0);
45        let (rect, response) = ui.allocate_exact_size(desired, Sense::hover());
46
47        if ui.is_rect_visible(rect) {
48            let (bg, fg) = variant_colors(&theme.colors, self.variant);
49            let radius = egui::CornerRadius::same((desired.y * 0.5) as u8);
50            let painter = ui.painter();
51
52            if self.outlined {
53                painter.rect(
54                    rect,
55                    radius,
56                    Color32::TRANSPARENT,
57                    Stroke::new(1.0, bg),
58                    egui::StrokeKind::Inside,
59                );
60                painter.galley_with_override_text_color(
61                    rect.center() - galley.size() * 0.5,
62                    galley,
63                    bg,
64                );
65            } else {
66                painter.rect_filled(rect, radius, bg);
67                painter.galley_with_override_text_color(
68                    rect.center() - galley.size() * 0.5,
69                    galley,
70                    fg,
71                );
72            }
73        }
74
75        response
76    }
77}
78
79fn variant_colors(c: &ThemeColor, v: Variant) -> (Color32, Color32) {
80    match v {
81        Variant::Primary => (c.primary_background, c.primary_foreground),
82        Variant::Secondary => (c.secondary_background, c.secondary_foreground),
83        Variant::Ghost => (c.muted_background, c.muted_foreground),
84        Variant::Outline => (c.foreground, c.background),
85        Variant::Link => (c.link_foreground, c.background),
86        Variant::Danger => (c.danger_background, c.danger_foreground),
87        Variant::Success => (c.success_background, c.success_foreground),
88        Variant::Warning => (c.warning_background, c.warning_foreground),
89        Variant::Info => (c.info_background, c.info_foreground),
90    }
91}