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::layout_cache::LayoutContainer;
28use crate::render::texture_cache::TextureCache;
29use crate::render::texture_store::TextureStore;
30use crate::widgets::text_widget::{TextJustify, TextWidget};
31use sdl2::pixels::Color;
32use sdl2::rect::Rect;
33use std::any::Any;
34use std::collections::HashMap;
35
36pub type OnToggleCallbackType =
39 Option<Box<dyn FnMut(&mut ToggleButtonWidget, &[WidgetContainer], &[LayoutContainer], bool)>>;
40
41pub struct ToggleButtonWidget {
43 config: WidgetConfig,
44 system_properties: HashMap<i32, String>,
45 callback_registry: CallbackRegistry,
46 texture_store: TextureStore,
47 base_widget: BaseWidget,
48 text_widget: TextWidget,
49 active: bool,
50 selected: bool,
51 in_bounds: bool,
52 originated: bool,
53 on_toggle: OnToggleCallbackType,
54}
55
56impl ToggleButtonWidget {
59 pub fn new(points: Points, size: Size, text: String, font_size: i32, selected: bool) -> Self {
63 let mut base_widget = BaseWidget::new(points.clone(), size.clone());
64 let mut text_widget = TextWidget::new(
65 String::from("assets/OpenSans-Regular.ttf"),
66 sdl2::ttf::FontStyle::NORMAL,
67 font_size,
68 TextJustify::Center,
69 text,
70 make_points(points[POINT_X] + 2, points[POINT_Y] + 2),
71 make_size(size[SIZE_WIDTH] - 4, size[SIZE_HEIGHT] - 4),
72 );
73
74 let base_color = if selected {
75 Color::RGB(0, 0, 0)
76 } else {
77 Color::RGB(255, 255, 255)
78 };
79 let text_color = if selected {
80 Color::RGB(255, 255, 255)
81 } else {
82 Color::RGB(0, 0, 0)
83 };
84
85 let mut config = WidgetConfig::new(points, size);
86
87 base_widget.set_color(CONFIG_COLOR_BASE, base_color);
88 base_widget.set_color(CONFIG_COLOR_BORDER, Color::RGB(0, 0, 0));
89 base_widget.set_numeric(CONFIG_BORDER_WIDTH, 2);
90
91 text_widget.set_color(CONFIG_COLOR_BASE, base_color);
92 text_widget.set_color(CONFIG_COLOR_TEXT, text_color);
93
94 config.set_toggle(CONFIG_SELECTED_STATE, selected);
95
96 Self {
97 config,
98 system_properties: HashMap::new(),
99 callback_registry: CallbackRegistry::new(),
100 texture_store: TextureStore::default(),
101 base_widget,
102 text_widget,
103 active: false,
104 selected,
105 in_bounds: false,
106 originated: false,
107 on_toggle: None,
108 }
109 }
110
111 pub fn set_selected(&mut self, selected: bool) {
113 self.selected = selected;
114 self.get_config().set_invalidated(true);
115 }
116
117 pub fn is_selected(&self) -> bool {
119 self.selected
120 }
121
122 fn draw_hovered(&mut self) {
124 let base_color = if self.selected {
125 Color::RGB(255, 255, 255)
126 } else {
127 Color::RGB(0, 0, 0)
128 };
129 let text_color = if self.selected {
130 Color::RGB(0, 0, 0)
131 } else {
132 Color::RGB(255, 255, 255)
133 };
134
135 self.base_widget.set_color(CONFIG_COLOR_BASE, base_color);
136 self.text_widget.set_color(CONFIG_COLOR_TEXT, text_color);
137 self.text_widget.set_color(CONFIG_COLOR_BASE, base_color);
138 self.get_config().set_invalidated(true);
139 }
140
141 fn draw_unhovered(&mut self) {
143 let base_color = if self.selected {
144 Color::RGB(0, 0, 0)
145 } else {
146 Color::RGB(255, 255, 255)
147 };
148 let text_color = if self.selected {
149 Color::RGB(255, 255, 255)
150 } else {
151 Color::RGB(0, 0, 0)
152 };
153
154 self.base_widget.set_color(CONFIG_COLOR_BASE, base_color);
155 self.text_widget.set_color(CONFIG_COLOR_TEXT, text_color);
156 self.text_widget.set_color(CONFIG_COLOR_BASE, base_color);
157 self.get_config().set_invalidated(true);
158 }
159
160 pub fn on_toggle<F>(&mut self, callback: F)
162 where
163 F: FnMut(&mut ToggleButtonWidget, &[WidgetContainer], &[LayoutContainer], bool) + 'static,
164 {
165 self.on_toggle = Some(Box::new(callback));
166 }
167
168 fn call_toggle_callback(&mut self, widgets: &[WidgetContainer], layouts: &[LayoutContainer]) {
170 if let Some(mut cb) = self.on_toggle.take() {
171 cb(self, widgets, layouts, self.selected);
172 self.on_toggle = Some(cb);
173 }
174 }
175}
176
177impl Widget for ToggleButtonWidget {
179 fn draw(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) -> Option<&Texture> {
181 if self.get_config().invalidated() {
182 let bounds = self.get_config().get_size(CONFIG_SIZE);
183 let base_color = self.get_color(CONFIG_COLOR_BASE);
184
185 self.texture_store
186 .create_or_resize_texture(c, bounds[0] as u32, bounds[1] as u32);
187
188 let base_widget_texture = self.base_widget.draw(c, t).unwrap();
191 let text_widget_texture = self.text_widget.draw(c, t).unwrap();
192
193 c.with_texture_canvas(self.texture_store.get_mut_ref(), |texture| {
194 texture.set_draw_color(base_color);
195 texture.clear();
196
197 texture
198 .copy(
199 base_widget_texture,
200 None,
201 Rect::new(0, 0, bounds[0], bounds[1]),
202 )
203 .unwrap();
204
205 texture
206 .copy(
207 text_widget_texture,
208 None,
209 Rect::new(2, 2, bounds[0] - 4, bounds[1] - 4),
210 )
211 .unwrap();
212 })
213 .unwrap();
214 }
215
216 self.texture_store.get_optional_ref()
217 }
218
219 fn mouse_entered(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
221 if self.active {
222 self.draw_hovered();
223 }
224
225 self.in_bounds = true;
226 self.mouse_entered_callback(_widgets, _layouts);
227 }
228
229 fn mouse_exited(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
231 if self.active {
232 self.draw_unhovered();
233 }
234
235 self.in_bounds = false;
236 self.mouse_exited_callback(_widgets, _layouts);
237 }
238
239 fn button_clicked(
241 &mut self,
242 _widgets: &[WidgetContainer],
243 _layouts: &[LayoutContainer],
244 _button: u8,
245 _clicks: u8,
246 _state: bool,
247 ) {
248 if _button == 1 {
249 if _state {
250 self.draw_hovered();
251 self.active = true;
252 self.originated = true;
253 } else {
254 self.active = false;
255
256 if self.in_bounds && self.originated {
257 self.selected = !self.selected;
258 self.set_toggle(CONFIG_SELECTED_STATE, self.selected);
259 self.call_toggle_callback(_widgets, _layouts);
260 }
261
262 self.originated = false;
263 }
264 }
265
266 self.button_clicked_callback(_widgets, _layouts, _button, _clicks, _state);
267 }
268
269 default_widget_functions!();
270 default_widget_properties!();
271 default_widget_callbacks!();
272}