#include "../include/SearchBar.hpp" #include "../include/Scale.hpp" #include #include #include #include #include // TODO: Use a seperate placeholder sf::Text instead of switching the text to placeholder text.... static const sf::Color text_placeholder_color(255, 255, 255, 100); static const sf::Color front_color(55, 60, 68); static const float background_margin_horizontal = 15.0f; static const float PADDING_HORIZONTAL = 25.0f; static const float padding_top = 10.0f; static const float padding_bottom = 15.0f; static const float background_margin_vertical = 4.0f; namespace QuickMedia { SearchBar::SearchBar(sf::Font &font, sf::Texture *plugin_logo, const std::string &placeholder, bool input_masked) : onTextUpdateCallback(nullptr), onTextSubmitCallback(nullptr), onTextBeginTypingCallback(nullptr), onAutocompleteRequestCallback(nullptr), text_autosearch_delay(0), autocomplete_search_delay(0), caret_visible(true), text(placeholder, font, 16), autocomplete_text("", font, 16), background(sf::Vector2f(1.0f, 1.0f), 10.0f, 10), placeholder_str(placeholder), show_placeholder(true), updated_search(false), updated_autocomplete(false), draw_logo(false), needs_update(true), input_masked(input_masked), typing(false), vertical_pos(0.0f) { text.setFillColor(text_placeholder_color); autocomplete_text.setFillColor(text_placeholder_color); background.setFillColor(front_color); //background.setCornersRadius(5); background_shadow.setFillColor(sf::Color(23, 25, 27)); //background_shadow.setPosition(background.getPosition() + sf::Vector2f(5.0f, 5.0f)); shade.setFillColor(sf::Color(33, 38, 44)); //background.setOutlineThickness(1.0f); //background.setOutlineColor(sf::Color(13, 15, 17)); if(plugin_logo && plugin_logo->getNativeHandle() != 0) plugin_logo_sprite.setTexture(*plugin_logo, true); } void SearchBar::draw(sf::RenderWindow &window, bool draw_shadow) { if(needs_update) { needs_update = false; sf::Vector2u window_size = window.getSize(); onWindowResize(sf::Vector2f(window_size.x, window_size.y)); } (void)draw_shadow; //if(draw_shadow) // window.draw(background_shadow); window.draw(shade); window.draw(background); // TODO: Render starting from the character after text length window.draw(autocomplete_text); 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) window.draw(caret); if(draw_logo) window.draw(plugin_logo_sprite); } void SearchBar::on_event(sf::Event &event) { if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::V && event.key.control) { append_text(sf::Clipboard::getString()); } } void SearchBar::update() { sf::Int32 elapsed_time = time_since_search_update.getElapsedTime().asMilliseconds(); if(updated_search && elapsed_time >= text_autosearch_delay) { 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; } else if(updated_autocomplete && elapsed_time >= autocomplete_search_delay) { updated_autocomplete = false; if(!show_placeholder && onAutocompleteRequestCallback) { auto u8 = text.getString().toUtf8(); std::string *u8_str = (std::string*)&u8; onAutocompleteRequestCallback(*u8_str); } } } void SearchBar::onWindowResize(const sf::Vector2f &window_size) { draw_logo = plugin_logo_sprite.getTexture() != nullptr; float padding_horizontal = PADDING_HORIZONTAL; if(window_size.x - padding_horizontal * 2.0f < 400.0f) { padding_horizontal = 0.0f; draw_logo = false; } float font_height = text.getLocalBounds().height + 12.0f; float rect_height = std::floor(font_height + background_margin_vertical * 2.0f); float offset_x = padding_horizontal; 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(25.0f, padding_top + vertical_pos); offset_x = 25.0f + new_size.x + 25.0f; } const float width = std::floor(window_size.x - offset_x - padding_horizontal); background.setSize(sf::Vector2f(width, rect_height)); shade.setSize(sf::Vector2f(window_size.x, padding_top + rect_height + padding_bottom)); caret.setSize(sf::Vector2f(2.0f, text.getCharacterSize() + 2.0f)); background_shadow.setSize(sf::Vector2f(window_size.x, 5.0f)); background.setPosition(offset_x, padding_top + vertical_pos); shade.setPosition(0.0f, vertical_pos); background_shadow.setPosition(0.0f, std::floor(shade.getSize().y + vertical_pos)); sf::Vector2f font_position(std::floor(offset_x + background_margin_horizontal), std::floor(padding_top + background_margin_vertical + vertical_pos)); autocomplete_text.setPosition(font_position); 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(text_placeholder_color); autocomplete_text.setString(""); } else { clear_autocomplete_if_text_not_substring(); } if(!updated_search) { typing = true; if(onTextBeginTypingCallback) onTextBeginTypingCallback(); } updated_search = true; updated_autocomplete = true; time_since_search_update.restart(); } } else if(codepoint == 13) { // Return 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(text_placeholder_color); autocomplete_text.setString(""); needs_update = true; updated_search = false; updated_autocomplete = false; } 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 += text_to_add; text.setString(str); clear_autocomplete_if_text_not_substring(); if(!updated_search) { typing = true; if(onTextBeginTypingCallback) onTextBeginTypingCallback(); } updated_search = true; updated_autocomplete = true; time_since_search_update.restart(); if(str[str.getSize() - 1] == '\n') needs_update = true; } bool SearchBar::is_cursor_at_start_of_line() const { // TODO: When it's possible to move the cursor, then check at the cursor position instead of end of the string const sf::String &str = text.getString(); return show_placeholder || str.getSize() == 0 || str[str.getSize() - 1] == '\n'; } void SearchBar::set_to_autocomplete() { const sf::String &autocomplete_str = autocomplete_text.getString(); if(!autocomplete_str.isEmpty()) { if(show_placeholder) { show_placeholder = false; text.setString(""); text.setFillColor(sf::Color::White); } text.setString(autocomplete_str); if(!updated_search) { typing = true; if(onTextBeginTypingCallback) onTextBeginTypingCallback(); } updated_search = true; updated_autocomplete = true; time_since_search_update.restart(); needs_update = true; } } void SearchBar::set_autocomplete_text(const std::string &text) { autocomplete_text.setString(text); } void SearchBar::set_vertical_position(float vertical_pos) { if(std::abs(this->vertical_pos - vertical_pos) > 1.0f) { this->vertical_pos = vertical_pos; needs_update = true; } } void SearchBar::set_background_color(sf::Color color) { background.setFillColor(color); } void SearchBar::clear_autocomplete_if_text_not_substring() { const sf::String &text_str = text.getString(); const sf::String &autocomplete_str = autocomplete_text.getString(); if(text_str.getSize() > autocomplete_str.getSize()) { autocomplete_text.setString(""); return; } for(size_t i = 0; i < autocomplete_str.getSize(); ++i) { if(text_str[i] != autocomplete_str[i]) { autocomplete_text.setString(""); return; } } } void SearchBar::clear_autocomplete_if_last_char_not_substr() { const sf::String &text_str = text.getString(); const sf::String &autocomplete_str = autocomplete_text.getString(); if(text_str.isEmpty() || text_str.getSize() > autocomplete_str.getSize()) { autocomplete_text.setString(""); return; } if(autocomplete_str[text_str.getSize() - 1] != text_str[text_str.getSize() - 1]) { autocomplete_text.setString(""); return; } } 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(); } }