use crate::render::callbacks::CallbackRegistry;
use crate::render::widget::*;
use crate::render::widget_cache::WidgetContainer;
use crate::render::widget_config::*;
use crate::render::{Points, Size, POINT_X, SIZE_HEIGHT, SIZE_WIDTH};
use sdl2::render::{Canvas, Texture, TextureQuery};
use sdl2::video::Window;
use crate::render::layout_cache::LayoutContainer;
use crate::render::texture_cache::TextureCache;
use crate::render::texture_store::TextureStore;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use std::any::Any;
use std::collections::HashMap;
use std::path::Path;
pub type OnTabSelectedCallbackType =
Option<Box<dyn FnMut(&mut TabBarWidget, &[WidgetContainer], &[LayoutContainer], u16)>>;
pub struct TabBarWidget {
config: WidgetConfig,
system_properties: HashMap<i32, String>,
callback_registry: CallbackRegistry,
texture_store: TextureStore,
tab_items: Vec<String>,
tab_widths: Vec<u32>,
on_tab_selected: OnTabSelectedCallbackType,
selected_item: i16,
hovered_item: i16,
in_bounds: bool,
calculated: bool,
}
impl TabBarWidget {
pub fn new(points: Points, size: Size, tab_items: Vec<String>) -> Self {
Self {
config: WidgetConfig::new(points, size),
system_properties: HashMap::new(),
callback_registry: CallbackRegistry::new(),
texture_store: TextureStore::default(),
on_tab_selected: None,
tab_items,
tab_widths: vec![0],
selected_item: -1,
hovered_item: -1,
in_bounds: false,
calculated: false,
}
}
pub fn on_tab_selected<F>(&mut self, callback: F)
where
F: FnMut(&mut TabBarWidget, &[WidgetContainer], &[LayoutContainer], u16) + 'static,
{
self.on_tab_selected = Some(Box::new(callback));
}
fn call_tab_selected_callback(
&mut self,
widgets: &[WidgetContainer],
layouts: &[LayoutContainer],
tab: u16,
) {
if let Some(mut cb) = self.on_tab_selected.take() {
cb(self, widgets, layouts, tab);
self.on_tab_selected = Some(cb);
}
}
fn adjust_widgets(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) {
let ttf_context = t.get_ttf_context();
let texture_creator = c.texture_creator();
let num_tabs = self.tab_items.len();
let mut font = ttf_context
.load_font(Path::new(&String::from("assets/OpenSans-Regular.ttf")), 10)
.unwrap();
let mut tab_widths = Vec::new();
let bounds = self.get_config().get_size(CONFIG_SIZE);
font.set_style(sdl2::ttf::FontStyle::NORMAL);
for i in 0..num_tabs {
let surface = font
.render(&self.tab_items[i])
.blended_wrapped(Color::RGB(0, 0, 0), bounds[SIZE_WIDTH])
.map_err(|e| e.to_string())
.unwrap();
let font_texture = texture_creator
.create_texture_from_surface(&surface)
.map_err(|e| e.to_string())
.unwrap();
let TextureQuery { width, .. } = font_texture.query();
tab_widths.push((width + 20) as u32);
}
self.tab_widths = tab_widths;
self.calculated = true;
}
fn find_hovered_item(&self, x: i32) -> i16 {
let mut selected_item = -1;
let mut start_x: i32 = 20;
for i in 0..self.tab_widths.len() {
if x >= start_x && x <= (start_x + self.tab_widths[i] as i32 + 30) {
selected_item = i as i16;
break;
}
start_x += self.tab_widths[i] as i32 + 31;
}
selected_item
}
}
impl Widget for TabBarWidget {
fn draw(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) -> Option<&Texture> {
if !self.calculated {
self.adjust_widgets(c, t);
}
if self.get_config().invalidated() {
let bounds = self.get_config().get_size(CONFIG_SIZE);
let base_color = self.get_color(CONFIG_COLOR_BASE);
self.texture_store
.create_or_resize_texture(c, bounds[0] as u32, bounds[1] as u32);
let tab_widths = self.tab_widths.clone();
let tab_items = self.tab_items.clone();
let selected_tab = self.selected_item;
let hovered_tab = self.hovered_item;
c.with_texture_canvas(self.texture_store.get_mut_ref(), |texture| {
texture.set_draw_color(base_color);
texture.clear();
let mut start_x: u32 = 20;
for i in 0..tab_widths.len() {
let mut font_color = Color::RGB(0, 0, 0);
if selected_tab == i as i16 {
texture.set_draw_color(Color::RGB(128, 128, 128));
font_color = Color::RGB(255, 255, 255);
} else if hovered_tab == i as i16 {
texture.set_draw_color(Color::RGB(192, 192, 192));
} else {
texture.set_draw_color(Color::RGB(224, 224, 224));
}
texture
.fill_rect(Rect::new(
start_x as i32,
0,
tab_widths[i] + 30,
bounds[SIZE_HEIGHT],
))
.unwrap();
let (font_texture, font_width, font_height) = t.render_text(
texture,
String::from("assets/OpenSans-Regular.ttf"),
14,
sdl2::ttf::FontStyle::NORMAL,
tab_items[i].clone(),
font_color,
bounds[SIZE_WIDTH],
);
texture
.copy(
&font_texture,
None,
Rect::new(
start_x as i32 + 10,
(bounds[SIZE_HEIGHT] / 2 - 10) as i32,
font_width,
font_height,
),
)
.unwrap();
start_x += tab_widths[i] + 30 + 1;
}
texture.set_draw_color(Color::RGB(0, 0, 0));
texture
.draw_line(
Point::new(0, bounds[SIZE_HEIGHT] as i32 - 1),
Point::new(bounds[SIZE_WIDTH] as i32, bounds[SIZE_HEIGHT] as i32 - 1),
)
.unwrap();
})
.unwrap();
}
self.texture_store.get_optional_ref()
}
fn mouse_entered(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
self.in_bounds = true;
}
fn mouse_exited(&mut self, _widgets: &[WidgetContainer], _layouts: &[LayoutContainer]) {
self.in_bounds = false;
self.hovered_item = -1;
self.set_invalidated(true);
}
fn mouse_moved(
&mut self,
_widgets: &[WidgetContainer],
_layouts: &[LayoutContainer],
points: Points,
) {
if self.calculated {
let origin = self.get_config().get_point(CONFIG_ORIGIN);
let true_x = points[POINT_X] - origin[POINT_X];
let previous_hovered_item = self.hovered_item;
let hovered_item = self.find_hovered_item(true_x);
self.hovered_item = hovered_item;
if previous_hovered_item != hovered_item {
self.set_invalidated(true);
}
}
}
fn button_clicked(
&mut self,
_widgets: &[WidgetContainer],
_layouts: &[LayoutContainer],
button: u8,
_clicks: u8,
state: bool,
) {
if button == 1 && self.in_bounds && self.calculated && state && self.hovered_item != -1 {
self.selected_item = self.hovered_item;
self.set_invalidated(true);
if self.selected_item > -1 {
self.call_tab_selected_callback(_widgets, _layouts, self.selected_item as u16);
}
}
}
default_widget_functions!();
default_widget_properties!();
default_widget_callbacks!();
}