Suggest animated emoji by regular emoji.

This commit is contained in:
John Preston 2022-08-02 17:57:59 +03:00
parent e7b3416da8
commit 2319278c92
2 changed files with 77 additions and 30 deletions

View file

@ -64,12 +64,19 @@ auto SuggestionsWidget::triggered() const -> rpl::producer<Chosen> {
return _triggered.events(); return _triggered.events();
} }
void SuggestionsWidget::showWithQuery(const QString &query, bool force) { void SuggestionsWidget::showWithQuery(SuggestionsQuery query, bool force) {
if (!force && (_query == query)) { if (!force && (_query == query)) {
return; return;
} }
_query = query; _query = query;
auto rows = prependCustom(getRowsByQuery()); auto rows = [&] {
if (const auto emoji = std::get_if<EmojiPtr>(&query)) {
return prependCustom(
{},
lookupCustom({ Row(*emoji, (*emoji)->text()) }));
}
return prependCustom(getRowsByQuery(v::get<QString>(query)));
}();
if (rows.empty()) { if (rows.empty()) {
_toggleAnimated.fire(false); _toggleAnimated.fire(false);
} }
@ -94,14 +101,15 @@ void SuggestionsWidget::selectFirstResult() {
auto SuggestionsWidget::prependCustom(std::vector<Row> rows) auto SuggestionsWidget::prependCustom(std::vector<Row> rows)
-> std::vector<Row> { -> std::vector<Row> {
const auto custom = lookupCustom(rows);
return prependCustom(std::move(rows), custom);
}
auto SuggestionsWidget::lookupCustom(const std::vector<Row> &rows) const
-> base::flat_multi_map<int, Custom> {
if (rows.empty()) { if (rows.empty()) {
return {}; return {};
} }
struct Custom {
not_null<DocumentData*> document;
not_null<EmojiPtr> emoji;
QString replacement;
};
auto custom = base::flat_multi_map<int, Custom>(); auto custom = base::flat_multi_map<int, Custom>();
const auto premium = _session->premium(); const auto premium = _session->premium();
const auto stickers = &_session->data().stickers(); const auto stickers = &_session->data().stickers();
@ -132,6 +140,13 @@ auto SuggestionsWidget::prependCustom(std::vector<Row> rows)
} }
} }
} }
return custom;
}
auto SuggestionsWidget::prependCustom(
std::vector<Row> rows,
const base::flat_multi_map<int, Custom> &custom)
-> std::vector<Row> {
if (custom.empty()) { if (custom.empty()) {
return rows; return rows;
} }
@ -179,18 +194,19 @@ SuggestionsWidget::Row::Row(
, replacement(replacement) { , replacement(replacement) {
} }
auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> { auto SuggestionsWidget::getRowsByQuery(const QString &text) const
if (_query.isEmpty()) { -> std::vector<Row> {
if (text.isEmpty()) {
return {}; return {};
} }
const auto middle = (_query[0] == ':'); const auto middle = (text[0] == ':');
const auto real = middle ? _query.mid(1) : _query; const auto real = middle ? text.mid(1) : text;
const auto simple = [&] { const auto simple = [&] {
if (!middle || _query.size() > 2) { if (!middle || text.size() > 2) {
return false; return false;
} }
// Suggest :D and :-P only as exact matches. // 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 exact = !middle || simple;
const auto list = Core::App().emojiKeywords().query(real, exact); const auto list = Core::App().emojiKeywords().query(real, exact);
@ -730,8 +746,14 @@ void SuggestionsController::handleTextChange() {
InvokeQueued(_container, [=] { _ignoreCursorPositionChange = false; }); InvokeQueued(_container, [=] { _ignoreCursorPositionChange = false; });
const auto query = getEmojiQuery(); const auto query = getEmojiQuery();
if (query.isEmpty() || _textChangeAfterKeyPress) { if (v::is<EmojiPtr>(query)) {
const auto exact = (!query.isEmpty() && query[0] != ':'); showWithQuery(query);
_suggestions->selectFirstResult();
return;
}
const auto text = v::get<QString>(query);
if (text.isEmpty() || _textChangeAfterKeyPress) {
const auto exact = !text.isEmpty() && (text[0] != ':');
if (exact) { if (exact) {
const auto hidden = _container->isHidden() const auto hidden = _container->isHidden()
|| _container->isHiding(); || _container->isHiding();
@ -743,14 +765,14 @@ void SuggestionsController::handleTextChange() {
} }
} }
void SuggestionsController::showWithQuery(const QString &query) { void SuggestionsController::showWithQuery(SuggestionsQuery query) {
_showExactTimer.cancel(); _showExactTimer.cancel();
const auto force = base::take(_keywordsRefreshed); const auto force = base::take(_keywordsRefreshed);
_lastShownQuery = query; _lastShownQuery = query;
_suggestions->showWithQuery(_lastShownQuery, force); _suggestions->showWithQuery(_lastShownQuery, force);
} }
QString SuggestionsController::getEmojiQuery() { SuggestionsQuery SuggestionsController::getEmojiQuery() {
if (!Core::App().settings().suggestEmoji()) { if (!Core::App().settings().suggestEmoji()) {
return QString(); return QString();
} }
@ -763,7 +785,7 @@ QString SuggestionsController::getEmojiQuery() {
const auto modernLimit = Core::App().emojiKeywords().maxQueryLength(); const auto modernLimit = Core::App().emojiKeywords().maxQueryLength();
const auto legacyLimit = GetSuggestionMaxLength(); const auto legacyLimit = GetSuggestionMaxLength();
const auto position = cursor.position(); const auto position = cursor.position();
const auto findTextPart = [&] { const auto findTextPart = [&]() -> SuggestionsQuery {
auto document = _field->document(); auto document = _field->document();
auto block = document->findBlock(position); auto block = document->findBlock(position);
for (auto i = block.begin(); !i.atEnd(); ++i) { for (auto i = block.begin(); !i.atEnd(); ++i) {
@ -776,8 +798,13 @@ QString SuggestionsController::getEmojiQuery() {
continue; continue;
} }
const auto format = fragment.charFormat(); const auto format = fragment.charFormat();
if (format.isImageFormat() if (format.objectType() == InputField::kCustomEmojiFormat) {
|| 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; continue;
} }
_queryStartPosition = from; _queryStartPosition = from;
@ -786,7 +813,11 @@ QString SuggestionsController::getEmojiQuery() {
return QString(); return QString();
}; };
const auto text = findTextPart(); const auto part = findTextPart();
if (const auto emoji = std::get_if<EmojiPtr>(&part)) {
return *emoji;
}
const auto text = v::get<QString>(part);
if (text.isEmpty()) { if (text.isEmpty()) {
return QString(); return QString();
} }
@ -828,12 +859,15 @@ void SuggestionsController::replaceCurrent(
const QString &replacement, const QString &replacement,
const QString &customEmojiData) { const QString &customEmojiData) {
const auto suggestion = getEmojiQuery(); const auto suggestion = getEmojiQuery();
if (suggestion.isEmpty()) { const auto length = v::is<EmojiPtr>(suggestion)
? 1
: v::get<QString>(suggestion).size();
if (!length) {
showWithQuery(QString()); showWithQuery(QString());
} else { } else {
const auto cursor = _field->textCursor(); const auto cursor = _field->textCursor();
const auto position = cursor.position(); const auto position = cursor.position();
const auto from = position - suggestion.size(); const auto from = position - length;
_replaceCallback(from, position, replacement, customEmojiData); _replaceCallback(from, position, replacement, customEmojiData);
} }
} }

View file

@ -29,12 +29,14 @@ class CustomEmoji;
namespace Ui::Emoji { namespace Ui::Emoji {
using SuggestionsQuery = std::variant<QString, EmojiPtr>;
class SuggestionsWidget final : public Ui::RpWidget { class SuggestionsWidget final : public Ui::RpWidget {
public: public:
SuggestionsWidget(QWidget *parent, not_null<Main::Session*> session); SuggestionsWidget(QWidget *parent, not_null<Main::Session*> session);
~SuggestionsWidget(); ~SuggestionsWidget();
void showWithQuery(const QString &query, bool force = false); void showWithQuery(SuggestionsQuery query, bool force = false);
void selectFirstResult(); void selectFirstResult();
bool handleKeyEvent(int key); bool handleKeyEvent(int key);
@ -55,6 +57,11 @@ private:
not_null<EmojiPtr> emoji; not_null<EmojiPtr> emoji;
QString replacement; QString replacement;
}; };
struct Custom {
not_null<DocumentData*> document;
not_null<EmojiPtr> emoji;
QString replacement;
};
bool eventHook(QEvent *e) override; bool eventHook(QEvent *e) override;
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
@ -68,8 +75,14 @@ private:
void scrollByWheelEvent(not_null<QWheelEvent*> e); void scrollByWheelEvent(not_null<QWheelEvent*> e);
void paintFadings(Painter &p) const; void paintFadings(Painter &p) const;
[[nodiscard]] std::vector<Row> getRowsByQuery() const; [[nodiscard]] std::vector<Row> getRowsByQuery(const QString &text) const;
[[nodiscard]] std::vector<Row> prependCustom(std::vector<Row> rows); [[nodiscard]] base::flat_multi_map<int, Custom> lookupCustom(
const std::vector<Row> &rows) const;
[[nodiscard]] std::vector<Row> prependCustom(
std::vector<Row> rows);
[[nodiscard]] std::vector<Row> prependCustom(
std::vector<Row> rows,
const base::flat_multi_map<int, Custom> &custom);
void resizeToRows(); void resizeToRows();
void setSelected( void setSelected(
int selected, int selected,
@ -95,7 +108,7 @@ private:
void customEmojiRepaint(); void customEmojiRepaint();
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
QString _query; SuggestionsQuery _query;
std::vector<Row> _rows; std::vector<Row> _rows;
base::flat_map< base::flat_map<
@ -154,8 +167,8 @@ public:
private: private:
void handleCursorPositionChange(); void handleCursorPositionChange();
void handleTextChange(); void handleTextChange();
void showWithQuery(const QString &query); void showWithQuery(SuggestionsQuery query);
[[nodiscard]] QString getEmojiQuery(); [[nodiscard]] SuggestionsQuery getEmojiQuery();
void suggestionsUpdated(bool visible); void suggestionsUpdated(bool visible);
void updateGeometry(); void updateGeometry();
void updateForceHidden(); void updateForceHidden();
@ -183,7 +196,7 @@ private:
base::unique_qptr<QObject> _outerFilter; base::unique_qptr<QObject> _outerFilter;
base::Timer _showExactTimer; base::Timer _showExactTimer;
bool _keywordsRefreshed = false; bool _keywordsRefreshed = false;
QString _lastShownQuery; SuggestionsQuery _lastShownQuery;
Options _options; Options _options;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;