1use egui::{
4 vec2, CornerRadius, Margin, Response, Stroke, Ui, Widget, WidgetInfo, WidgetText, WidgetType,
5};
6
7use crate::{
8 indicator::{Indicator, IndicatorState},
9 theme::Theme,
10};
11
12#[derive(Default)]
26#[must_use = "Add with `ui.add(...)`."]
27pub struct StatusPill {
28 items: Vec<(WidgetText, IndicatorState)>,
29}
30
31impl std::fmt::Debug for StatusPill {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("StatusPill")
34 .field(
35 "items",
36 &self
37 .items
38 .iter()
39 .map(|(l, s)| (l.text().to_string(), *s))
40 .collect::<Vec<_>>(),
41 )
42 .finish()
43 }
44}
45
46impl StatusPill {
47 pub fn new() -> Self {
49 Self { items: Vec::new() }
50 }
51
52 pub fn item(mut self, label: impl Into<WidgetText>, state: IndicatorState) -> Self {
54 self.items.push((label.into(), state));
55 self
56 }
57}
58
59impl Widget for StatusPill {
60 fn ui(self, ui: &mut Ui) -> Response {
61 let theme = Theme::current(ui.ctx());
62 let p = &theme.palette;
63 let t = &theme.typography;
64
65 let frame = egui::Frame::new()
66 .fill(p.card)
67 .stroke(Stroke::new(1.0, p.border))
68 .corner_radius(CornerRadius::same(99))
69 .inner_margin(Margin::symmetric(12, 4));
70
71 let response = frame
72 .show(ui, |ui| {
73 ui.horizontal(|ui| {
74 ui.spacing_mut().item_spacing = vec2(10.0, 0.0);
75 for (i, (label, state)) in self.items.iter().enumerate() {
76 if i > 0 {
77 ui.add_space(4.0);
78 }
79 ui.add(Indicator::new(*state));
80 let rt = egui::RichText::new(label.text())
81 .color(p.text_faint)
82 .size(t.small);
83 ui.add(egui::Label::new(rt).wrap_mode(egui::TextWrapMode::Extend));
84 }
85 });
86 })
87 .response;
88
89 response.widget_info(|| WidgetInfo::labeled(WidgetType::Other, true, "status pill"));
90 response
91 }
92}