diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index 3c63ab0bc5..643bd2dd7e 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -64,12 +64,19 @@ auto SuggestionsWidget::triggered() const -> rpl::producer { return _triggered.events(); } -void SuggestionsWidget::showWithQuery(const QString &query, bool force) { +void SuggestionsWidget::showWithQuery(SuggestionsQuery query, bool force) { if (!force && (_query == query)) { return; } _query = query; - auto rows = prependCustom(getRowsByQuery()); + auto rows = [&] { + if (const auto emoji = std::get_if(&query)) { + return prependCustom( + {}, + lookupCustom({ Row(*emoji, (*emoji)->text()) })); + } + return prependCustom(getRowsByQuery(v::get(query))); + }(); if (rows.empty()) { _toggleAnimated.fire(false); } @@ -94,14 +101,15 @@ void SuggestionsWidget::selectFirstResult() { auto SuggestionsWidget::prependCustom(std::vector rows) -> std::vector { + const auto custom = lookupCustom(rows); + return prependCustom(std::move(rows), custom); +} + +auto SuggestionsWidget::lookupCustom(const std::vector &rows) const +-> base::flat_multi_map { if (rows.empty()) { return {}; } - struct Custom { - not_null document; - not_null emoji; - QString replacement; - }; auto custom = base::flat_multi_map(); const auto premium = _session->premium(); const auto stickers = &_session->data().stickers(); @@ -132,6 +140,13 @@ auto SuggestionsWidget::prependCustom(std::vector rows) } } } + return custom; +} + +auto SuggestionsWidget::prependCustom( + std::vector rows, + const base::flat_multi_map &custom) +-> std::vector { if (custom.empty()) { return rows; } @@ -179,18 +194,19 @@ SuggestionsWidget::Row::Row( , replacement(replacement) { } -auto SuggestionsWidget::getRowsByQuery() const -> std::vector { - if (_query.isEmpty()) { +auto SuggestionsWidget::getRowsByQuery(const QString &text) const +-> std::vector { + if (text.isEmpty()) { return {}; } - const auto middle = (_query[0] == ':'); - const auto real = middle ? _query.mid(1) : _query; + const auto middle = (text[0] == ':'); + const auto real = middle ? text.mid(1) : text; const auto simple = [&] { - if (!middle || _query.size() > 2) { + if (!middle || text.size() > 2) { return false; } // Suggest :D and :-P only as exact matches. - return ranges::none_of(_query, [](QChar ch) { return ch.isLower(); }); + return ranges::none_of(text, [](QChar ch) { return ch.isLower(); }); }(); const auto exact = !middle || simple; const auto list = Core::App().emojiKeywords().query(real, exact); @@ -730,8 +746,14 @@ void SuggestionsController::handleTextChange() { InvokeQueued(_container, [=] { _ignoreCursorPositionChange = false; }); const auto query = getEmojiQuery(); - if (query.isEmpty() || _textChangeAfterKeyPress) { - const auto exact = (!query.isEmpty() && query[0] != ':'); + if (v::is(query)) { + showWithQuery(query); + _suggestions->selectFirstResult(); + return; + } + const auto text = v::get(query); + if (text.isEmpty() || _textChangeAfterKeyPress) { + const auto exact = !text.isEmpty() && (text[0] != ':'); if (exact) { const auto hidden = _container->isHidden() || _container->isHiding(); @@ -743,14 +765,14 @@ void SuggestionsController::handleTextChange() { } } -void SuggestionsController::showWithQuery(const QString &query) { +void SuggestionsController::showWithQuery(SuggestionsQuery query) { _showExactTimer.cancel(); const auto force = base::take(_keywordsRefreshed); _lastShownQuery = query; _suggestions->showWithQuery(_lastShownQuery, force); } -QString SuggestionsController::getEmojiQuery() { +SuggestionsQuery SuggestionsController::getEmojiQuery() { if (!Core::App().settings().suggestEmoji()) { return QString(); } @@ -763,7 +785,7 @@ QString SuggestionsController::getEmojiQuery() { const auto modernLimit = Core::App().emojiKeywords().maxQueryLength(); const auto legacyLimit = GetSuggestionMaxLength(); const auto position = cursor.position(); - const auto findTextPart = [&] { + const auto findTextPart = [&]() -> SuggestionsQuery { auto document = _field->document(); auto block = document->findBlock(position); for (auto i = block.begin(); !i.atEnd(); ++i) { @@ -776,8 +798,13 @@ QString SuggestionsController::getEmojiQuery() { continue; } const auto format = fragment.charFormat(); - if (format.isImageFormat() - || format.objectType() == InputField::kCustomEmojiFormat) { + if (format.objectType() == InputField::kCustomEmojiFormat) { + continue; + } else if (format.isImageFormat()) { + const auto imageName = format.toImageFormat().name(); + if (const auto emoji = Emoji::FromUrl(imageName)) { + return emoji; + } continue; } _queryStartPosition = from; @@ -786,7 +813,11 @@ QString SuggestionsController::getEmojiQuery() { return QString(); }; - const auto text = findTextPart(); + const auto part = findTextPart(); + if (const auto emoji = std::get_if(&part)) { + return *emoji; + } + const auto text = v::get(part); if (text.isEmpty()) { return QString(); } @@ -828,12 +859,15 @@ void SuggestionsController::replaceCurrent( const QString &replacement, const QString &customEmojiData) { const auto suggestion = getEmojiQuery(); - if (suggestion.isEmpty()) { + const auto length = v::is(suggestion) + ? 1 + : v::get(suggestion).size(); + if (!length) { showWithQuery(QString()); } else { const auto cursor = _field->textCursor(); const auto position = cursor.position(); - const auto from = position - suggestion.size(); + const auto from = position - length; _replaceCallback(from, position, replacement, customEmojiData); } } diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.h index 5f6829c5c6..e25794c159 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.h @@ -29,12 +29,14 @@ class CustomEmoji; namespace Ui::Emoji { +using SuggestionsQuery = std::variant; + class SuggestionsWidget final : public Ui::RpWidget { public: SuggestionsWidget(QWidget *parent, not_null session); ~SuggestionsWidget(); - void showWithQuery(const QString &query, bool force = false); + void showWithQuery(SuggestionsQuery query, bool force = false); void selectFirstResult(); bool handleKeyEvent(int key); @@ -55,6 +57,11 @@ private: not_null emoji; QString replacement; }; + struct Custom { + not_null document; + not_null emoji; + QString replacement; + }; bool eventHook(QEvent *e) override; void paintEvent(QPaintEvent *e) override; @@ -68,8 +75,14 @@ private: void scrollByWheelEvent(not_null e); void paintFadings(Painter &p) const; - [[nodiscard]] std::vector getRowsByQuery() const; - [[nodiscard]] std::vector prependCustom(std::vector rows); + [[nodiscard]] std::vector getRowsByQuery(const QString &text) const; + [[nodiscard]] base::flat_multi_map lookupCustom( + const std::vector &rows) const; + [[nodiscard]] std::vector prependCustom( + std::vector rows); + [[nodiscard]] std::vector prependCustom( + std::vector rows, + const base::flat_multi_map &custom); void resizeToRows(); void setSelected( int selected, @@ -95,7 +108,7 @@ private: void customEmojiRepaint(); const not_null _session; - QString _query; + SuggestionsQuery _query; std::vector _rows; base::flat_map< @@ -154,8 +167,8 @@ public: private: void handleCursorPositionChange(); void handleTextChange(); - void showWithQuery(const QString &query); - [[nodiscard]] QString getEmojiQuery(); + void showWithQuery(SuggestionsQuery query); + [[nodiscard]] SuggestionsQuery getEmojiQuery(); void suggestionsUpdated(bool visible); void updateGeometry(); void updateForceHidden(); @@ -183,7 +196,7 @@ private: base::unique_qptr _outerFilter; base::Timer _showExactTimer; bool _keywordsRefreshed = false; - QString _lastShownQuery; + SuggestionsQuery _lastShownQuery; Options _options; rpl::lifetime _lifetime;