Skip to main content

libdqg/
titlebar.rs

1use crate::renderer::DrawPass;
2use crate::types::Color;
3
4pub const TITLEBAR_HEIGHT: f32 = 32.0;
5const BUTTON_WIDTH: f32 = 46.0;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum TitleBarButton {
9    Minimize,
10    Maximize,
11    Close,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum TitleBarHit {
16    None,
17    Drag,
18    Button(TitleBarButton),
19}
20
21pub struct TitleBar {
22    pub hovered: Option<TitleBarButton>,
23    resizable: bool,
24}
25
26impl TitleBar {
27    pub fn new(resizable: bool) -> Self {
28        Self { hovered: None, resizable }
29    }
30
31    fn button_rect(button: TitleBarButton, screen_w: f32) -> (f32, f32, f32, f32) {
32        let index_from_right = match button {
33            TitleBarButton::Close => 0.0,
34            TitleBarButton::Maximize => 1.0,
35            TitleBarButton::Minimize => 2.0,
36        };
37        let x = screen_w - BUTTON_WIDTH * (index_from_right + 1.0);
38        (x, 0.0, BUTTON_WIDTH, TITLEBAR_HEIGHT)
39    }
40
41    pub fn hit_test(&self, x: f32, y: f32, screen_w: f32) -> TitleBarHit {
42        if y < 0.0 || y > TITLEBAR_HEIGHT {
43            return TitleBarHit::None;
44        }
45        for &button in &[TitleBarButton::Close, TitleBarButton::Maximize, TitleBarButton::Minimize] {
46            if button == TitleBarButton::Maximize && !self.resizable {
47                continue;
48            }
49            let (bx, by, bw, bh) = Self::button_rect(button, screen_w);
50            if x >= bx && x < bx + bw && y >= by && y < by + bh {
51                return TitleBarHit::Button(button);
52            }
53        }
54        TitleBarHit::Drag
55    }
56
57    pub fn update_hover(&mut self, x: f32, y: f32, screen_w: f32) {
58        self.hovered = match self.hit_test(x, y, screen_w) {
59            TitleBarHit::Button(button) => Some(button),
60            _ => None,
61        };
62    }
63
64    pub fn render(&self, pass: &mut DrawPass, screen_w: u32, maximized: bool) {
65        let w = screen_w as f32;
66
67        for &button in &[TitleBarButton::Minimize, TitleBarButton::Maximize, TitleBarButton::Close] {
68            let (bx, by, bw, bh) = Self::button_rect(button, w);
69
70            if self.hovered == Some(button) {
71                let bg = if button == TitleBarButton::Close {
72                    Color::from_hex(0xff0000)
73                } else {
74                    Color::new(1.0, 1.0, 1.0, 0.15)
75                };
76                pass.draw_rect(bx, by, bw, bh, 0.0, bg);
77            }
78
79            let cx = bx + bw * 0.5;
80            let cy = by + bh * 0.5;
81            let icon_color = if button == TitleBarButton::Maximize && !self.resizable {
82                Color::new(1.0, 1.0, 1.0, 0.35)
83            } else {
84                Color::new(1.0, 1.0, 1.0, 1.0)
85            };
86
87            match button {
88                TitleBarButton::Minimize => {
89                    pass.draw_line(cx - 5.0, cy, cx + 5.0, cy, 1.0, icon_color);
90                }
91                TitleBarButton::Maximize => {
92                    if maximized {
93                        pass.draw_rect(cx - 4.0, cy - 5.0, 8.0, 8.0, 1.0, icon_color);
94                        pass.draw_rect(cx - 6.0, cy - 3.0, 8.0, 8.0, 1.0, icon_color);
95                    } else {
96                        pass.draw_rect(cx - 5.0, cy - 5.0, 10.0, 10.0, 1.0, icon_color);
97                    }
98                }
99                TitleBarButton::Close => {
100                    pass.draw_line(cx - 5.0, cy - 5.0, cx + 5.0, cy + 5.0, 1.0, icon_color);
101                    pass.draw_line(cx - 5.0, cy + 5.0, cx + 5.0, cy - 5.0, 1.0, icon_color);
102                }
103            }
104        }
105    }
106}