1use egui::{
5 Color32, CornerRadius, FontSelection, Response, Sense, Stroke, Ui, Vec2, Widget, WidgetInfo,
6 WidgetText, WidgetType,
7};
8
9use crate::theme::{with_alpha, Theme};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub enum BadgeTone {
14 Ok,
16 Warning,
18 Danger,
20 Info,
22 Neutral,
24}
25
26impl BadgeTone {
27 fn colours(self, theme: &Theme) -> (Color32, Color32) {
28 let p = &theme.palette;
29 match self {
30 BadgeTone::Ok => (with_alpha(p.green, 64), p.success),
31 BadgeTone::Warning => (with_alpha(p.amber, 64), p.warning),
32 BadgeTone::Danger => (with_alpha(p.red, 64), p.danger),
33 BadgeTone::Info => (with_alpha(p.sky, 64), p.sky),
34 BadgeTone::Neutral => (with_alpha(p.text_muted, 40), p.text_muted),
35 }
36 }
37}
38
39#[must_use = "Add with `ui.add(...)`."]
48pub struct Badge {
49 text: WidgetText,
50 tone: BadgeTone,
51}
52
53impl std::fmt::Debug for Badge {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("Badge")
56 .field("text", &self.text.text())
57 .field("tone", &self.tone)
58 .finish()
59 }
60}
61
62impl Badge {
63 pub fn new(text: impl Into<WidgetText>, tone: BadgeTone) -> Self {
65 Self {
66 text: text.into(),
67 tone,
68 }
69 }
70}
71
72impl Widget for Badge {
73 fn ui(self, ui: &mut Ui) -> Response {
74 let theme = Theme::current(ui.ctx());
75 let t = &theme.typography;
76 let (bg, fg) = self.tone.colours(&theme);
77
78 let font = egui::FontId::proportional(t.small);
79 let galley = egui::WidgetText::from(
80 egui::RichText::new(self.text.text().to_uppercase())
81 .color(fg)
82 .size(t.small)
83 .strong(),
84 )
85 .into_galley(
86 ui,
87 Some(egui::TextWrapMode::Extend),
88 f32::INFINITY,
89 FontSelection::FontId(font),
90 );
91
92 let pad = Vec2::new(9.0, 3.0);
93 let desired = galley.size() + pad * 2.0;
94 let (rect, response) = ui.allocate_exact_size(desired, Sense::hover());
95
96 if ui.is_rect_visible(rect) {
97 ui.painter().rect(
98 rect,
99 CornerRadius::same(99),
100 bg,
101 Stroke::NONE,
102 egui::StrokeKind::Inside,
103 );
104 let text_pos = egui::pos2(rect.min.x + pad.x, rect.center().y - galley.size().y * 0.5);
105 ui.painter().galley(text_pos, galley, fg);
106 }
107
108 response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, true, self.text.text()));
109 response
110 }
111}