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