From 44a7d11e4a6471e784ad1ef5d1b83a3a8730dbc8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 14 Jul 2022 14:20:56 +0300 Subject: [PATCH] Support mixed custom / default recent emoji. --- .../SourceFiles/boxes/sticker_set_box.cpp | 2 +- .../chat_helpers/emoji_list_widget.cpp | 363 ++++++++++++++---- .../chat_helpers/emoji_list_widget.h | 34 +- .../chat_helpers/emoji_suggestions_widget.cpp | 12 +- Telegram/SourceFiles/core/core_settings.cpp | 53 +-- Telegram/SourceFiles/core/core_settings.h | 16 +- Telegram/SourceFiles/core/ui_integration.cpp | 2 +- .../data/stickers/data_custom_emoji.cpp | 31 +- .../data/stickers/data_custom_emoji.h | 3 + .../data/stickers/data_stickers_set.h | 2 +- 10 files changed, 401 insertions(+), 117 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index d74739ca0..04b63d665 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -201,7 +201,7 @@ private: uint64 _setId = 0; uint64 _setAccessHash = 0; uint64 _setHash = 0; - uint64 _setThumbnailDocumentId = 0; + DocumentId _setThumbnailDocumentId = 0; QString _setTitle, _setShortName; int _setCount = 0; Data::StickersSetFlags _setFlags; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index af8f722ac..71a810200 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -34,6 +34,81 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_chat_helpers.h" namespace ChatHelpers { +namespace { + +constexpr auto kFakeEmojiDocumentIdBase = 0x7777'FFFF'FFFF'0000ULL; + +[[nodiscard]] DocumentId FakeEmojiDocumentId(EmojiPtr emoji) { + return kFakeEmojiDocumentIdBase + emoji->index(); +} + +class DefaultEmojiLoader final : public Ui::CustomEmoji::Loader { +public: + DefaultEmojiLoader(EmojiPtr emoji, int size); + + QString entityData() override; + + void load(Fn loaded) override; + bool loading() override; + void cancel() override; + Ui::CustomEmoji::Preview preview() override; + +private: + void validateImage(); + + EmojiPtr _emoji = nullptr; + QImage _image; + int _size = 0; + +}; + +DefaultEmojiLoader::DefaultEmojiLoader(EmojiPtr emoji, int size) +: _emoji(emoji) +, _size(size) { +} + +void DefaultEmojiLoader::load(Fn loaded) { + validateImage(); + const auto data = entityData(); + const auto unloader = [emoji = _emoji, size = _size] { + return std::make_unique(emoji, size); + }; + auto cache = Ui::CustomEmoji::Cache(_size); + cache.add(0, _image); + cache.finish(); + loaded(Ui::CustomEmoji::Cached(data, unloader, std::move(cache))); +} + +void DefaultEmojiLoader::validateImage() { + if (!_image.isNull()) { + return; + } + _image = QImage( + { _size, _size }, + QImage::Format_ARGB32_Premultiplied); + _image.setDevicePixelRatio(style::DevicePixelRatio()); + _image.fill(Qt::transparent); + QPainter p(&_image); + Ui::Emoji::Draw(p, _emoji, _size, 0, 0); +} + +QString DefaultEmojiLoader::entityData() { + return "default-emoji://" + _emoji->id(); +} + +bool DefaultEmojiLoader::loading() { + return false; +} + +void DefaultEmojiLoader::cancel() { +} + +Ui::CustomEmoji::Preview DefaultEmojiLoader::preview() { + validateImage(); + return { _image }; +} + +} // namespace class EmojiColorPicker : public Ui::RpWidget { public: @@ -92,10 +167,12 @@ struct EmojiListWidget::CustomInstance { Fn, Ui::CustomEmoji::RepaintRequest)> repaintLater, - Fn repaint); + Fn repaint, + bool recentOnly = false); Ui::CustomEmoji::Instance emoji; Ui::CustomEmoji::Object object; + bool recentOnly = false; }; EmojiListWidget::CustomInstance::CustomInstance( @@ -103,11 +180,13 @@ EmojiListWidget::CustomInstance::CustomInstance( Fn, Ui::CustomEmoji::RepaintRequest)> repaintLater, - Fn repaint) + Fn repaint, + bool recentOnly) : emoji( Ui::CustomEmoji::Loading(std::move(loader), Ui::CustomEmoji::Preview()), std::move(repaintLater)) -, object(&emoji, std::move(repaint)) { +, object(&emoji, std::move(repaint)) +, recentOnly(recentOnly) { } EmojiColorPicker::EmojiColorPicker(QWidget *parent) @@ -384,6 +463,7 @@ EmojiListWidget::~EmojiListWidget() { } void EmojiListWidget::repaintLater( + DocumentId documentId, uint64 setId, Ui::CustomEmoji::RepaintRequest request) { if (_instances.empty()) { @@ -393,7 +473,12 @@ void EmojiListWidget::repaintLater( if (repaint.when < request.when) { repaint.when = request.when; } - repaint.ids.emplace(setId); + if (setId) { + repaint.ids.emplace(setId); + } + if (_recentCustomIds.contains(documentId)) { + repaint.ids.emplace(RecentEmojiSectionSetId()); + } scheduleRepaintTimer(); } @@ -450,8 +535,12 @@ void EmojiListWidget::invokeRepaints() { template void EmojiListWidget::repaintCustom(CheckId checkId) { enumerateSections([&](const SectionInfo &info) { - if (info.section >= kEmojiSectionCount - && checkId(_custom[info.section - kEmojiSectionCount].id)) { + const auto repaint1 = (info.section == int(Section::Recent)) + && checkId(RecentEmojiSectionSetId()); + const auto repaint2 = !repaint1 + && (info.section >= kEmojiSectionCount) + && checkId(_custom[info.section - kEmojiSectionCount].id); + if (repaint1 || repaint2) { update( 0, info.rowsTop, @@ -618,16 +707,17 @@ int EmojiListWidget::countDesiredHeight(int newWidth) { void EmojiListWidget::ensureLoaded(int section) { Expects(section >= 0 && section < sectionsCount()); - if (section >= kEmojiSectionCount || !_emoji[section].empty()) { + if (section == int(Section::Recent)) { + if (_recent.empty()) { + fillRecent(); + } + return; + } else if (section >= kEmojiSectionCount || !_emoji[section].empty()) { return; } - _emoji[section] = (static_cast
(section) == Section::Recent) - ? Core::App().settings().recentEmojiSection() - : Ui::Emoji::GetSection(static_cast
(section)); + _emoji[section] = Ui::Emoji::GetSection(static_cast
(section)); _counts[section] = _emoji[section].size(); - if (static_cast
(section) == Section::Recent) { - return; - } + const auto &variants = Core::App().settings().emojiVariants(); for (auto &emoji : _emoji[section]) { if (emoji->hasVariants()) { @@ -639,6 +729,24 @@ void EmojiListWidget::ensureLoaded(int section) { } } +void EmojiListWidget::fillRecent() { + _recent.clear(); + _recentCustomIds.clear(); + + const auto &list = Core::App().settings().recentEmoji(); + _recent.reserve(list.size()); + for (const auto &one : list) { + _recent.push_back({ + .instance = resolveCustomInstance(one.id.data), + .id = one.id.data, + }); + if (const auto documentId = std::get_if(&one.id.data)) { + _recentCustomIds.emplace(*documentId); + } + } + _counts[0] = _recent.size(); +} + void EmojiListWidget::paintEvent(QPaintEvent *e) { Painter p(this); QRect r = e ? e->rect() : rect(); @@ -720,7 +828,9 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { if (rtl()) tl.setX(width() - tl.x() - _singleSize.width()); Ui::FillRoundRect(p, QRect(tl, _singleSize), st::emojiPanHover, Ui::StickerHoverCorners); } - if (info.section < kEmojiSectionCount) { + if (info.section == int(Section::Recent)) { + drawRecent(p, w, now, paused, index); + } else if (info.section < kEmojiSectionCount) { drawEmoji(p, w, info.section, index); } else { const auto set = info.section - kEmojiSectionCount; @@ -733,6 +843,23 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { }); } +void EmojiListWidget::drawRecent( + QPainter &p, + QPoint position, + crl::time now, + bool paused, + int index) { + const auto size = (_esize / cIntRetinaFactor()); + _recentPainted = true; + _recent[index].instance->object.paint( + p, + position.x() + (_singleSize.width() - size) / 2, + position.y() + (_singleSize.height() - size) / 2, + now, + st::windowBgRipple->c, + paused); +} + void EmojiListWidget::drawEmoji( QPainter &p, QPoint position, @@ -775,6 +902,20 @@ bool EmojiListWidget::checkPickerHide() { return false; } +EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const { + const auto section = over ? over->section : -1; + const auto index = over ? over->index : -1; + return (section == int(Section::Recent) + && index < _recent.size() + && v::is(_recent[index].id)) + ? v::get(_recent[index].id) + : (section > int(Section::Recent) + && section < kEmojiSectionCount + && index < _emoji[section].size()) + ? _emoji[section][index] + : nullptr; +} + void EmojiListWidget::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); @@ -783,13 +924,12 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) { } setPressed(_selected); if (const auto over = std::get_if(&_selected)) { - if (over->section < kEmojiSectionCount - && over->index < _emoji[over->section].size() - && _emoji[over->section][over->index]->hasVariants()) { + const auto emoji = lookupOverEmoji(over); + if (emoji && emoji->hasVariants()) { _pickerSelected = _selected; setCursor(style::cur_default); const auto &variants = Core::App().settings().emojiVariants(); - if (!variants.contains(_emoji[over->section][over->index]->nonColoredId())) { + if (!variants.contains(emoji->nonColoredId())) { showPicker(); } else { _showPickerTimer.callOnce(500); @@ -806,13 +946,10 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) { return _picker->handleMouseRelease(QCursor::pos()); } else if (const auto over = std::get_if(&_pickerSelected)) { - const auto section = over->section; - const auto index = over->index; - if (section < kEmojiSectionCount - && index < _emoji[section].size() - && _emoji[section][index]->hasVariants()) { + const auto emoji = lookupOverEmoji(over); + if (emoji && emoji->hasVariants()) { const auto &variants = Core::App().settings().emojiVariants(); - if (variants.contains(_emoji[section][index]->nonColoredId())) { + if (variants.contains(emoji->nonColoredId())) { _picker->hideAnimated(); _pickerSelected = v::null; } @@ -834,12 +971,20 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { if (const auto over = std::get_if(&_selected)) { const auto section = over->section; const auto index = over->index; - if (section < kEmojiSectionCount && index < _emoji[section].size()) { - const auto emoji = _emoji[section][index]; + if (const auto emoji = lookupOverEmoji(over)) { if (emoji->hasVariants() && !_picker->isHidden()) { return; } selectEmoji(emoji); + } else if (section == int(Section::Recent) + && index < _recent.size()) { + const auto id = std::get_if(&_recent[index].id); + const auto document = id + ? session().data().document(*id).get() + : nullptr; + if (document && document->sticker()) { + selectCustom(document); + } } else if (section >= kEmojiSectionCount && index < _custom[section - kEmojiSectionCount].list.size()) { auto &set = _custom[section - kEmojiSectionCount]; @@ -875,11 +1020,12 @@ void EmojiListWidget::removeSet(uint64 setId) { } void EmojiListWidget::selectEmoji(EmojiPtr emoji) { - Core::App().settings().incrementRecentEmoji(emoji); + Core::App().settings().incrementRecentEmoji({ emoji }); _chosen.fire_copy(emoji); } void EmojiListWidget::selectCustom(not_null document) { + Core::App().settings().incrementRecentEmoji({ document->id }); _customChosen.fire({ .document = document }); } @@ -889,14 +1035,11 @@ void EmojiListWidget::showPicker() { } const auto over = std::get_if(&_pickerSelected); - const auto section = over ? over->section : -1; - if (section >= 0 - && section < kEmojiSectionCount - && over->index < _emoji[section].size() - && _emoji[section][over->index]->hasVariants()) { - _picker->showEmoji(_emoji[section][over->index]); + const auto emoji = lookupOverEmoji(over); + if (emoji && emoji->hasVariants()) { + _picker->showEmoji(emoji); - auto y = emojiRect(section, over->index).y(); + auto y = emojiRect(over->section, over->index).y(); y -= _picker->height() - st::roundRadiusSmall + getVisibleTop(); if (y < st::emojiPanHeader) { y += _picker->height() - st::roundRadiusSmall + _singleSize.height() - st::roundRadiusSmall; @@ -960,7 +1103,10 @@ void EmojiListWidget::colorChosen(EmojiPtr emoji) { Core::App().settings().saveEmojiVariant(emoji); } const auto over = std::get_if(&_pickerSelected); - if (over && over->section >= 0 && over->section < kEmojiSectionCount) { + if (over + && over->section > int(Section::Recent) + && over->section < kEmojiSectionCount + && over->index < _emoji[over->section].size()) { _emoji[over->section][over->index] = emoji; rtlupdate(emojiRect(over->section, over->index)); } @@ -1006,13 +1152,8 @@ uint64 EmojiListWidget::currentSet(int yOffset) const { QString EmojiListWidget::tooltipText() const { const auto &replacements = Ui::Emoji::internal::GetAllReplacements(); const auto over = std::get_if(&_selected); - const auto section = over ? over->section : -1; - const auto index = over ? over->index : -1; - if (section >= 0 - && section < kEmojiSectionCount - && index < _emoji[section].size()) { - const auto emoji = _emoji[section][index]->original(); - const auto text = emoji->text(); + if (const auto emoji = lookupOverEmoji(over)) { + const auto text = emoji->original()->text(); // find the replacement belonging to the emoji const auto it = ranges::find_if(replacements, [&](const auto &one) { return text == Ui::Emoji::QStringFromUTF16(one.emoji); @@ -1046,8 +1187,7 @@ void EmojiListWidget::processHideFinished() { void EmojiListWidget::refreshRecent() { clearSelection(); - _emoji[0] = Core::App().settings().recentEmojiSection(); - _counts[0] = _emoji[0].size(); + fillRecent(); resizeToWidth(width()); } @@ -1087,30 +1227,8 @@ void EmojiListWidget::refreshCustom() { set.reserve(list.size()); for (const auto document : list) { if (document->sticker()) { - auto i = _instances.find(document->id); - if (i == end(_instances)) { - auto loader = owner->customEmojiManager().createLoader( - document, - Data::CustomEmojiManager::SizeTag::Large); - const auto repaintDelayed = [=]( - not_null instance, - Ui::CustomEmoji::RepaintRequest request) { - repaintLater(setId, request); - }; - const auto repaintNow = [=] { - repaintCustom([&](uint64 id) { - return id == setId; - }); - }; - i = _instances.emplace( - document->id, - std::make_unique( - std::move(loader), - std::move(repaintDelayed), - std::move(repaintNow))).first; - } set.push_back({ - .instance = i->second.get(), + .instance = resolveCustomInstance(document, setId), .document = document, }); } @@ -1129,6 +1247,117 @@ void EmojiListWidget::refreshCustom() { ValidateIconAnimations::None); } +auto EmojiListWidget::customInstanceWithLoader( + std::unique_ptr loader, + DocumentId documentId, + uint64 setId) +-> std::unique_ptr { + const auto recentOnly = (setId == RecentEmojiSectionSetId()); + const auto repaintDelayedSetId = !recentOnly ? setId : uint64(0); + const auto repaintDelayed = [=]( + not_null instance, + Ui::CustomEmoji::RepaintRequest request) { + repaintLater(documentId, repaintDelayedSetId, request); + }; + const auto repaintNow = [=] { + if (_recentCustomIds.contains(documentId)) { + const auto recentSetId = RecentEmojiSectionSetId(); + repaintCustom([&](uint64 id) { + return (id == setId) || (id == recentSetId); + }); + } else { + repaintCustom([&](uint64 id) { + return (id == setId); + }); + } + }; + return std::make_unique( + std::move(loader), + std::move(repaintDelayed), + std::move(repaintNow), + recentOnly); +} + +auto EmojiListWidget::resolveCustomInstance( + not_null document, + uint64 setId) +-> not_null { + Expects(document->sticker() != nullptr); + + const auto documentId = document->id; + const auto i = _instances.find(documentId); + const auto recentOnly = (i != end(_instances)) && i->second->recentOnly; + if (i != end(_instances) && !recentOnly) { + return i->second.get(); + } + auto instance = customInstanceWithLoader( + document->owner().customEmojiManager().createLoader( + document, + Data::CustomEmojiManager::SizeTag::Large), + documentId, + setId); + if (recentOnly) { + for (auto &recent : _recent) { + if (recent.instance == i->second.get()) { + recent.instance = instance.get(); + } + } + i->second = std::move(instance); + return i->second.get(); + } + return _instances.emplace( + documentId, + std::move(instance)).first->second.get(); +} + +auto EmojiListWidget::resolveCustomInstance( + std::variant customId) +-> not_null { + if (const auto documentId = std::get_if(&customId)) { + return resolveCustomInstance(*documentId); + } else if (const auto emoji = std::get_if(&customId)) { + return resolveCustomInstance(FakeEmojiDocumentId(*emoji), *emoji); + } + Unexpected("Custom recent emoji id."); +} + +auto EmojiListWidget::resolveCustomInstance( + DocumentId fakeId, + EmojiPtr emoji) +-> not_null { + const auto i = _instances.find(fakeId); + if (i != end(_instances)) { + return i->second.get(); + } + return _instances.emplace( + fakeId, + std::make_unique( + std::make_unique( + emoji, + Ui::Emoji::GetSizeLarge()), + [](const auto&, const auto&) {}, + [] {}, + true)).first->second.get(); +} + +auto EmojiListWidget::resolveCustomInstance( + DocumentId documentId) +-> not_null { + const auto i = _instances.find(documentId); + if (i != end(_instances)) { + return i->second.get(); + } + return _instances.emplace( + documentId, + customInstanceWithLoader( + session().data().customEmojiManager().createLoader( + documentId, + Data::CustomEmojiManager::SizeTag::Large), + documentId, + RecentEmojiSectionSetId()) + ).first->second.get(); +} + std::vector EmojiListWidget::fillIcons() { auto result = std::vector(); result.reserve(2 + _custom.size()); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index fc97b2222..9d4dd1145 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -29,6 +29,7 @@ enum class Section; } // namespace Ui::Emoji namespace Ui::CustomEmoji { +class Loader; class Instance; struct RepaintRequest; } // namespace Ui::CustomEmoji @@ -116,6 +117,10 @@ private: std::unique_ptr ripple; bool painted = false; }; + struct RecentOne { + not_null instance; + std::variant id; + }; struct RepaintSet { base::flat_set ids; crl::time when = 0; @@ -176,8 +181,15 @@ private: void setSelected(OverState newSelected); void setPressed(OverState newPressed); + [[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const; void selectEmoji(EmojiPtr emoji); void selectCustom(not_null document); + void drawRecent( + QPainter &p, + QPoint position, + crl::time now, + bool paused, + int index); void drawEmoji( QPainter &p, QPoint position, @@ -206,6 +218,7 @@ private: [[nodiscard]] QPoint buttonRippleTopLeft(int section) const; void repaintLater( + DocumentId documentId, uint64 setId, Ui::CustomEmoji::RepaintRequest request); template @@ -213,12 +226,31 @@ private: void scheduleRepaintTimer(); void invokeRepaints(); + void fillRecent(); + [[nodiscard]] not_null resolveCustomInstance( + not_null document, + uint64 setId); + [[nodiscard]] not_null resolveCustomInstance( + std::variant customId); + [[nodiscard]] not_null resolveCustomInstance( + DocumentId fakeId, + EmojiPtr emoji); + [[nodiscard]] not_null resolveCustomInstance( + DocumentId documentId); + [[nodiscard]] std::unique_ptr customInstanceWithLoader( + std::unique_ptr loader, + DocumentId documentId, + uint64 setId); + StickersListFooter *_footer = nullptr; int _counts[kEmojiSectionCount]; + std::vector _recent; + base::flat_set _recentCustomIds; + bool _recentPainted = false; QVector _emoji[kEmojiSectionCount]; std::vector _custom; - base::flat_map> _instances; + base::flat_map> _instances; int _rowsLeft = 0; int _columnCount = 1; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index 3171abd40..58c8512fe 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -118,10 +118,14 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector { auto lastRecent = begin(result); const auto &recent = Core::App().settings().recentEmoji(); for (const auto &item : recent) { - const auto emoji = item.emoji->original() - ? item.emoji->original() - : item.emoji; - const auto it = ranges::find(result, emoji, [](const Row &row) { + const auto emoji = std::get_if(&item.id.data); + if (!emoji) { + continue; + } + const auto original = (*emoji)->original() + ? (*emoji)->original() + : (*emoji); + const auto it = ranges::find(result, original, [](const Row &row) { return row.emoji.get(); }); if (it > lastRecent && it != end(result)) { diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 97a027787..494fcee6f 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -81,11 +81,17 @@ QByteArray Settings::serialize() const { const auto windowPosition = Serialize(_windowPosition); const auto proxy = _proxy.serialize(); - auto recentEmojiPreloadGenerated = std::vector(); + auto recentEmojiPreloadGenerated = std::vector(); if (_recentEmojiPreload.empty()) { recentEmojiPreloadGenerated.reserve(_recentEmoji.size()); - for (const auto &[emoji, rating] : _recentEmoji) { - recentEmojiPreloadGenerated.push_back({ emoji->id(), rating }); + for (const auto &[id, rating] : _recentEmoji) { + auto string = QString(); + if (const auto documentId = std::get_if(&id.data)) { + string = QString::number(*documentId); + } else if (const auto emoji = std::get_if(&id.data)) { + string = (*emoji)->id(); + } + recentEmojiPreloadGenerated.push_back({ string, rating }); } } const auto &recentEmojiPreloadData = _recentEmojiPreload.empty() @@ -316,7 +322,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 callAudioBackend = 0; qint32 disableCallsLegacy = 0; QByteArray windowPosition; - std::vector recentEmojiPreload; + std::vector recentEmojiPreload; base::flat_map emojiVariants; qint32 disableOpenGL = _disableOpenGL ? 1 : 0; qint32 groupCallNoiseSuppression = _groupCallNoiseSuppression ? 1 : 0; @@ -790,18 +796,24 @@ const std::vector &Settings::recentEmoji() const { } void Settings::resolveRecentEmoji() const { - const auto haveAlready = [&](EmojiPtr emoji) { + const auto haveAlready = [&](RecentEmojiId id) { return ranges::contains( _recentEmoji, - emoji->id(), - [](const RecentEmoji &data) { return data.emoji->id(); }); + id, + [](const RecentEmoji &data) { return data.id; }); }; if (!_recentEmojiPreload.empty()) { _recentEmoji.reserve(_recentEmojiPreload.size()); for (const auto &[id, rating] : base::take(_recentEmojiPreload)) { - if (const auto emoji = Ui::Emoji::Find(id)) { - if (!haveAlready(emoji)) { - _recentEmoji.push_back({ emoji, rating }); + auto length = int(); + const auto emoji = Ui::Emoji::Find(id, &length); + if (emoji && length == id.size()) { + if (!haveAlready({ emoji })) { + _recentEmoji.push_back({ { emoji }, rating }); + } + } else if (const auto documentId = id.toULongLong()) { + if (!haveAlready({ documentId })) { + _recentEmoji.push_back({ { documentId }, rating }); } } } @@ -810,29 +822,18 @@ void Settings::resolveRecentEmoji() const { for (const auto emoji : Ui::Emoji::GetDefaultRecent()) { if (_recentEmoji.size() >= kRecentEmojiLimit) { break; - } else if (!haveAlready(emoji)) { - _recentEmoji.push_back({ emoji, 1 }); + } else if (!haveAlready({ emoji })) { + _recentEmoji.push_back({ { emoji }, 1 }); } } } -EmojiPack Settings::recentEmojiSection() const { - const auto &recent = recentEmoji(); - - auto result = EmojiPack(); - result.reserve(recent.size()); - for (const auto &[emoji, rating] : recent) { - result.push_back(emoji); - } - return result; -} - -void Settings::incrementRecentEmoji(EmojiPtr emoji) { +void Settings::incrementRecentEmoji(RecentEmojiId id) { resolveRecentEmoji(); auto i = _recentEmoji.begin(), e = _recentEmoji.end(); for (; i != e; ++i) { - if (i->emoji == emoji) { + if (i->id == id) { ++i->rating; if (i->rating > 0x8000) { for (auto j = _recentEmoji.begin(); j != e; ++j) { @@ -856,7 +857,7 @@ void Settings::incrementRecentEmoji(EmojiPtr emoji) { while (_recentEmoji.size() >= kRecentEmojiLimit) { _recentEmoji.pop_back(); } - _recentEmoji.push_back({ emoji, 1 }); + _recentEmoji.push_back({ id, 1 }); for (i = _recentEmoji.end() - 1; i != _recentEmoji.begin(); --i) { if ((i - 1)->rating > i->rating) { break; diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index e866838a9..7c63b85f1 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -573,13 +573,19 @@ public: return _workMode.changes(); } + struct RecentEmojiId { + std::variant data; + + friend inline auto operator<=>( + RecentEmojiId, + RecentEmojiId) = default; + }; struct RecentEmoji { - EmojiPtr emoji = nullptr; + RecentEmojiId id; ushort rating = 0; }; [[nodiscard]] const std::vector &recentEmoji() const; - [[nodiscard]] EmojiPack recentEmojiSection() const; - void incrementRecentEmoji(EmojiPtr emoji); + void incrementRecentEmoji(RecentEmojiId id); void setLegacyRecentEmojiPreload(QVector> data); [[nodiscard]] rpl::producer<> recentEmojiUpdated() const { return _recentEmojiUpdated.events(); @@ -708,7 +714,7 @@ private: static constexpr auto kDefaultDialogsWidthRatio = 5. / 14; static constexpr auto kDefaultBigDialogsWidthRatio = 0.275; - struct RecentEmojiId { + struct RecentEmojiPreload { QString emoji; ushort rating = 0; }; @@ -764,7 +770,7 @@ private: rpl::variable> _dictionariesEnabled; rpl::variable _autoDownloadDictionaries = true; rpl::variable _mainMenuAccountsShown = true; - mutable std::vector _recentEmojiPreload; + mutable std::vector _recentEmojiPreload; mutable std::vector _recentEmoji; base::flat_map _emojiVariants; rpl::event_stream<> _recentEmojiUpdated; diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index 31733fc73..58fba0308 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -269,7 +269,7 @@ const Ui::Emoji::One *UiIntegration::defaultEmojiVariant( const auto result = (i != end(variants)) ? emoji->variant(i->second) : emoji; - Core::App().settings().incrementRecentEmoji(result); + Core::App().settings().incrementRecentEmoji({ result }); return result; } diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 2c7146005..ab01b7ada 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -371,17 +371,7 @@ std::unique_ptr CustomEmojiManager::create( auto i = _instances.find(parsed.id); if (i == end(_instances)) { using Loading = Ui::CustomEmoji::Loading; - auto loader = std::make_unique( - _owner, - parsed, - SizeTag::Normal); - if (loader->resolving()) { - _loaders[parsed.id].push_back(base::make_weak(loader.get())); - _pendingForRequest.emplace(parsed.id); - if (!_requestId && _pendingForRequest.size() == 1) { - crl::on_main(this, [=] { request(); }); - } - } + auto loader = createLoader(parsed.id, SizeTag::Normal); const auto repaint = [=]( not_null instance, Ui::CustomEmoji::RepaintRequest request) { @@ -406,6 +396,25 @@ std::unique_ptr CustomEmojiManager::createLoader( return std::make_unique(document, tag); } +std::unique_ptr CustomEmojiManager::createLoader( + DocumentId documentId, + SizeTag tag) { + const auto selfId = _owner->session().userId().bare; + auto result = std::make_unique( + _owner, + CustomEmojiId{ .selfId = selfId, .id = documentId }, + tag); + if (result->resolving()) { + _loaders[documentId].push_back(base::make_weak(result.get())); + _pendingForRequest.emplace(documentId); + if (!_requestId && _pendingForRequest.size() == 1) { + crl::on_main(this, [=] { request(); }); + } + } + + return result; +} + void CustomEmojiManager::request() { auto ids = QVector(); ids.reserve(std::min(kMaxPerRequest, int(_pendingForRequest.size()))); diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index edbc89f39..357eae4ac 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -45,6 +45,9 @@ public: [[nodiscard]] std::unique_ptr createLoader( not_null document, SizeTag tag); + [[nodiscard]] std::unique_ptr createLoader( + DocumentId documentId, + SizeTag tag); [[nodiscard]] Main::Session &session() const; [[nodiscard]] Session &owner() const; diff --git a/Telegram/SourceFiles/data/stickers/data_stickers_set.h b/Telegram/SourceFiles/data/stickers/data_stickers_set.h index c2c2803b4..311521d1c 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers_set.h +++ b/Telegram/SourceFiles/data/stickers/data_stickers_set.h @@ -99,7 +99,7 @@ public: uint64 id = 0; uint64 accessHash = 0; uint64 hash = 0; - uint64 thumbnailDocumentId = 0; + DocumentId thumbnailDocumentId = 0; QString title, shortName; int count = 0; StickersSetFlags flags;