Skip to main content

pfx_editor_style/widgets/
header.rs

1use egui::{Align2, CornerRadius, Id, Rect, Response, Sense, Stroke, StrokeKind, Ui, Vec2, Widget};
2
3use super::{State, place, shaped};
4use crate::theme::Theme;
5
6pub struct Header<'a> {
7    code: &'a str,
8    title: &'a str,
9    info: Option<&'a str>,
10    focused: bool,
11    width: Option<f32>,
12    id: Option<Id>,
13}
14
15impl<'a> Header<'a> {
16    pub fn new(code: &'a str, title: &'a str) -> Header<'a> {
17        Header {
18            code,
19            title,
20            info: None,
21            focused: false,
22            width: None,
23            id: None,
24        }
25    }
26
27    pub fn info(mut self, info: &'a str) -> Header<'a> {
28        self.info = Some(info);
29        self
30    }
31
32    pub fn width(mut self, width: f32) -> Header<'a> {
33        self.width = Some(width);
34        self
35    }
36
37    pub fn id(mut self, id: Id) -> Header<'a> {
38        self.id = Some(id);
39        self
40    }
41
42    pub fn focused(mut self, focused: bool) -> Header<'a> {
43        self.focused = focused;
44        self
45    }
46}
47
48impl Widget for Header<'_> {
49    fn ui(self, ui: &mut Ui) -> Response {
50        let theme = Theme::of(ui.ctx());
51        let size = Vec2::new(
52            self.width.unwrap_or_else(|| ui.available_width()),
53            theme.row_height + 2.0,
54        );
55        let (rect, response) = match self.id {
56            Some(id) => {
57                let (rect, _) = ui.allocate_exact_size(size, Sense::hover());
58                (rect, ui.interact(rect, id, Sense::click()))
59            }
60            None => ui.allocate_exact_size(size, Sense::click()),
61        };
62        if !ui.is_rect_visible(rect) {
63            return response;
64        }
65        let state = State::of(&response, self.focused);
66        let painter = ui.painter();
67        let code_font = theme.font(theme.size_code);
68        let code_width = painter
69            .layout_no_wrap(self.code.into(), code_font.clone(), theme.dim)
70            .size()
71            .x;
72        let code_rect = Rect::from_min_size(
73            rect.left_center() - Vec2::new(0.0, 8.0),
74            Vec2::new((code_width + 8.0).round(), 16.0),
75        );
76        let (code_fill, code_border, code_text, title_text) = match state {
77            State::Rest => (theme.bg, theme.line, theme.dim, theme.text2),
78            State::Hovered => (theme.bg, theme.accent, theme.text2, theme.text),
79            State::Active => (theme.hot, theme.hot, theme.on_accent, theme.text),
80        };
81        painter.rect(
82            code_rect,
83            CornerRadius::same(theme.radius),
84            code_fill,
85            Stroke::new(theme.stroke, code_border),
86            StrokeKind::Inside,
87        );
88        painter.text(
89            code_rect.center(),
90            Align2::CENTER_CENTER,
91            self.code,
92            code_font,
93            code_text,
94        );
95        let title = shaped(
96            painter,
97            self.title,
98            theme.title_font(theme.size_small),
99            title_text,
100            theme.titles,
101        );
102        place(
103            painter,
104            code_rect.right_center() + Vec2::new(8.0, 0.0),
105            Align2::LEFT_CENTER,
106            title,
107            title_text,
108        );
109        if let Some(info) = self.info {
110            let info = shaped(
111                painter,
112                info,
113                theme.font(theme.size_code),
114                theme.muted,
115                theme.labels,
116            );
117            place(
118                painter,
119                rect.right_center(),
120                Align2::RIGHT_CENTER,
121                info,
122                theme.muted,
123            );
124        }
125        response
126    }
127}