use crate::render::callbacks::CallbackRegistry;
use crate::render::widget::*;
use crate::render::widget_cache::WidgetContainer;
use crate::render::widget_config::*;
use crate::render::{
make_points, make_size, Points, Size, POINT_X, POINT_Y, SIZE_HEIGHT, SIZE_WIDTH,
};
use sdl2::render::{Canvas, Texture};
use sdl2::video::Window;
use crate::render::canvas_helper::CanvasHelper;
use crate::render::layout_cache::LayoutContainer;
use crate::render::texture_cache::TextureCache;
use crate::render::texture_store::TextureStore;
use crate::render::widget_config::CompassPosition::Center;
use crate::widgets::image_widget::ImageWidget;
use crate::widgets::text_widget::{TextJustify, TextWidget};
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use std::any::Any;
use std::collections::HashMap;
pub type OnToggleCallbackType =
Option<Box<dyn FnMut(&mut CheckboxWidget, &[WidgetContainer], &[LayoutContainer], bool)>>;
pub struct CheckboxWidget {
config: WidgetConfig,
system_properties: HashMap<i32, String>,
callback_registry: CallbackRegistry,
texture_store: TextureStore,
text_widget: TextWidget,
unchecked_widget: ImageWidget,
checked_widget: ImageWidget,
active: bool,
selected: bool,
in_bounds: bool,
originated: bool,
on_toggle: OnToggleCallbackType,
}
impl CheckboxWidget {
pub fn new(points: Points, size: Size, text: String, font_size: i32, selected: bool) -> Self {
let mut text_widget = TextWidget::new(
String::from("assets/OpenSans-Regular.ttf"),
sdl2::ttf::FontStyle::NORMAL,
font_size,
TextJustify::Left,
text,
make_points(
points[POINT_X] + size[SIZE_HEIGHT] as i32 + 6,
points[POINT_Y] + 2,
),
make_size(
size[SIZE_WIDTH] - size[SIZE_HEIGHT] - 10,
size[SIZE_HEIGHT] - 4,
),
);
let mut config = WidgetConfig::new(points.clone(), size.clone());
let mut unchecked_widget = ImageWidget::new(
String::from("assets/checkbox_unselected.png"),
make_points(points[POINT_X] + 2, points[POINT_Y] + 2),
make_size(size[SIZE_HEIGHT] - 4, size[SIZE_HEIGHT] - 4),
true,
);
let mut checked_widget = ImageWidget::new(
String::from("assets/checkbox_selected.png"),
make_points(points[POINT_X] + 2, points[POINT_Y] + 2),
make_size(size[SIZE_HEIGHT] - 4, size[SIZE_HEIGHT] - 4),
true,
);
text_widget.set_color(CONFIG_COLOR_TEXT, Color::RGB(0, 0, 0));
unchecked_widget.set_compass(CONFIG_IMAGE_POSITION, Center);
checked_widget.set_compass(CONFIG_IMAGE_POSITION, Center);
config.set_toggle(CONFIG_SELECTED_STATE, selected);
Self {
config,
system_properties: HashMap::new(),
callback_registry: CallbackRegistry::new(),
texture_store: TextureStore::default(),
text_widget,
unchecked_widget,
checked_widget,
active: false,
selected,
in_bounds: false,
originated: false,
on_toggle: None,
}
}
pub fn on_toggle<F>(&mut self, callback: F)
where
F: FnMut(&mut CheckboxWidget, &[WidgetContainer], &[LayoutContainer], bool) + 'static,
{
self.on_toggle = Some(Box::new(callback));
}
fn call_toggle_callback(&mut self, widgets: &[WidgetContainer], layouts: &[LayoutContainer]) {
if let Some(mut cb) = self.on_toggle.take() {
cb(self, widgets, layouts, self.selected);
self.on_toggle = Some(cb);
}
}
}
impl CanvasHelper for CheckboxWidget {}
impl Widget for CheckboxWidget {
fn draw(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) -> Option<&Texture> {
self.text_widget.draw(c, t);
self.draw_bounding_box(c);
if self.get_config().invalidated() {
let bounds = self.get_config().get_size(CONFIG_SIZE);
let base_color = self.get_color(CONFIG_COLOR_BASE);
let border_color = self.get_config().get_color(CONFIG_COLOR_BORDER);
self.texture_store
.create_or_resize_texture(c, bounds[0] as u32, bounds[1] as u32);
let checkbox_widget_texture = if self.active {
if self.in_bounds {
if self.selected {
self.unchecked_widget.draw(c, t).unwrap()
} else {
self.checked_widget.draw(c, t).unwrap()
}
} else {
if self.selected {
self.checked_widget.draw(c, t).unwrap()
} else {
self.unchecked_widget.draw(c, t).unwrap()
}
}
} else {
if self.selected {
self.checked_widget.draw(c, t).unwrap()
} else {
self.unchecked_widget.draw(c, t).unwrap()
}
};
let text_widget_texture = self.text_widget.draw(c, t).unwrap();
c.with_texture_canvas(self.texture_store.get_mut_ref(), |texture| {
texture.set_draw_color(base_color);
texture.clear();
texture
.copy(
text_widget_texture,
None,
Rect::new(
2 + bounds[1] as i32 + 6,
0,
bounds[0] - bounds[1],
bounds[1] - 4,
),
)
.unwrap();
texture
.copy(
checkbox_widget_texture,
None,
Rect::new(2, 2, bounds[1] - 4, bounds[1] - 4),
)
.unwrap();
texture.set_draw_color(border_color);
texture
.draw_rect(Rect::new(0, 0, bounds[0], bounds[1]))
.unwrap();
})
.unwrap();
}
self.texture_store.get_optional_ref()
}
fn mouse_entered(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
self.in_bounds = true;
self.mouse_entered_callback(_widgets, _layouts);
self.get_config().set_invalidated(true);
}
fn mouse_exited(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
self.in_bounds = false;
self.mouse_exited_callback(_widgets, _layouts);
self.get_config().set_invalidated(true);
}
fn button_clicked(
&mut self,
_widgets: &[WidgetContainer],
_layouts: &[LayoutContainer],
_button: u8,
_clicks: u8,
_state: bool,
) {
if _button == 1 {
if _state {
self.active = true;
self.originated = true;
} else {
self.active = false;
if self.in_bounds && self.originated {
self.selected = !self.selected;
self.set_toggle(CONFIG_SELECTED_STATE, self.selected);
self.call_toggle_callback(_widgets, _layouts);
}
self.originated = false;
}
self.get_config().set_invalidated(true);
}
self.button_clicked_callback(_widgets, _layouts, _button, _clicks, _state);
}
default_widget_functions!();
default_widget_properties!();
default_widget_callbacks!();
}