#include "../include/Tabs.hpp" #include "../include/Body.hpp" #include "../include/ResourceLoader.hpp" #include "../include/Utils.hpp" #include "../include/Config.hpp" #include "../include/Theme.hpp" #include "../include/Scale.hpp" #include #include #include #include namespace QuickMedia { static float floor(float v) { return (int)v; } static const float tab_text_size = floor(get_config().tab.font_size * get_config().scale * get_config().font_scale); static const float tab_height = tab_text_size + floor(10.0f * get_config().scale * get_config().spacing_scale); static const float tab_min_width = 250.0f; static const float tab_margin_x = floor(10.0f * get_config().spacing_scale); // static float Tabs::get_height() { return tab_height; } // static float Tabs::get_shade_height() { return tab_height + floor(10.0f * get_config().scale * get_config().spacing_scale); } Tabs::Tabs(mgl::Shader *rounded_rectangle_shader, mgl::Color shade_color) : background(mgl::vec2f(1.0f, 1.0f), 10.0f * get_config().scale, get_theme().selected_color, rounded_rectangle_shader), shade_color(shade_color) { shade.set_color(shade_color); arrow_sprite.set_texture(TextureLoader::get_texture("images/arrow.png")); } Tabs::Tabs(mgl::Shader *rounded_rectangle_shader) : Tabs(rounded_rectangle_shader, get_theme().shade_color) {} int Tabs::add_tab(std::string title, Body *body) { assert(body); tabs.push_back({ mgl::Text(std::move(title), *FontLoader::get_font(FontLoader::FontType::LATIN, tab_text_size)), body} ); return tabs.size() - 1; } void Tabs::move_selected_tab(int new_tab) { const int prev_tab = selected_tab; const int tab_diff = new_tab - prev_tab; if(tab_diff > 0) { while(selected_tab < new_tab) { ++selected_tab; const float scroll_fixed = scroll + (tab_offset * width_per_tab); if(scroll_fixed + tab_index_to_x_offset(selected_tab) + tab_background_width > container_width - tab_margin_x) --tab_offset; } } else if(tab_diff < 0) { while(selected_tab > new_tab) { --selected_tab; const float scroll_fixed = scroll + (tab_offset * width_per_tab); if(scroll_fixed + tab_index_to_x_offset(selected_tab) + tab_background_width < tab_margin_x) ++tab_offset; } } else { return; } if(on_change_tab) on_change_tab(prev_tab, selected_tab); } void Tabs::on_event(mgl::Event &event) { if(event.type == mgl::Event::KeyPressed && event.key.control && !tabs.empty()) { bool move_left = (event.key.code == mgl::Keyboard::Left || (event.key.alt && event.key.code == mgl::Keyboard::H)); move_left |= (event.key.code == mgl::Keyboard::Tab && event.key.shift); bool move_right = (event.key.code == mgl::Keyboard::Right || (event.key.alt && event.key.code == mgl::Keyboard::L)); move_right |= (event.key.code == mgl::Keyboard::Tab && !event.key.shift); if(move_left) { if(selected_tab > 0) move_selected_tab(selected_tab - 1); } else if(move_right) { if(selected_tab < (int)tabs.size() - 1) move_selected_tab(selected_tab + 1); } else if(event.key.code >= mgl::Keyboard::Num1 && event.key.code <= mgl::Keyboard::Num9) { const int tab_target = event.key.code - mgl::Keyboard::Num1; if(tab_target < (int)tabs.size()) move_selected_tab(tab_target); } } } static mgl::View create_scissor_view(mgl::vec2f pos, mgl::vec2f size) { mgl::View view = { mgl::vec2i(pos.x, pos.y), mgl::vec2i(size.x, size.y) }; return view; } void Tabs::draw(mgl::Window &window, mgl::vec2f pos, float width) { if(width - tab_margin_x < 0.0f || tabs.empty()) return; container_width = width; const int num_visible_tabs = std::min((int)tabs.size(), std::max(1, (int)(width / tab_min_width))); width_per_tab = floor(width / num_visible_tabs); const float tab_text_y = floor(pos.y + tab_height*0.5f - tab_text_size*0.7f); tab_background_width = floor(width_per_tab - tab_margin_x*2.0f); background.set_size(mgl::vec2f(tab_background_width, tab_height)); if(shade_color != mgl::Color(0, 0, 0, 0)) { shade.set_size(mgl::vec2f(width, get_shade_height())); shade.set_position(mgl::vec2f(floor(pos.x), floor(pos.y))); window.draw(shade); } float scroll_fixed = scroll + (tab_offset * width_per_tab); float overflow_last = (scroll_fixed + tab_index_to_x_offset(tabs.size() - 1) + tab_background_width) - (width - tab_margin_x); if(overflow_last < 0.0f) scroll_fixed -= overflow_last; float overflow = (scroll_fixed + tab_index_to_x_offset(selected_tab) + tab_background_width) - (width - tab_margin_x); float underflow = scroll_fixed + tab_index_to_x_offset(selected_tab); if(overflow > 0.0f) scroll_fixed -= overflow; else if(underflow < 0.0f) scroll_fixed -= underflow; bool tabs_cutoff_left = false; bool tabs_cutoff_right = false; const auto start_pos = pos; const mgl::View prev_view = window.get_view(); pos.x += scroll_fixed; for(size_t i = 0; i < tabs.size(); ++i) { const int index = i; const float background_pos_x = floor(pos.x + tab_index_to_x_offset(i)); if(background_pos_x - start_pos.x >= width - tab_margin_x) { tabs_cutoff_right = true; break; } else if(background_pos_x + tab_background_width - start_pos.x <= tab_margin_x) { tabs_cutoff_left = true; continue; } if((int)index == selected_tab) { background.set_position(mgl::vec2f(background_pos_x, floor(pos.y))); background.draw(window); } mgl::Text &tab_text = tabs[index].text; float text_pos_x = floor(pos.x + i*width_per_tab + width_per_tab*0.5f - tab_text.get_bounds().size.x*0.5f); text_pos_x = std::max(text_pos_x, background_pos_x); window.set_view(create_scissor_view({ text_pos_x, tab_text_y }, { tab_background_width, tab_height*2 })); window.draw(tab_text); window.set_view(prev_view); } const float lw = floor(25.0f * get_config().scale); const float lh = background.get_size().y; if(tabs_cutoff_left) { mgl::Vertex gradient_points[4]; gradient_points[0].position = mgl::vec2f(start_pos.x + tab_margin_x, start_pos.y); gradient_points[1].position = mgl::vec2f(start_pos.x + tab_margin_x + lw, start_pos.y); gradient_points[2].position = mgl::vec2f(start_pos.x + tab_margin_x + lw, start_pos.y + lh); gradient_points[3].position = mgl::vec2f(start_pos.x + tab_margin_x, start_pos.y + lh); gradient_points[0].color = shade_color; gradient_points[1].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); gradient_points[2].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); gradient_points[3].color = shade_color; window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); mgl::vec2i arrow_sprite_width = arrow_sprite.get_texture()->get_size(); arrow_sprite_width.y = arrow_sprite_width.x; const mgl::vec2f arrow_scale_ratio = get_ratio(arrow_sprite_width, clamp_to_size_y(arrow_sprite_width, (int)lh - 8)); arrow_sprite.set_scale(arrow_scale_ratio); arrow_sprite.set_origin(arrow_sprite.get_texture()->get_size().to_vec2f() * 0.5f); arrow_sprite.set_position(start_pos + mgl::vec2f((int)(arrow_sprite.get_texture()->get_size().x * 0.5f * arrow_scale_ratio.x) + 5, (int)(lh * 0.5f))); arrow_sprite.set_rotation(-90.0f); arrow_sprite.set_color(get_theme().arrow_color); window.draw(arrow_sprite); } if(tabs_cutoff_right) { mgl::Vertex gradient_points[4]; gradient_points[0].position = mgl::vec2f(start_pos.x + width - lw - tab_margin_x, start_pos.y); gradient_points[1].position = mgl::vec2f(start_pos.x + width, start_pos.y); gradient_points[2].position = mgl::vec2f(start_pos.x + width, start_pos.y + lh); gradient_points[3].position = mgl::vec2f(start_pos.x + width - lw - tab_margin_x, start_pos.y + lh); gradient_points[0].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); gradient_points[1].color = shade_color; gradient_points[2].color = shade_color; gradient_points[3].color = mgl::Color(shade_color.r, shade_color.g, shade_color.b, 10); window.draw(gradient_points, 4, mgl::PrimitiveType::Quads); mgl::vec2i arrow_sprite_width = arrow_sprite.get_texture()->get_size(); arrow_sprite_width.y = arrow_sprite_width.x; const mgl::vec2f arrow_scale_ratio = get_ratio(arrow_sprite_width, clamp_to_size_y(arrow_sprite_width, (int)lh - 8)); arrow_sprite.set_scale(arrow_scale_ratio); arrow_sprite.set_origin(arrow_sprite.get_texture()->get_size().to_vec2f() * 0.5f); arrow_sprite.set_position(start_pos + mgl::vec2f(width - (int)(arrow_sprite.get_texture()->get_size().x * 0.5f * arrow_scale_ratio.x) - 5, (int)(lh * 0.5f))); arrow_sprite.set_rotation(90.0f); arrow_sprite.set_color(get_theme().arrow_color); window.draw(arrow_sprite); } } void Tabs::set_text(int index, const std::string &text) { if(index < 0 || index >= (int)tabs.size() || text == tabs[index].text.get_string()) return; tabs[index].text.set_string(text); } void Tabs::set_selected(int index) { if(index >= 0 && index < (int)tabs.size()) move_selected_tab(index); } int Tabs::get_selected() const { return selected_tab; } float Tabs::tab_index_to_x_offset(int index) { return floor(index*width_per_tab + tab_margin_x); } }