Skip to main content

pfx_editor_style/widgets/
keycap.rs

1use egui::{Align2, CornerRadius, Response, Sense, Stroke, StrokeKind, Ui, Vec2, Widget};
2
3use super::State;
4use crate::theme::Theme;
5
6pub struct KeyCap<'a> {
7    key: &'a str,
8    lit: bool,
9    button: bool,
10}
11
12impl<'a> KeyCap<'a> {
13    pub fn new(key: &'a str) -> KeyCap<'a> {
14        KeyCap {
15            key,
16            lit: false,
17            button: false,
18        }
19    }
20
21    pub fn lit(mut self, lit: bool) -> KeyCap<'a> {
22        self.lit = lit;
23        self
24    }
25
26    pub fn button(mut self) -> KeyCap<'a> {
27        self.button = true;
28        self
29    }
30}
31
32impl Widget for KeyCap<'_> {
33    fn ui(self, ui: &mut Ui) -> Response {
34        let theme = Theme::of(ui.ctx());
35        let key_font = theme.font(theme.size_code);
36        let width = ui
37            .painter()
38            .layout_no_wrap(self.key.into(), key_font.clone(), theme.text)
39            .size()
40            .x;
41        let size = Vec2::new((width + 10.0).round().max(18.0), 17.0);
42        let sense = if self.button {
43            Sense::click()
44        } else {
45            Sense::hover()
46        };
47        let (rect, response) = ui.allocate_exact_size(size, sense);
48        if !ui.is_rect_visible(rect) {
49            return response;
50        }
51        let state = if self.button {
52            State::of(&response, self.lit)
53        } else if self.lit {
54            State::Active
55        } else {
56            State::Rest
57        };
58        let (fill, border, text) = match state {
59            State::Rest if !ui.is_enabled() => (theme.surface, theme.line, theme.dim),
60            State::Rest => (theme.surface, theme.line, theme.text),
61            State::Hovered => (theme.accent_deep, theme.accent, theme.text),
62            State::Active => (theme.accent_bright, theme.accent_bright, theme.on_accent),
63        };
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.hline(
73            rect.x_range().shrink(1.0),
74            rect.bottom() - 1.5,
75            Stroke::new(
76                theme.stroke,
77                if state == State::Active {
78                    theme.accent_bright
79                } else {
80                    border
81                },
82            ),
83        );
84        painter.text(
85            rect.center() - Vec2::new(0.0, 0.5),
86            Align2::CENTER_CENTER,
87            self.key,
88            key_font,
89            text,
90        );
91        response
92    }
93}