1use crate::render::callbacks::CallbackRegistry;
17use crate::render::widget::*;
18use crate::render::widget_cache::WidgetContainer;
19use crate::render::widget_config::*;
20use crate::render::{
21 make_points, make_size, Points, Size, POINT_X, POINT_Y, SIZE_HEIGHT, SIZE_WIDTH,
22};
23
24use sdl2::render::{Canvas, Texture};
25use sdl2::video::Window;
26
27use crate::render::canvas_helper::CanvasHelper;
28use crate::render::layout_cache::LayoutContainer;
29use crate::render::texture_cache::TextureCache;
30use crate::render::texture_store::TextureStore;
31use crate::render::widget_config::CompassPosition::Center;
32use crate::widgets::image_widget::ImageWidget;
33use crate::widgets::text_widget::{TextJustify, TextWidget};
34use sdl2::pixels::Color;
35use sdl2::rect::Rect;
36use std::any::Any;
37use std::collections::HashMap;
38
39pub type OnToggleCallbackType =
42 Option<Box<dyn FnMut(&mut CheckboxWidget, &[WidgetContainer], &[LayoutContainer], bool)>>;
43
44pub struct CheckboxWidget {
46 config: WidgetConfig,
47 system_properties: HashMap<i32, String>,
48 callback_registry: CallbackRegistry,
49 texture_store: TextureStore,
50 text_widget: TextWidget,
51 unchecked_widget: ImageWidget,
52 checked_widget: ImageWidget,
53 active: bool,
54 selected: bool,
55 in_bounds: bool,
56 originated: bool,
57 on_toggle: OnToggleCallbackType,
58}
59
60impl CheckboxWidget {
63 pub fn new(points: Points, size: Size, text: String, font_size: i32, selected: bool) -> Self {
67 let mut text_widget = TextWidget::new(
68 String::from("assets/OpenSans-Regular.ttf"),
69 sdl2::ttf::FontStyle::NORMAL,
70 font_size,
71 TextJustify::Left,
72 text,
73 make_points(
74 points[POINT_X] + size[SIZE_HEIGHT] as i32 + 6,
75 points[POINT_Y] + 2,
76 ),
77 make_size(
78 size[SIZE_WIDTH] - size[SIZE_HEIGHT] - 10,
79 size[SIZE_HEIGHT] - 4,
80 ),
81 );
82
83 let mut config = WidgetConfig::new(points.clone(), size.clone());
84 let mut unchecked_widget = ImageWidget::new(
85 String::from("assets/checkbox_unselected.png"),
86 make_points(points[POINT_X] + 2, points[POINT_Y] + 2),
87 make_size(size[SIZE_HEIGHT] - 4, size[SIZE_HEIGHT] - 4),
88 true,
89 );
90 let mut checked_widget = ImageWidget::new(
91 String::from("assets/checkbox_selected.png"),
92 make_points(points[POINT_X] + 2, points[POINT_Y] + 2),
93 make_size(size[SIZE_HEIGHT] - 4, size[SIZE_HEIGHT] - 4),
94 true,
95 );
96
97 text_widget.set_color(CONFIG_COLOR_TEXT, Color::RGB(0, 0, 0));
98 unchecked_widget.set_compass(CONFIG_IMAGE_POSITION, Center);
99 checked_widget.set_compass(CONFIG_IMAGE_POSITION, Center);
100
101 config.set_toggle(CONFIG_SELECTED_STATE, selected);
102
103 Self {
104 config,
105 system_properties: HashMap::new(),
106 callback_registry: CallbackRegistry::new(),
107 texture_store: TextureStore::default(),
108 text_widget,
109 unchecked_widget,
110 checked_widget,
111 active: false,
112 selected,
113 in_bounds: false,
114 originated: false,
115 on_toggle: None,
116 }
117 }
118
119 pub fn on_toggle<F>(&mut self, callback: F)
121 where
122 F: FnMut(&mut CheckboxWidget, &[WidgetContainer], &[LayoutContainer], bool) + 'static,
123 {
124 self.on_toggle = Some(Box::new(callback));
125 }
126
127 fn call_toggle_callback(&mut self, widgets: &[WidgetContainer], layouts: &[LayoutContainer]) {
129 if let Some(mut cb) = self.on_toggle.take() {
130 cb(self, widgets, layouts, self.selected);
131 self.on_toggle = Some(cb);
132 }
133 }
134}
135
136impl CanvasHelper for CheckboxWidget {}
137
138impl Widget for CheckboxWidget {
140 fn draw(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) -> Option<&Texture> {
142 if self.get_config().invalidated() {
143 let bounds = self.get_config().get_size(CONFIG_SIZE);
144 let base_color = self.get_color(CONFIG_COLOR_BASE);
145 let border_color = self.get_config().get_color(CONFIG_COLOR_BORDER);
146
147 self.texture_store
148 .create_or_resize_texture(c, bounds[0] as u32, bounds[1] as u32);
149
150 let checkbox_widget_texture = if self.active {
153 if self.in_bounds {
154 if self.selected {
155 self.unchecked_widget.draw(c, t).unwrap()
156 } else {
157 self.checked_widget.draw(c, t).unwrap()
158 }
159 } else if self.selected {
160 self.checked_widget.draw(c, t).unwrap()
161 } else {
162 self.unchecked_widget.draw(c, t).unwrap()
163 }
164 } else if self.selected {
165 self.checked_widget.draw(c, t).unwrap()
166 } else {
167 self.unchecked_widget.draw(c, t).unwrap()
168 };
169
170 let text_widget_texture = self.text_widget.draw(c, t).unwrap();
171
172 c.with_texture_canvas(self.texture_store.get_mut_ref(), |texture| {
173 texture.set_draw_color(base_color);
174 texture.clear();
175
176 texture
177 .copy(
178 text_widget_texture,
179 None,
180 Rect::new(
181 2 + bounds[1] as i32 + 6,
182 0,
183 bounds[0] - bounds[1],
184 bounds[1] - 4,
185 ),
186 )
187 .unwrap();
188
189 texture
190 .copy(
191 checkbox_widget_texture,
192 None,
193 Rect::new(2, 2, bounds[1] - 4, bounds[1] - 4),
194 )
195 .unwrap();
196
197 texture.set_draw_color(border_color);
198 texture
199 .draw_rect(Rect::new(0, 0, bounds[0], bounds[1]))
200 .unwrap();
201 })
202 .unwrap();
203 }
204
205 self.texture_store.get_optional_ref()
206 }
207
208 fn mouse_entered(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
210 self.in_bounds = true;
211 self.mouse_entered_callback(_widgets, _layouts);
212 self.get_config().set_invalidated(true);
213 }
214
215 fn mouse_exited(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
217 self.in_bounds = false;
218 self.mouse_exited_callback(_widgets, _layouts);
219 self.get_config().set_invalidated(true);
220 }
221
222 fn button_clicked(
224 &mut self,
225 _widgets: &[WidgetContainer],
226 _layouts: &[LayoutContainer],
227 _button: u8,
228 _clicks: u8,
229 _state: bool,
230 ) {
231 if _button == 1 {
232 if _state {
233 self.active = true;
234 self.originated = true;
235 } else {
236 self.active = false;
237
238 if self.in_bounds && self.originated {
239 self.selected = !self.selected;
240 self.set_toggle(CONFIG_SELECTED_STATE, self.selected);
241 self.call_toggle_callback(_widgets, _layouts);
242 }
243
244 self.originated = false;
245 }
246
247 self.get_config().set_invalidated(true);
248 }
249
250 self.button_clicked_callback(_widgets, _layouts, _button, _clicks, _state);
251 }
252
253 default_widget_functions!();
254 default_widget_properties!();
255 default_widget_callbacks!();
256}