macroquad/ui/widgets/
checkbox.rs1use 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}