#include "../include/SearchBar.hpp" #include "../include/Theme.hpp" #include "../include/Scale.hpp" #include "../include/ResourceLoader.hpp" #include "../include/Config.hpp" #include "../include/Utils.hpp" #include #include #include #include #include // TODO: Use a seperate placeholder sf::Text instead of switching the text to placeholder text.... namespace QuickMedia { static const float background_margin_horizontal = 10.0f + std::floor(5.0f * get_config().scale); static const float padding_top_default = std::floor(10.0f * get_config().scale); static const float padding_bottom_default = std::floor(15.0f * get_config().scale); static const float background_margin_vertical = std::floor(4.0f * get_config().scale); SearchBar::SearchBar(sf::Texture *plugin_logo, sf::Shader *rounded_rectangle_shader, const std::string &placeholder, bool input_masked) : onTextUpdateCallback(nullptr), onTextSubmitCallback(nullptr), onTextBeginTypingCallback(nullptr), text_autosearch_delay(50), caret_visible(true), text(placeholder, *FontLoader::get_font(FontLoader::FontType::LATIN), std::floor(get_config().search.font_size * get_config().scale * get_config().font_scale)), background(sf::Vector2f(1.0f, 1.0f), 10.0f, get_theme().selected_color, rounded_rectangle_shader), placeholder_str(placeholder), show_placeholder(true), updated_search(false), draw_logo(false), needs_update(true), input_masked(input_masked), typing(false), backspace_pressed(false), mouse_left_inside(false), pos(0.0f, 0.0f), prev_size(0.0f, 0.0f) { padding_top = padding_top_default; padding_bottom = padding_bottom_default; text.setFillColor(get_theme().placeholder_text_color); shade.setFillColor(get_theme().shade_color); if(plugin_logo && plugin_logo->getNativeHandle() != 0) plugin_logo_sprite.setTexture(*plugin_logo, true); } void SearchBar::draw(sf::RenderWindow &window, sf::Vector2f size, bool draw_background) { if(std::abs(size.x - prev_size.x) > 1.0f || std::abs(size.y - prev_size.y) > 1.0f) { needs_update = true; prev_size = size; } if(needs_update) { needs_update = false; onWindowResize(size); } if(draw_background) window.draw(shade); background.draw(window); if(input_masked && !show_placeholder) { std::string masked_str(text.getString().getSize(), '*'); sf::Text masked_text(std::move(masked_str), *text.getFont(), text.getCharacterSize()); masked_text.setPosition(text.getPosition()); window.draw(masked_text); caret.setPosition(masked_text.findCharacterPos(masked_text.getString().getSize()) + sf::Vector2f(0.0f, 2.0f)); } else { window.draw(text); if(show_placeholder || text.getString().isEmpty()) caret.setPosition(text.getPosition() - sf::Vector2f(2.0f, 0.0f) + sf::Vector2f(0.0f, 2.0f)); else caret.setPosition(text.findCharacterPos(text.getString().getSize()) + sf::Vector2f(0.0f, 2.0f)); } if(caret_visible && is_editable()) window.draw(caret); if(draw_logo) window.draw(plugin_logo_sprite); } void SearchBar::on_event(sf::Event &event) { if(is_touch_enabled() && event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left) { sf::FloatRect box(background.get_position(), background.get_size()); if(box.contains(event.mouseButton.x, event.mouseButton.y)) mouse_left_inside = true; else mouse_left_inside = false; } else if(is_touch_enabled() && event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left) { sf::FloatRect box(background.get_position(), background.get_size()); if(mouse_left_inside && box.contains(event.mouseButton.x, event.mouseButton.y)) show_virtual_keyboard(); mouse_left_inside = false; } if(!editable) return; if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Backspace) backspace_pressed = true; else if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Backspace) backspace_pressed = false; if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::V && event.key.control) { auto clipboard = sf::Clipboard::getString().toUtf8(); append_text(*(std::string*)&clipboard); } if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::D && event.key.control) { clear(); updated_search = true; time_since_search_update.restart(); } if((sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl)) && (event.type != sf::Event::TextEntered || event.text.unicode != 13)) // Enter return; if(event.type == sf::Event::TextEntered && event.text.unicode != 8 && event.text.unicode != 127) // 8 = backspace, 127 = del onTextEntered(event.text.unicode); else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Backspace) onTextEntered(8); } void SearchBar::update() { sf::Int32 elapsed_time = time_since_search_update.getElapsedTime().asMilliseconds(); int timeout = text_autosearch_delay; if(backspace_pressed) timeout = 750; if(updated_search && elapsed_time >= timeout) { updated_search = false; auto u8 = text.getString().toUtf8(); std::string *u8_str = (std::string*)&u8; if(show_placeholder) u8_str->clear(); if(onTextUpdateCallback) onTextUpdateCallback(*u8_str); typing = false; } } void SearchBar::onWindowResize(const sf::Vector2f &size) { draw_logo = plugin_logo_sprite.getTexture() != nullptr; if(size.x < 400.0f) draw_logo = false; float font_height = text.getCharacterSize() + 7.0f; float rect_height = std::floor(font_height + background_margin_vertical * 2.0f); float offset_x; if(draw_logo) { float one_line_height = std::floor(text.getCharacterSize() + 8.0f + background_margin_vertical * 2.0f); auto texture_size = plugin_logo_sprite.getTexture()->getSize(); sf::Vector2f texture_size_f(texture_size.x, texture_size.y); sf::Vector2f new_size = wrap_to_size(texture_size_f, sf::Vector2f(200.0f, one_line_height)); plugin_logo_sprite.setScale(get_ratio(texture_size_f, new_size)); plugin_logo_sprite.setPosition(pos.x + padding_x, pos.y + padding_top + rect_height * 0.5f - plugin_logo_sprite.getTexture()->getSize().y * plugin_logo_sprite.getScale().y * 0.5f); offset_x = padding_x + new_size.x + 10.0f; } else { offset_x = padding_x; } const float width = std::floor(size.x - offset_x - padding_x); background.set_size(sf::Vector2f(width, rect_height)); shade.setSize(sf::Vector2f(size.x, padding_top + rect_height + padding_bottom)); caret.setSize(sf::Vector2f(std::floor(2.0f * get_config().scale), text.getCharacterSize() + std::floor(2.0f * get_config().scale))); background.set_position(sf::Vector2f(pos.x + offset_x, pos.y + padding_top)); shade.setPosition(pos); sf::Vector2f font_position(std::floor(pos.x + offset_x + background_margin_horizontal), std::floor(pos.y + padding_top + background_margin_vertical)); text.setPosition(font_position); } void SearchBar::onTextEntered(sf::Uint32 codepoint) { if(codepoint == 8 && !show_placeholder) { // Backspace sf::String str = text.getString(); if(str.getSize() > 0) { // TODO: When it's possible to move the cursor, then check at cursor position instead of end of the string if(str[str.getSize() - 1] == '\n') needs_update = true; str.erase(str.getSize() - 1, 1); text.setString(str); if(str.getSize() == 0) { show_placeholder = true; text.setString(placeholder_str); text.setFillColor(get_theme().placeholder_text_color); } if(!updated_search) { typing = true; if(onTextBeginTypingCallback) onTextBeginTypingCallback(); } updated_search = true; time_since_search_update.restart(); } } else if(codepoint == 13) { // Return backspace_pressed = false; if(onTextSubmitCallback) { auto u8 = text.getString().toUtf8(); std::string *u8_str = (std::string*)&u8; if(show_placeholder) u8_str->clear(); onTextSubmitCallback(*u8_str); } } else if(codepoint > 31) { // Non-control character append_text(sf::String(codepoint)); } else if(codepoint == '\n') needs_update = true; } void SearchBar::clear() { if(show_placeholder) return; show_placeholder = true; text.setString(placeholder_str); text.setFillColor(get_theme().placeholder_text_color); needs_update = true; updated_search = false; backspace_pressed = false; } void SearchBar::set_text(const std::string &text) { clear(); append_text(text); } void SearchBar::append_text(const std::string &text_to_add) { if(text_to_add.empty()) return; if(show_placeholder) { show_placeholder = false; text.setString(""); text.setFillColor(sf::Color::White); } sf::String str = text.getString(); str += sf::String::fromUtf8(text_to_add.begin(), text_to_add.end());; text.setString(str); if(!updated_search) { typing = true; if(onTextBeginTypingCallback) onTextBeginTypingCallback(); } updated_search = true; time_since_search_update.restart(); backspace_pressed = false; if(text_to_add.find('\n') != std::string::npos) needs_update = true; } void SearchBar::set_position(sf::Vector2f pos) { if(std::abs(this->pos.x - pos.x) > 1.0f || std::abs(this->pos.y - pos.y) > 1.0f) { this->pos = pos; needs_update = true; } } void SearchBar::set_editable(bool editable) { this->editable = editable; } bool SearchBar::is_editable() const { return editable; } float SearchBar::getBottom() const { return getBottomWithoutShadow() + 5.0f;//background_shadow.getSize().y; } float SearchBar::getBottomWithoutShadow() const { float font_height = text.getCharacterSize() + 7.0f; return std::floor(font_height + background_margin_vertical * 2.0f + padding_top + padding_bottom); } std::string SearchBar::get_text() const { if(show_placeholder) return ""; return text.getString(); } bool SearchBar::is_empty() const { return show_placeholder; } }