pfx_editor_style/widgets/
pill.rs1use egui::{Align2, Color32, CornerRadius, Response, Sense, Stroke, StrokeKind, Ui, Vec2, Widget};
2
3use super::{place, shaped};
4use crate::theme::Theme;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum Tone {
8 Neutral,
9 Ok,
10 Busy,
11 Danger,
12}
13
14impl Tone {
15 fn colours(self, theme: &Theme) -> (Color32, Color32) {
16 match self {
17 Tone::Neutral => (theme.surface, theme.text2),
18 Tone::Ok => (theme.ok_tint, theme.ok),
19 Tone::Busy => (theme.hot_dim, theme.hot),
20 Tone::Danger => (theme.error_tint, theme.error),
21 }
22 }
23}
24
25pub struct Pill<'a> {
26 text: &'a str,
27 tone: Tone,
28 active: bool,
29}
30
31impl<'a> Pill<'a> {
32 pub fn new(text: &'a str, tone: Tone) -> Pill<'a> {
33 Pill {
34 text,
35 tone,
36 active: false,
37 }
38 }
39
40 pub fn active(mut self, active: bool) -> Pill<'a> {
41 self.active = active;
42 self
43 }
44}
45
46impl Widget for Pill<'_> {
47 fn ui(self, ui: &mut Ui) -> Response {
48 let theme = Theme::of(ui.ctx());
49 let (fill, ink) = self.tone.colours(&theme);
50 let galley = shaped(
51 ui.painter(),
52 self.text,
53 theme.font(theme.size_code),
54 ink,
55 theme.labels,
56 );
57 let width = galley.size().x;
58 let size = Vec2::new((width + 22.0).round(), 16.0);
59 let (rect, response) = ui.allocate_exact_size(size, Sense::hover());
60 if !ui.is_rect_visible(rect) {
61 return response;
62 }
63 let border = if self.active { ink } else { fill };
64 let painter = ui.painter();
65 painter.rect(
66 rect,
67 CornerRadius::same(theme.radius),
68 fill,
69 Stroke::new(theme.stroke, border),
70 StrokeKind::Inside,
71 );
72 painter.circle_filled(rect.left_center() + Vec2::new(8.0, 0.0), 2.5, ink);
73 place(
74 painter,
75 rect.left_center() + Vec2::new(15.0, -0.5),
76 Align2::LEFT_CENTER,
77 galley,
78 ink,
79 );
80 response
81 }
82}