diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 0a1aed46c..616160e8b 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -618,6 +618,8 @@ PRIVATE history/view/media/history_view_call.cpp history/view/media/history_view_contact.h history/view/media/history_view_contact.cpp + history/view/media/history_view_custom_emoji.h + history/view/media/history_view_custom_emoji.cpp history/view/media/history_view_dice.h history/view/media/history_view_dice.cpp history/view/media/history_view_document.h diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 98d1facee..441a0b8ce 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers_emoji_image_loader.h" #include "history/history_item.h" +#include "history/history.h" #include "lottie/lottie_common.h" #include "ui/emoji_config.h" #include "ui/text/text_isolated_emoji.h" @@ -17,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_session.h" #include "data/data_document.h" +#include "data/stickers/data_custom_emoji.h" #include "core/core_settings.h" #include "core/application.h" #include "base/call_delayed.h" @@ -121,7 +123,22 @@ EmojiPack::EmojiPack(not_null session) EmojiPack::~EmojiPack() = default; bool EmojiPack::add(not_null item) { - if (const auto emoji = item->isolatedEmoji()) { + if (const auto custom = item->onlyCustomEmoji()) { + auto &ids = _onlyCustomItems[item]; + Assert(ids.empty()); + auto &manager = item->history()->owner().customEmojiManager(); + for (const auto &line : custom.lines) { + for (const auto &element : line) { + const auto &data = element.entityData; + const auto id = Data::ParseCustomEmojiData(data).id; + if (!manager.resolved(id, [] {})) { + ids.emplace(id); + _onlyCustomWaiting[id].emplace(item); + } + } + } + return true; + } else if (const auto emoji = item->isolatedEmoji()) { _items[emoji].emplace(item); return true; } @@ -129,16 +146,28 @@ bool EmojiPack::add(not_null item) { } void EmojiPack::remove(not_null item) { - Expects(item->isIsolatedEmoji()); + Expects(item->isIsolatedEmoji() || item->isOnlyCustomEmoji()); - const auto emoji = item->isolatedEmoji(); - const auto i = _items.find(emoji); - Assert(i != end(_items)); - const auto j = i->second.find(item); - Assert(j != end(i->second)); - i->second.erase(j); - if (i->second.empty()) { - _items.erase(i); + if (item->isOnlyCustomEmoji()) { + if (const auto list = _onlyCustomItems.take(item)) { + for (const auto id : *list) { + const auto i = _onlyCustomWaiting.find(id); + Assert(i != end(_onlyCustomWaiting)); + i->second.remove(item); + if (i->second.empty()) { + _onlyCustomWaiting.erase(i); + } + } + } + } else if (const auto emoji = item->isolatedEmoji()) { + const auto i = _items.find(emoji); + Assert(i != end(_items)); + const auto j = i->second.find(item); + Assert(j != end(i->second)); + i->second.erase(j); + if (i->second.empty()) { + _items.erase(i); + } } } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h index 67014ec12..742376417 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.h @@ -107,6 +107,13 @@ private: base::flat_map> _images; mtpRequestId _requestId = 0; + base::flat_map< + not_null, + base::flat_set> _onlyCustomItems; + base::flat_map< + DocumentId, + base::flat_set>> _onlyCustomWaiting; + base::flat_map< EmojiPtr, base::flat_map>> _animations; diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 3d00ec152..67d98a72c 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -268,8 +268,8 @@ enum class MessageFlag : uint32 { // Outgoing message and failed to be sent. SendingFailed = (1U << 26), - // No media and only a several emoji text. - IsolatedEmoji = (1U << 27), + // No media and only a several emoji or an only custom emoji text. + SpecialOnlyEmoji = (1U << 27), // Message existing in the message history. HistoryEntry = (1U << 28), diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index 4f7977db5..c55f59ee0 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -35,11 +35,12 @@ constexpr auto kMaxPerRequest = 100; using SizeTag = CustomEmojiManager::SizeTag; [[nodiscard]] ChatHelpers::StickerLottieSize LottieSizeFromTag(SizeTag tag) { + // NB! onlyCustomEmoji dimensions caching uses last ::EmojiInteraction-s. using LottieSize = ChatHelpers::StickerLottieSize; switch (tag) { - case SizeTag::Normal: return LottieSize::MessageHistory; - case SizeTag::Large: return LottieSize::StickersPanel; - case SizeTag::Isolated: return LottieSize::EmojiInteraction; + case SizeTag::Normal: return LottieSize::EmojiInteraction; + case SizeTag::Large: return LottieSize::EmojiInteractionReserved1; + case SizeTag::Isolated: return LottieSize::EmojiInteractionReserved2; } Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag."); } @@ -55,12 +56,6 @@ using SizeTag = CustomEmojiManager::SizeTag; Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag."); } -[[nodiscard]] int SizeFromTag(SizeTag tag) { - const auto emoji = EmojiSizeFromTag(tag); - const auto factor = style::DevicePixelRatio(); - return Ui::Text::AdjustCustomEmojiSize(emoji / factor) * factor; -} - } // namespace class CustomEmojiLoader final @@ -233,7 +228,7 @@ void CustomEmojiLoader::startCacheLookup( lookup->process = std::make_unique(Process{ .loaded = std::move(loaded), }); - const auto size = SizeFromTag(_tag); + const auto size = FrameSizeFromTag(_tag); const auto weak = base::make_weak(&lookup->process->guard); document->owner().cacheBigFile().get(key, [=](QByteArray value) { auto cache = Ui::CustomEmoji::Cache::FromSerialized(value, size); @@ -286,7 +281,7 @@ void CustomEmojiLoader::check() { load->process->lifetime.destroy(); const auto tag = _tag; - const auto size = SizeFromTag(_tag); + const auto size = FrameSizeFromTag(_tag); auto bytes = Lottie::ReadContent(data, filepath); auto loader = [=] { return std::make_unique(document, tag); @@ -348,7 +343,7 @@ Ui::CustomEmoji::Preview CustomEmojiLoader::preview() { || !dimensions.width()) { return {}; } - const auto scale = (SizeFromTag(_tag) * 1.) + const auto scale = (FrameSizeFromTag(_tag) * 1.) / (style::DevicePixelRatio() * dimensions.width()); return { document->createMediaView()->thumbnailPath(), scale }; }; @@ -411,7 +406,7 @@ Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview( if (j == end(other)) { continue; } else if (const auto nonExact = j->second->imagePreview()) { - const auto size = SizeFromTag(tag); + const auto size = FrameSizeFromTag(tag); return { nonExact.image().scaled( size, @@ -451,6 +446,24 @@ std::unique_ptr CustomEmojiManager::create( }); } +bool CustomEmojiManager::resolved(QStringView data, Fn callback) { + return resolved(ParseCustomEmojiData(data).id, std::move(callback)); +} + +bool CustomEmojiManager::resolved( + DocumentId documentId, + Fn callback) { + if (_owner->document(documentId)->sticker()) { + return true; + } + _resolvers[documentId].push_back(std::move(callback)); + _pendingForRequest.emplace(documentId); + if (!_requestId && _pendingForRequest.size() == 1) { + crl::on_main(this, [=] { request(); }); + } + return false; +} + std::unique_ptr CustomEmojiManager::createLoader( not_null document, SizeTag tag) { @@ -510,6 +523,11 @@ void CustomEmojiManager::request() { } } } + if (const auto callbacks = _resolvers.take(id)) { + for (const auto &callback : *callbacks) { + callback(); + } + } requestSetFor(document); } requestFinished(); @@ -623,6 +641,12 @@ Session &CustomEmojiManager::owner() const { return *_owner; } +int FrameSizeFromTag(SizeTag tag) { + const auto emoji = EmojiSizeFromTag(tag); + const auto factor = style::DevicePixelRatio(); + return Ui::Text::AdjustCustomEmojiSize(emoji / factor) * factor; +} + QString SerializeCustomEmojiId(const CustomEmojiId &id) { return QString::number(id.id) + ':' diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index 516fd3331..1e8825f03 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -54,6 +54,9 @@ public: Fn update, SizeTag tag = SizeTag::Normal); + bool resolved(QStringView data, Fn callback); + bool resolved(DocumentId documentId, Fn callback); + [[nodiscard]] std::unique_ptr createLoader( not_null document, SizeTag tag); @@ -98,15 +101,16 @@ private: std::array< base::flat_map< - uint64, + DocumentId, std::unique_ptr>, kSizeCount> _instances; std::array< base::flat_map< - uint64, + DocumentId, std::vector>>, kSizeCount> _loaders; - base::flat_set _pendingForRequest; + base::flat_map>> _resolvers; + base::flat_set _pendingForRequest; mtpRequestId _requestId = 0; base::flat_map _repaints; @@ -117,6 +121,8 @@ private: }; +[[nodiscard]] int FrameSizeFromTag(CustomEmojiManager::SizeTag tag); + [[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id); [[nodiscard]] QString SerializeCustomEmojiId( not_null document); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 9092d0fd3..eac25a0ed 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1311,6 +1311,10 @@ Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const { return {}; } +Ui::Text::OnlyCustomEmoji HistoryItem::onlyCustomEmoji() const { + return {}; +} + HistoryItem::~HistoryItem() { applyTTL(0); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 7ff9bbb31..54a3298a5 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -190,7 +190,12 @@ public: return isGroupEssential() && isEmpty(); } [[nodiscard]] bool isIsolatedEmoji() const { - return _flags & MessageFlag::IsolatedEmoji; + return (_flags & MessageFlag::SpecialOnlyEmoji) + && _text.isIsolatedEmoji(); + } + [[nodiscard]] bool isOnlyCustomEmoji() const { + return (_flags & MessageFlag::SpecialOnlyEmoji) + && _text.isOnlyCustomEmoji(); } [[nodiscard]] bool hasViews() const { return _flags & MessageFlag::HasViews; @@ -309,6 +314,7 @@ public: ToPreviewOptions options) const; [[nodiscard]] virtual TextWithEntities inReplyText() const; [[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const; + [[nodiscard]] virtual Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const; [[nodiscard]] virtual TextWithEntities originalText() const { return TextWithEntities(); } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 94b65d2cd..09291fba6 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1491,7 +1491,7 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) { return; } - clearIsolatedEmoji(); + clearSpecialOnlyEmoji(); const auto context = Core::MarkedTextContext{ .session = &history()->session(), .customEmojiRepaint = [=] { customEmojiRepaint(); }, @@ -1510,7 +1510,7 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) { EnsureNonEmpty(), Ui::ItemTextOptions(this)); } else if (!_media) { - checkIsolatedEmoji(); + checkSpecialOnlyEmoji(); } _textWidth = -1; @@ -1523,7 +1523,7 @@ void HistoryMessage::reapplyText() { } void HistoryMessage::setEmptyText() { - clearIsolatedEmoji(); + clearSpecialOnlyEmoji(); _text.setMarkedText( st::messageTextStyle, { QString(), EntitiesInText() }, @@ -1533,17 +1533,17 @@ void HistoryMessage::setEmptyText() { _textHeight = 0; } -void HistoryMessage::clearIsolatedEmoji() { - if (!(_flags & MessageFlag::IsolatedEmoji)) { +void HistoryMessage::clearSpecialOnlyEmoji() { + if (!(_flags & MessageFlag::SpecialOnlyEmoji)) { return; } history()->session().emojiStickersPack().remove(this); - _flags &= ~MessageFlag::IsolatedEmoji; + _flags &= ~MessageFlag::SpecialOnlyEmoji; } -void HistoryMessage::checkIsolatedEmoji() { +void HistoryMessage::checkSpecialOnlyEmoji() { if (history()->session().emojiStickersPack().add(this)) { - _flags |= MessageFlag::IsolatedEmoji; + _flags |= MessageFlag::SpecialOnlyEmoji; } } @@ -1596,6 +1596,10 @@ Ui::Text::IsolatedEmoji HistoryMessage::isolatedEmoji() const { return _text.toIsolatedEmoji(); } +Ui::Text::OnlyCustomEmoji HistoryMessage::onlyCustomEmoji() const { + return _text.toOnlyCustomEmoji(); +} + TextWithEntities HistoryMessage::originalText() const { if (emptyText()) { return { QString(), EntitiesInText() }; diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 44035e7af..9acaa4bda 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -180,6 +180,7 @@ public: void setText(const TextWithEntities &textWithEntities) override; [[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const override; + [[nodiscard]] Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const override; [[nodiscard]] TextWithEntities originalText() const override; [[nodiscard]] auto originalTextWithLocalEntities() const -> TextWithEntities override; @@ -232,8 +233,8 @@ private: [[nodiscard]] bool checkCommentsLinkedChat(ChannelId id) const; - void clearIsolatedEmoji(); - void checkIsolatedEmoji(); + void clearSpecialOnlyEmoji(); + void checkSpecialOnlyEmoji(); // For an invoice button we replace the button text with a "Receipt" key. // It should show the receipt for the payed invoice. Still let mobile apps do that. diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index f09f891e5..cee73fe2d 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media_grouped.h" #include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_large_emoji.h" +#include "history/view/media/history_view_custom_emoji.h" #include "history/view/history_view_react_animation.h" #include "history/view/history_view_react_button.h" #include "history/view/history_view_cursor_state.h" @@ -574,6 +575,13 @@ void Element::refreshMedia(Element *replacing) { const auto session = &history()->session(); if (const auto media = _data->media()) { _media = media->createView(this, replacing); + } else if (_data->isOnlyCustomEmoji() + && Core::App().settings().largeEmoji()) { + _media = std::make_unique( + this, + std::make_unique( + this, + _data->onlyCustomEmoji())); } else if (_data->isIsolatedEmoji() && Core::App().settings().largeEmoji()) { const auto emoji = _data->isolatedEmoji(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp b/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp new file mode 100644 index 000000000..81045544b --- /dev/null +++ b/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp @@ -0,0 +1,260 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "history/view/media/history_view_custom_emoji.h" + +#include "history/view/media/history_view_sticker.h" +#include "history/view/history_view_element.h" +#include "history/history.h" +#include "history/history_item.h" +#include "data/data_session.h" +#include "data/data_document.h" +#include "data/stickers/data_custom_emoji.h" +#include "chat_helpers/stickers_lottie.h" +#include "ui/chat/chat_style.h" +#include "ui/text/text_isolated_emoji.h" +#include "styles/style_chat.h" + +namespace HistoryView { +namespace { + +using SizeTag = Data::CustomEmojiManager::SizeTag; +using LottieSize = ChatHelpers::StickerLottieSize; +using CustomPtr = std::unique_ptr; +using StickerPtr = std::unique_ptr; + +struct CustomEmojiSizeInfo { + LottieSize tag = LottieSize::MessageHistory; + float64 scale = 1.; +}; + +[[nodiscard]] const base::flat_map &SizesInfo() { + // size = i->second.scale * st::maxAnimatedEmojiSize. + // CustomEmojiManager::SizeTag caching uses first ::EmojiInteraction-s. + using Info = CustomEmojiSizeInfo; + static auto result = base::flat_map{ + { 1, Info{ LottieSize::EmojiInteractionReserved7, 1. } }, + { 2, Info{ LottieSize::EmojiInteractionReserved6, 0.7 } }, + { 3, Info{ LottieSize::EmojiInteractionReserved5, 0.52 } }, + }; + return result; +} + +[[nodiscard]] SizeTag EmojiSize(int dimension) { + return (dimension == 4 || dimension == 5) + ? SizeTag::Isolated + : (dimension == 6 || dimension == 7) + ? SizeTag::Large + : SizeTag::Normal; +} + +[[nodiscard]] bool IsLargeSizeDimension(int dimension) { + return (dimension == 5) || (dimension == 6); +} + +} //namespace + +CustomEmoji::CustomEmoji( + not_null parent, + const Ui::Text::OnlyCustomEmoji &emoji) +: _parent(parent) { + Expects(!emoji.lines.empty()); + + auto resolving = false; + const auto owner = &parent->data()->history()->owner(); + const auto manager = &owner->customEmojiManager(); + const auto max = ranges::max_element( + emoji.lines, + std::less<>(), + &std::vector::size); + const auto dimension = int(std::max(emoji.lines.size(), max->size())); + const auto &sizes = SizesInfo(); + const auto i = sizes.find(dimension); + const auto useCustomEmoji = (i == end(sizes)); + const auto tag = EmojiSize(dimension); + _singleSize = !useCustomEmoji + ? int(base::SafeRound(i->second.scale * st::maxAnimatedEmojiSize)) + : Data::FrameSizeFromTag(tag); + for (const auto &line : emoji.lines) { + _lines.emplace_back(); + for (const auto &element : line) { + if (useCustomEmoji) { + _lines.back().push_back( + manager->create( + element.entityData, + [=] { parent->customEmojiRepaint(); }, + tag)); + } else { + const auto &data = element.entityData; + const auto id = Data::ParseCustomEmojiData(data).id; + const auto document = owner->document(id); + if (document->sticker()) { + const auto skipPremiumEffect = false; + auto sticker = std::make_unique( + parent, + document, + skipPremiumEffect); + sticker->setCustomEmojiPart(_singleSize, i->second.tag); + _lines.back().push_back(std::move(sticker)); + } else { + _lines.back().push_back(element.entityData); + resolving = true; + } + } + } + } + if (resolving) { + + } +} + +CustomEmoji::~CustomEmoji() { + if (_hasHeavyPart) { + unloadHeavyPart(); + _parent->checkHeavyPart(); + } +} + +QSize CustomEmoji::countOptimalSize() { + Expects(!_lines.empty()); + + const auto max = ranges::max_element( + _lines, + std::less<>(), + &std::vector::size); + return { + _singleSize * int(max->size()), + _singleSize * int(_lines.size()), + }; +} + +QSize CustomEmoji::countCurrentSize(int newWidth) { + const auto perRow = std::max(newWidth / _singleSize, 1); + auto width = 0; + auto height = 0; + for (const auto &line : _lines) { + const auto count = int(line.size()); + accumulate_max(width, std::min(perRow, count) * _singleSize); + height += std::max((count + perRow - 1) / perRow, 1) * _singleSize; + } + return { width, height }; +} + +void CustomEmoji::draw( + Painter &p, + const PaintContext &context, + const QRect &r) { + _parent->clearCustomEmojiRepaint(); + + auto x = r.x(); + auto y = r.y(); + const auto perRow = std::max(r.width() / _singleSize, 1); + const auto paused = _parent->delegate()->elementIsGifPaused(); + for (auto &line : _lines) { + const auto count = int(line.size()); + const auto rows = std::max((count + perRow - 1) / perRow, 1); + for (auto row = 0; row != rows; ++row) { + for (auto column = 0; column != perRow; ++column) { + const auto index = row * perRow + column; + if (index >= count) { + break; + } + paintElement(p, x, y, line[index], context, paused); + x += _singleSize; + } + x = r.x(); + y += _singleSize; + } + } +} + +void CustomEmoji::paintElement( + Painter &p, + int x, + int y, + LargeCustomEmoji &element, + const PaintContext &context, + bool paused) { + if (const auto sticker = std::get_if(&element)) { + paintSticker(p, x, y, sticker->get(), context, paused); + } else if (const auto custom = std::get_if(&element)) { + paintCustom(p, x, y, custom->get(), context, paused); + } +} + +void CustomEmoji::paintSticker( + Painter &p, + int x, + int y, + not_null sticker, + const PaintContext &context, + bool paused) { + sticker->draw(p, context, { QPoint(x, y), sticker->countOptimalSize() }); +} + +void CustomEmoji::paintCustom( + Painter &p, + int x, + int y, + not_null emoji, + const PaintContext &context, + bool paused) { + if (!_hasHeavyPart) { + _hasHeavyPart = true; + _parent->history()->owner().registerHeavyViewPart(_parent); + } + const auto inner = st::largeEmojiSize + 2 * st::largeEmojiOutline; + const auto outer = Ui::Text::AdjustCustomEmojiSize(inner); + const auto skip = (inner - outer) / 2; + const auto preview = context.imageStyle()->msgServiceBg->c; + if (context.selected()) { + const auto factor = style::DevicePixelRatio(); + const auto size = QSize(outer, outer) * factor; + if (_selectedFrame.size() != size) { + _selectedFrame = QImage( + size, + QImage::Format_ARGB32_Premultiplied); + _selectedFrame.setDevicePixelRatio(factor); + } + _selectedFrame.fill(Qt::transparent); + auto q = QPainter(&_selectedFrame); + emoji->paint(q, 0, 0, context.now, preview, paused); + q.end(); + + _selectedFrame = Images::Colored( + std::move(_selectedFrame), + context.st->msgStickerOverlay()->c); + p.drawImage(x + skip, y + skip, _selectedFrame); + } else { + emoji->paint(p, x + skip, y + skip, context.now, preview, paused); + } +} + +bool CustomEmoji::hasHeavyPart() const { + return _hasHeavyPart; +} + +void CustomEmoji::unloadHeavyPart() { + if (!_hasHeavyPart) { + return; + } + const auto unload = [&](const LargeCustomEmoji &element) { + if (const auto sticker = std::get_if(&element)) { + (*sticker)->unloadHeavyPart(); + } else if (const auto custom = std::get_if(&element)) { + (*custom)->unload(); + } + }; + _hasHeavyPart = false; + for (const auto &line : _lines) { + for (const auto &element : line) { + unload(element); + } + } +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.h b/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.h new file mode 100644 index 000000000..275a90609 --- /dev/null +++ b/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.h @@ -0,0 +1,84 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "history/view/media/history_view_media_unwrapped.h" + +namespace Ui::Text { +struct OnlyCustomEmoji; +} // namespace Ui::Text + +namespace Stickers { +struct LargeEmojiImage; +} // namespace Stickers + +namespace HistoryView { + +class Sticker; + +using LargeCustomEmoji = std::variant< + QString, + std::unique_ptr, + std::unique_ptr>; + +class CustomEmoji final : public UnwrappedMedia::Content { +public: + CustomEmoji( + not_null parent, + const Ui::Text::OnlyCustomEmoji &emoji); + ~CustomEmoji(); + + QSize countOptimalSize() override; + QSize countCurrentSize(int newWidth) override; + void draw( + Painter &p, + const PaintContext &context, + const QRect &r) override; + + bool alwaysShowOutTimestamp() override { + return true; + } + bool hasTextForCopy() const override { + return true; + } + + bool hasHeavyPart() const override; + void unloadHeavyPart() override; + +private: + void paintElement( + Painter &p, + int x, + int y, + LargeCustomEmoji &element, + const PaintContext &context, + bool paused); + void paintSticker( + Painter &p, + int x, + int y, + not_null sticker, + const PaintContext &context, + bool paused); + void paintCustom( + Painter &p, + int x, + int y, + not_null emoji, + const PaintContext &context, + bool paused); + + const not_null _parent; + std::vector> _lines; + QImage _selectedFrame; + int _singleSize = 0; + bool _hasHeavyPart = false; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_dice.cpp b/Telegram/SourceFiles/history/view/media/history_view_dice.cpp index 755088a42..75174f92b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_dice.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_dice.cpp @@ -46,8 +46,8 @@ Dice::Dice(not_null parent, not_null dice) Dice::~Dice() = default; -QSize Dice::size() { - return _start ? _start->size() : Sticker::EmojiSize(); +QSize Dice::countOptimalSize() { + return _start ? _start->countOptimalSize() : Sticker::EmojiSize(); } ClickHandlerPtr Dice::link() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_dice.h b/Telegram/SourceFiles/history/view/media/history_view_dice.h index 6a18dffa0..68a5368d0 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_dice.h +++ b/Telegram/SourceFiles/history/view/media/history_view_dice.h @@ -21,7 +21,7 @@ public: Dice(not_null parent, not_null dice); ~Dice(); - QSize size() override; + QSize countOptimalSize() override; void draw( Painter &p, const PaintContext &context, diff --git a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp index dc2b89dc1..328f6669a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp @@ -68,7 +68,7 @@ LargeEmoji::~LargeEmoji() { } } -QSize LargeEmoji::size() { +QSize LargeEmoji::countOptimalSize() { using namespace rpl::mappers; const auto count = _images.size() diff --git a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h index cb7541b7c..2c7f618cd 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h +++ b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.h @@ -10,10 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media_unwrapped.h" #include "ui/text/text_isolated_emoji.h" -namespace Data { -struct FileOrigin; -} // namespace Data - namespace Stickers { struct LargeEmojiImage; } // namespace Stickers @@ -32,7 +28,7 @@ public: const Ui::Text::IsolatedEmoji &emoji); ~LargeEmoji(); - QSize size() override; + QSize countOptimalSize() override; void draw( Painter &p, const PaintContext &context, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp index 56049d8be..9f5402c61 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -32,6 +32,10 @@ auto UnwrappedMedia::Content::stickerTakeLottie( return nullptr; } +QSize UnwrappedMedia::Content::countCurrentSize(int newWidth) { + return countOptimalSize(); +} + UnwrappedMedia::UnwrappedMedia( not_null parent, std::unique_ptr content) @@ -41,10 +45,10 @@ UnwrappedMedia::UnwrappedMedia( QSize UnwrappedMedia::countOptimalSize() { _content->refreshLink(); - _contentSize = DownscaledSize(_content->size(), Sticker::Size()); - auto maxWidth = _contentSize.width(); - const auto minimal = st::largeEmojiSize + 2 * st::largeEmojiOutline; - auto minHeight = std::max(_contentSize.height(), minimal); + const auto optimal = _content->countOptimalSize(); + auto maxWidth = optimal.width(); + const auto minimal = st::emojiSize; + auto minHeight = std::max(optimal.height(), minimal); if (_parent->media() == this) { const auto item = _parent->data(); const auto via = item->Get(); @@ -53,17 +57,8 @@ QSize UnwrappedMedia::countOptimalSize() { if (forwarded) { forwarded->create(via); } - const auto additional = additionalWidth(via, reply, forwarded); - maxWidth += additional; + maxWidth += additionalWidth(via, reply, forwarded); accumulate_max(maxWidth, _parent->reactionsOptimalWidth()); - if (const auto surrounding = surroundingInfo(via, reply, forwarded, additional - st::msgReplyPadding.left())) { - const auto infoHeight = st::msgDateImgPadding.y() * 2 - + st::msgDateFont->height; - const auto minimal = surrounding.height - + st::msgDateImgDelta - + infoHeight; - minHeight = std::max(minHeight, minimal); - } if (const auto size = _parent->rightActionSize()) { minHeight = std::max( minHeight, @@ -76,34 +71,53 @@ QSize UnwrappedMedia::countOptimalSize() { QSize UnwrappedMedia::countCurrentSize(int newWidth) { const auto item = _parent->data(); accumulate_min(newWidth, maxWidth()); - accumulate_max(newWidth, _parent->reactionsOptimalWidth()); - const auto isPageAttach = (_parent->media() != this); - if (!isPageAttach) { - const auto via = item->Get(); - const auto reply = _parent->displayedReply(); - const auto forwarded = getDisplayedForwardedInfo(); - if (via || reply || forwarded) { - int usew = maxWidth() - additionalWidth(via, reply, forwarded); - int availw = newWidth - usew - st::msgReplyPadding.left() - st::msgReplyPadding.left() - st::msgReplyPadding.left(); - if (via) { - via->resize(availw); - } - if (reply) { - reply->resize(availw); - } - } + _contentSize = _content->countCurrentSize(newWidth); + auto newHeight = std::max(minHeight(), _contentSize.height()); + _additionalOnTop = false; + if (_parent->media() != this) { + return { newWidth, newHeight }; } - auto newHeight = minHeight(); - if (!isPageAttach - && _parent->hasOutLayout() + if (_parent->hasOutLayout() && !_parent->delegate()->elementIsChatWide()) { // Add some height to isolated emoji for the timestamp info. const auto infoHeight = st::msgDateImgPadding.y() * 2 + st::msgDateFont->height; - const auto minimal = st::largeEmojiSize - + 2 * st::largeEmojiOutline - + (st::msgDateImgDelta + infoHeight); - accumulate_max(newHeight, minimal); + const auto minimal = std::min( + st::largeEmojiSize + 2 * st::largeEmojiOutline, + _contentSize.height()); + accumulate_max(newHeight, minimal + st::msgDateImgDelta + infoHeight); + } + accumulate_max(newWidth, _parent->reactionsOptimalWidth()); + const auto via = item->Get(); + const auto reply = _parent->displayedReply(); + const auto forwarded = getDisplayedForwardedInfo(); + if (via || reply || forwarded) { + const auto paddings = 3 * st::msgReplyPadding.left(); + const auto additional = additionalWidth(via, reply, forwarded); + const auto optimalw = maxWidth() - additional; + const auto additionalMinWidth = std::min(additional, st::msgMinWidth / 2); + _additionalOnTop = (optimalw + paddings + additionalMinWidth) > newWidth; + const auto surrounding = surroundingInfo(via, reply, forwarded, additional - st::msgReplyPadding.left()); + if (_additionalOnTop) { + _topAdded = surrounding.height; + newHeight += _topAdded; + } else { + const auto infoHeight = st::msgDateImgPadding.y() * 2 + + st::msgDateFont->height; + const auto minimal = surrounding.height + + st::msgDateImgDelta + + infoHeight; + newHeight = std::max(newHeight, minimal); + } + const auto availw = newWidth + - (_additionalOnTop ? 0 : optimalw) + - paddings; + if (via) { + via->resize(availw); + } + if (reply) { + reply->resize(availw); + } } return { newWidth, newHeight }; } @@ -116,22 +130,16 @@ void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const { && !_parent->delegate()->elementIsChatWide(); const auto inWebPage = (_parent->media() != this); const auto item = _parent->data(); - const auto via = inWebPage ? nullptr : item->Get(); - const auto reply = inWebPage ? nullptr : _parent->displayedReply(); - const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo(); auto usex = 0; - auto usew = maxWidth(); - if (!inWebPage) { - usew -= additionalWidth(via, reply, forwarded); - if (rightAligned) { - usex = width() - usew; - } + auto usew = _contentSize.width(); + if (!inWebPage && rightAligned) { + usex = width() - usew; } if (rtl()) { usex = width() - usex - usew; } - const auto usey = rightAligned ? 0 : (height() - _contentSize.height()); + const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height()); const auto useh = rightAligned ? std::max( _contentSize.height(), @@ -144,6 +152,9 @@ void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const { if (!inWebPage && (context.skipDrawingParts != PaintContext::SkipDrawingParts::Surrounding)) { + const auto via = inWebPage ? nullptr : item->Get(); + const auto reply = inWebPage ? nullptr : _parent->displayedReply(); + const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo(); drawSurrounding(p, inner, context, via, reply, forwarded); } } @@ -201,10 +212,14 @@ void UnwrappedMedia::drawSurrounding( InfoDisplayType::Background); } auto replyRight = 0; - auto rectw = width() - inner.width() - st::msgReplyPadding.left(); + auto rectw = _additionalOnTop + ? std::min(width() - st::msgReplyPadding.left(), additionalWidth(via, reply, forwarded)) + : (width() - inner.width() - st::msgReplyPadding.left()); if (const auto surrounding = surroundingInfo(via, reply, forwarded, rectw)) { auto recth = surrounding.height; - int rectx = rightAligned ? 0 : (inner.width() + st::msgReplyPadding.left()); + int rectx = _additionalOnTop + ? (rightAligned ? (inner.width() + st::msgReplyPadding.left() - rectw) : 0) + : (rightAligned ? 0 : (inner.width() + st::msgReplyPadding.left())); int recty = 0; if (rtl()) rectx = width() - rectx - rectw; @@ -254,16 +269,10 @@ PointState UnwrappedMedia::pointState(QPoint point) const { && !_parent->delegate()->elementIsChatWide(); const auto inWebPage = (_parent->media() != this); const auto item = _parent->data(); - const auto via = inWebPage ? nullptr : item->Get(); - const auto reply = inWebPage ? nullptr : _parent->displayedReply(); - const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo(); auto usex = 0; - auto usew = maxWidth(); - if (!inWebPage) { - usew -= additionalWidth(via, reply, forwarded); - if (rightAligned) { - usex = width() - usew; - } + auto usew = _contentSize.width(); + if (!inWebPage && rightAligned) { + usex = width() - usew; } if (rtl()) { usex = width() - usex - usew; @@ -271,7 +280,7 @@ PointState UnwrappedMedia::pointState(QPoint point) const { const auto datey = height() - st::msgDateImgPadding.y() * 2 - st::msgDateFont->height; - const auto usey = rightAligned ? 0 : (height() - _contentSize.height()); + const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height()); const auto useh = rightAligned ? std::max(_contentSize.height(), datey) : _contentSize.height(); @@ -295,22 +304,16 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const { && !_parent->delegate()->elementIsChatWide(); const auto inWebPage = (_parent->media() != this); const auto item = _parent->data(); - const auto via = inWebPage ? nullptr : item->Get(); - const auto reply = inWebPage ? nullptr : _parent->displayedReply(); - const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo(); auto usex = 0; - auto usew = maxWidth(); - if (!inWebPage) { - usew -= additionalWidth(via, reply, forwarded); - if (rightAligned) { - usex = width() - usew; - } + auto usew = _contentSize.width(); + if (!inWebPage && rightAligned) { + usex = width() - usew; } if (rtl()) { usex = width() - usex - usew; } - const auto usey = rightAligned ? 0 : (height() - _contentSize.height()); + const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height()); const auto useh = rightAligned ? std::max( _contentSize.height(), @@ -319,11 +322,18 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const { const auto inner = QRect(usex, usey, usew, useh); if (_parent->media() == this) { + const auto via = inWebPage ? nullptr : item->Get(); + const auto reply = inWebPage ? nullptr : _parent->displayedReply(); + const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo(); auto replyRight = 0; - auto rectw = width() - inner.width() - st::msgReplyPadding.left(); - if (auto surrounding = surroundingInfo(via, reply, forwarded, rectw)) { + auto rectw = _additionalOnTop + ? std::min(width() - st::msgReplyPadding.left(), additionalWidth(via, reply, forwarded)) + : (width() - inner.width() - st::msgReplyPadding.left()); + if (const auto surrounding = surroundingInfo(via, reply, forwarded, rectw)) { auto recth = surrounding.height; - int rectx = rightAligned ? 0 : (inner.width() + st::msgReplyPadding.left()); + int rectx = _additionalOnTop + ? (rightAligned ? (inner.width() + st::msgReplyPadding.left() - rectw) : 0) + : (rightAligned ? 0 : (inner.width() + st::msgReplyPadding.left())); int recty = 0; if (rtl()) rectx = width() - rectx - rectw; @@ -420,10 +430,7 @@ QRect UnwrappedMedia::contentRectForReactions() const { const auto reply = _parent->displayedReply(); const auto forwarded = getDisplayedForwardedInfo(); auto usex = 0; - auto usew = maxWidth(); - if (!inWebPage) { - usew -= additionalWidth(via, reply, forwarded); - } + auto usew = _contentSize.width(); accumulate_max(usew, _parent->reactionsOptimalWidth()); if (rightAligned) { usex = width() - usew; @@ -431,7 +438,7 @@ QRect UnwrappedMedia::contentRectForReactions() const { if (rtl()) { usex = width() - usex - usew; } - const auto usey = rightAligned ? 0 : (height() - _contentSize.height()); + const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height()); const auto useh = rightAligned ? std::max( _contentSize.height(), diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h index 612cb5931..6ff2490df 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h @@ -21,7 +21,8 @@ class UnwrappedMedia final : public Media { public: class Content { public: - [[nodiscard]] virtual QSize size() = 0; + [[nodiscard]] virtual QSize countOptimalSize() = 0; + [[nodiscard]] virtual QSize countCurrentSize(int newWidth); virtual void draw( Painter &p, @@ -161,6 +162,8 @@ private: std::unique_ptr _content; QSize _contentSize; + int _topAdded = 0; + bool _additionalOnTop = false; }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_slot_machine.cpp b/Telegram/SourceFiles/history/view/media/history_view_slot_machine.cpp index 1ccadc4d2..6603dbdcf 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_slot_machine.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_slot_machine.cpp @@ -139,8 +139,8 @@ bool SlotMachine::isEndResolved() const { return _end[0].has_value() || (_dice->value() != kWinValue); } -QSize SlotMachine::size() { - return _pull ? _pull->size() : Sticker::EmojiSize(); +QSize SlotMachine::countOptimalSize() { + return _pull ? _pull->countOptimalSize() : Sticker::EmojiSize(); } ClickHandlerPtr SlotMachine::link() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_slot_machine.h b/Telegram/SourceFiles/history/view/media/history_view_slot_machine.h index 2aef84afa..68dc7e4fa 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_slot_machine.h +++ b/Telegram/SourceFiles/history/view/media/history_view_slot_machine.h @@ -21,7 +21,7 @@ public: SlotMachine(not_null parent, not_null dice); ~SlotMachine(); - QSize size() override; + QSize countOptimalSize() override; void draw( Painter &p, const PaintContext &context, diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 4757e26c3..606300e36 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -68,6 +68,10 @@ Sticker::Sticker( : _parent(parent) , _data(data) , _replacements(replacements) +, _cachingTag(ChatHelpers::StickerLottieSize::MessageHistory) +, _lottieOncePlayed(false) +, _premiumEffectPlayed(false) +, _nextLastDiceFrame(false) , _skipPremiumEffect(skipPremiumEffect) { if ((_dataMedia = _data->activeMediaView())) { dataMediaCreated(); @@ -106,6 +110,10 @@ bool Sticker::hasPremiumEffect() const { return !_skipPremiumEffect && _data->isPremiumSticker(); } +bool Sticker::customEmojiPart() const { + return (_cachingTag != ChatHelpers::StickerLottieSize::MessageHistory); +} + bool Sticker::isEmojiSticker() const { return (_parent->data()->media() == nullptr); } @@ -126,11 +134,11 @@ void Sticker::initSize() { } } -QSize Sticker::size() { +QSize Sticker::countOptimalSize() { if (_size.isEmpty()) { initSize(); } - return _size; + return DownscaledSize(_size, Size()); } bool Sticker::readyToDrawLottie() { @@ -184,6 +192,10 @@ void Sticker::draw( Painter &p, const PaintContext &context, const QRect &r) { + if (!customEmojiPart() && isEmojiSticker()) { + _parent->clearCustomEmojiRepaint(); + } + ensureDataMediaCreated(); if (readyToDrawLottie()) { paintLottie(p, context, r); @@ -257,7 +269,7 @@ void Sticker::paintLottie( ? true : (_diceIndex == 0) ? false - : (isEmojiSticker() + : ((!customEmojiPart() && isEmojiSticker()) || !Core::App().settings().loopAnimatedStickers()); const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd(); const auto switchToNext = /*(_externalInfo.frame >= 0) @@ -445,14 +457,21 @@ void Sticker::setDiceIndex(const QString &emoji, int index) { _diceIndex = index; } +void Sticker::setCustomEmojiPart( + int size, + ChatHelpers::StickerLottieSize tag) { + _size = { size, size }; + _cachingTag = tag; +} + void Sticker::setupLottie() { Expects(_dataMedia != nullptr); _lottie = ChatHelpers::LottiePlayerFromDocument( _dataMedia.get(), _replacements, - ChatHelpers::StickerLottieSize::MessageHistory, - size() * style::DevicePixelRatio(), + _cachingTag, + countOptimalSize() * style::DevicePixelRatio(), Lottie::Quality::High); checkPremiumEffectStart(); lottieCreated(); @@ -473,10 +492,10 @@ void Sticker::lottieCreated() { _lottie->updates( ) | rpl::start_with_next([=](Lottie::Update update) { v::match(update.data, [&](const Lottie::Information &information) { - _parent->history()->owner().requestViewResize(_parent); + _parent->customEmojiRepaint(); //markFramesTillExternal(); }, [&](const Lottie::DisplayFrameRequest &request) { - _parent->history()->owner().requestViewRepaint(_parent); + _parent->customEmojiRepaint(); }); }, _lifetime); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.h b/Telegram/SourceFiles/history/view/media/history_view_sticker.h index e048e4e82..bb6ef7984 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.h +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.h @@ -24,6 +24,10 @@ class SinglePlayer; struct ColorReplacements; } // namespace Lottie +namespace ChatHelpers { +enum class StickerLottieSize : uint8; +} // namespace ChatHelpers + namespace HistoryView { class Sticker final @@ -39,7 +43,7 @@ public: ~Sticker(); void initSize(); - QSize size() override; + QSize countOptimalSize() override; void draw( Painter &p, const PaintContext &context, @@ -65,6 +69,7 @@ public: } void setDiceIndex(const QString &emoji, int index); + void setCustomEmojiPart(int size, ChatHelpers::StickerLottieSize tag); [[nodiscard]] bool atTheEnd() const { return (_frameIndex >= 0) && (_frameIndex + 1 == _framesCount); } @@ -92,6 +97,7 @@ public: private: [[nodiscard]] bool hasPremiumEffect() const; + [[nodiscard]] bool customEmojiPart() const; [[nodiscard]] bool isEmojiSticker() const; void paintLottie(Painter &p, const PaintContext &context, const QRect &r); bool paintPixmap(Painter &p, const PaintContext &context, const QRect &r); @@ -123,10 +129,11 @@ private: int _diceIndex = -1; mutable int _frameIndex = -1; mutable int _framesCount = -1; - mutable bool _lottieOncePlayed = false; - mutable bool _premiumEffectPlayed = false; - mutable bool _nextLastDiceFrame = false; - bool _skipPremiumEffect = false; + ChatHelpers::StickerLottieSize _cachingTag = {}; + mutable bool _lottieOncePlayed : 1; + mutable bool _premiumEffectPlayed : 1; + mutable bool _nextLastDiceFrame : 1; + bool _skipPremiumEffect : 1; rpl::lifetime _lifetime; diff --git a/Telegram/lib_base b/Telegram/lib_base index d69b49fdd..500731e1f 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit d69b49fdd7bcb0b3414bc66fb34606dd56f695ba +Subproject commit 500731e1f9a4a8b98e388e7a06b91b41d8df7211 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 4768e7ee0..f27d756bc 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 4768e7ee03aa22f64f73dc13016d5bd94a047496 +Subproject commit f27d756bcd7508250a3f7ff73bf6053308c18cd6