macroquad/ui/widgets/
checkbox.rs

1use crate::{
2    math::{vec2, Rect, Vec2},
3    ui::{ElementState, Id, Layout, Ui, UiContent},
4};
5
6pub struct Checkbox<'a> {
7    id: Id,
8    label: &'a str,
9    ratio: f32,
10    pos: Option<Vec2>,
11    size: Option<Vec2>,
12}
13
14impl<'a> Checkbox<'a> {
15    pub const fn new(id: Id) -> Checkbox<'a> {
16        Checkbox {
17            id,
18            label: "",
19            ratio: 0.5,
20            pos: None,
21            size: None,
22        }
23    }
24
25    pub const fn ratio(self, ratio: f32) -> Self {
26        Self { ratio, ..self }
27    }
28
29    pub const fn label<'b>(self, label: &'b str) -> Checkbox<'b> {
30        Checkbox {
31            id: self.id,
32            label,
33            ratio: self.ratio,
34            pos: self.pos,
35            size: self.size,
36        }
37    }
38
39    pub const fn pos(self, pos: Vec2) -> Self {
40        Self {
41            pos: Some(pos),
42            ..self
43        }
44    }
45
46    pub const fn size(self, size: Vec2) -> Self {
47        Self {
48            size: Some(size),
49            ..self
50        }
51    }
52
53    pub fn ui(self, ui: &mut Ui, data: &mut bool) {
54        let context = ui.get_active_window_context();
55
56        let label_size = context.window.painter.content_with_margins_size(
57            &context.style.label_style,
58            &UiContent::Label(self.label.into()),
59        );
60        let size = self.size.unwrap_or(vec2(
61            context.window.cursor.area.w - context.style.margin * 2. - context.window.cursor.ident,
62            label_size.y.max(22.),
63        ));
64
65        let pos = self
66            .pos
67            .map(|pos| pos + context.window.cursor.fit(size, Layout::Vertical))
68            .unwrap_or_else(|| context.window.cursor.fit(size, Layout::Vertical));
69
70        let whole_area = Vec2::new(
71            if self.label.is_empty() {
72                size.x
73            } else {
74                size.x * self.ratio
75            },
76            size.y,
77        );
78        let checkbox_area = Vec2::new(19., 19.);
79        let checkbox_pos = Vec2::new(
80            pos.x + whole_area.x - 19. - 15.,
81            pos.y + context.style.margin,
82        );
83
84        let hovered = Rect::new(
85            checkbox_pos.x,
86            checkbox_pos.y,
87            checkbox_area.x,
88            checkbox_area.y,
89        )
90        .contains(context.input.mouse_position);
91
92        let background = context
93            .style
94            .checkbox_style
95            .background_sprite(ElementState {
96                focused: context.focused,
97                hovered,
98                clicked: *data,
99                selected: false,
100            });
101
102        let color = context.style.checkbox_style.color(ElementState {
103            focused: context.focused,
104            hovered,
105            clicked: hovered && context.input.is_mouse_down,
106            selected: *data,
107        });
108
109        if let Some(background) = background {
110            let background_margin = context
111                .style
112                .checkbox_style
113                .background_margin
114                .unwrap_or_default();
115
116            context.window.painter.draw_sprite(
117                Rect::new(checkbox_pos.x, checkbox_pos.y, 19., 19.),
118                background,
119                color,
120                Some(background_margin),
121            );
122        } else {
123            context.window.painter.draw_rect(
124                Rect::new(
125                    checkbox_pos.x,
126                    checkbox_pos.y,
127                    checkbox_area.x,
128                    checkbox_area.y,
129                ),
130                None,
131                color,
132            );
133        }
134
135        if hovered && context.input.click_up() {
136            *data ^= true;
137        }
138
139        let context = ui.get_active_window_context();
140
141        if self.label.is_empty() == false {
142            context.window.painter.draw_element_label(
143                &context.style.label_style,
144                Vec2::new(pos.x + size.x * self.ratio, pos.y),
145                self.label,
146                ElementState {
147                    focused: context.focused,
148                    hovered: false,
149                    clicked: false,
150                    selected: false,
151                },
152            );
153        }
154    }
155}
156
157impl Ui {
158    pub fn checkbox(&mut self, id: Id, label: &str, data: &mut bool) {
159        Checkbox::new(id).label(label).ui(self, data);
160    }
161}