1pub struct Checkbox {
2 pub pos_x: usize,
3 pub pos_y: usize,
4 pub size: usize,
5
6 pub checked: bool,
7 anim_t: f32,
8 pub anim_speed: f32,
9
10 pub box_color: crate::color::Color,
11 pub box_color_checked: crate::color::Color,
12 pub check_color: crate::color::Color,
13 pub border_color: crate::color::Color,
14 pub border_size: usize,
15 pub radius: usize,
16
17 pub label: Option<crate::ui::text::Text>,
18 pub label_size: f32,
19 pub label_color: crate::color::Color,
20 pub label_gap: usize,
21
22 lmb_was_down: bool,
23 toggled_this_frame: bool,
24}
25
26impl Default for Checkbox {
27 fn default() -> Self {
28 Self {
29 pos_x: 0,
30 pos_y: 0,
31 size: 20,
32 checked: false,
33 anim_t: 0.0,
34 anim_speed: 0.15,
35 box_color: crate::color::Color::new(40, 40, 50),
36 box_color_checked: crate::color::Color::new(108, 92, 231),
37 check_color: crate::color::Color::new(255, 255, 255),
38 border_color: crate::color::Color::new(100, 100, 120),
39 border_size: 1,
40 radius: 4,
41 label: None,
42 label_size: 14.0,
43 label_color: crate::color::Color::new(200, 200, 210),
44 label_gap: 8,
45 lmb_was_down: false,
46 toggled_this_frame: false,
47 }
48 }
49}
50
51impl Checkbox {
52 pub fn position(mut self, x: usize, y: usize) -> Self {
53 self.pos_x = x;
54 self.pos_y = y;
55 self
56 }
57
58 pub fn size(mut self, size: usize) -> Self {
59 self.size = size;
60 self
61 }
62
63 pub fn default_checked(mut self, checked: bool) -> Self {
64 self.checked = checked;
65 self.anim_t = if checked { 1.0 } else { 0.0 };
66 self
67 }
68
69 pub fn box_color(mut self, color: crate::color::Color) -> Self {
70 self.box_color = color;
71 self
72 }
73
74 pub fn box_color_checked(mut self, color: crate::color::Color) -> Self {
75 self.box_color_checked = color;
76 self
77 }
78
79 pub fn check_color(mut self, color: crate::color::Color) -> Self {
80 self.check_color = color;
81 self
82 }
83
84 pub fn border_color(mut self, color: crate::color::Color) -> Self {
85 self.border_color = color;
86 self
87 }
88
89 pub fn border(mut self, size: usize) -> Self {
90 self.border_size = size;
91 self
92 }
93
94 pub fn radius(mut self, radius: usize) -> Self {
95 self.radius = radius;
96 self
97 }
98
99 pub fn label(mut self, text: &str, font: crate::ttf::Font, size: f32) -> Self {
100 self.label = Some(crate::ui::text::Text::new(text, font));
101 self.label_size = size;
102 self
103 }
104
105 pub fn label_color(mut self, color: crate::color::Color) -> Self {
106 self.label_color = color;
107 self
108 }
109
110 pub fn label_gap(mut self, gap: usize) -> Self {
111 self.label_gap = gap;
112 self
113 }
114
115 pub fn is_checked(&self) -> bool {
116 self.checked
117 }
118
119 pub fn set_checked(&mut self, checked: bool) {
120 self.checked = checked;
121 }
122
123 pub fn just_toggled(&self) -> bool {
124 self.toggled_this_frame
125 }
126
127 fn update(&mut self, window: &mut crate::window::Window) {
128 self.toggled_this_frame = false;
129
130 let mouse = window.get_mouse_state();
131 let lmb = mouse.lmb_clicked;
132 let lmb_just = lmb && !self.lmb_was_down;
133
134 if lmb_just {
135 let mx = mouse.pos_x;
136 let my = mouse.pos_y;
137 let hit_w = self.size + self.label.as_ref().map_or(0, |l| {
138 self.label_gap + l.get_width(self.label_size)
139 });
140 let in_bounds = mx >= self.pos_x as f32
141 && mx < (self.pos_x + hit_w) as f32
142 && my >= self.pos_y as f32
143 && my < (self.pos_y + self.size) as f32;
144 if in_bounds {
145 self.checked = !self.checked;
146 self.toggled_this_frame = true;
147 }
148 }
149
150 self.lmb_was_down = lmb;
151
152 let target = if self.checked { 1.0 } else { 0.0 };
153 let diff = target - self.anim_t;
154 if diff.abs() > 0.001 {
155 self.anim_t += diff.signum() * self.anim_speed;
156 self.anim_t = self.anim_t.clamp(0.0, 1.0);
157 } else {
158 self.anim_t = target;
159 }
160 }
161
162 pub fn draw(&mut self, window: &mut crate::window::Window) {
163 self.update(window);
164
165 let bg = self.box_color.lerp(&self.box_color_checked, self.anim_t);
166
167 window.draw_rect_f(
169 self.pos_x, self.pos_y, self.size, self.size,
170 self.radius, &self.border_color, 0,
171 );
172
173 let inner_x = self.pos_x + self.border_size;
175 let inner_y = self.pos_y + self.border_size;
176 let inner_s = self.size.saturating_sub(self.border_size * 2);
177 let inner_r = self.radius.saturating_sub(self.border_size);
178 window.draw_rect_f(inner_x, inner_y, inner_s, inner_s, inner_r, &bg, 0);
179
180 if self.anim_t > 0.1 {
182 let s = self.size as f32;
183 let ox = self.pos_x as f32;
184 let oy = self.pos_y as f32;
185
186 let p1 = (ox + s * 0.22, oy + s * 0.50);
188 let p2 = (ox + s * 0.42, oy + s * 0.72);
189 let p3 = (ox + s * 0.78, oy + s * 0.30);
190
191 let th = (s * 0.12).max(1.5) as usize;
192 let alpha_color = crate::color::Color::rgba(
193 self.check_color.r, self.check_color.g, self.check_color.b,
194 (self.anim_t * 255.0) as u8,
195 );
196
197 window.draw_line(p1.0 as isize, p1.1 as isize, p2.0 as isize, p2.1 as isize, th, alpha_color);
198 window.draw_line(p2.0 as isize, p2.1 as isize, p3.0 as isize, p3.1 as isize, th, alpha_color);
199 }
200
201 if let Some(ref label) = self.label {
203 let label_x = self.pos_x + self.size + self.label_gap;
204 let label_y = self.pos_y as f32 + (self.size as f32 - self.label_size) / 2.0;
205 window.draw_text(label_x, label_y as usize, label, self.label_size, &self.label_color);
206 }
207 }
208}