From 2e6733e433f825d00a1347ceaeb063906a18a66b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 24 Jun 2022 10:49:11 +0400 Subject: [PATCH] Proof-of-concept animated custom emoji. --- Telegram/SourceFiles/core/ui_integration.cpp | 4 +- Telegram/SourceFiles/core/ui_integration.h | 1 + .../data/stickers/data_custom_emoji.cpp | 62 +++++++++++++++---- .../data/stickers/data_custom_emoji.h | 3 +- .../SourceFiles/history/history_message.cpp | 4 +- .../history/view/media/history_view_game.cpp | 8 ++- .../history/view/media/history_view_media.cpp | 4 +- .../view/media/history_view_web_page.cpp | 6 +- .../media/view/media_view_overlay_widget.cpp | 3 +- 9 files changed, 76 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index 24c32c56e..a052ef6a7 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -249,7 +249,9 @@ std::unique_ptr UiIntegration::createCustomEmoji( if (!my || !my->session) { return nullptr; } - return my->session->data().customEmojiManager().create(data); + return my->session->data().customEmojiManager().create( + data, + my->customEmojiRepaint); } rpl::producer<> UiIntegration::forcePopupMenuHideRequests() { diff --git a/Telegram/SourceFiles/core/ui_integration.h b/Telegram/SourceFiles/core/ui_integration.h index 5a52a801d..87b5a2f78 100644 --- a/Telegram/SourceFiles/core/ui_integration.h +++ b/Telegram/SourceFiles/core/ui_integration.h @@ -28,6 +28,7 @@ struct MarkedTextContext { Main::Session *session = nullptr; HashtagMentionType type = HashtagMentionType::Telegram; + Fn customEmojiRepaint; }; class UiIntegration final : public Ui::Integration { diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp index ce8c52659..db8a9e57d 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.cpp @@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "data/data_session.h" #include "data/data_document.h" +#include "data/data_document_media.h" +#include "data/data_file_origin.h" +#include "lottie/lottie_common.h" +#include "lottie/lottie_single_player.h" +#include "chat_helpers/stickers_lottie.h" #include "ui/text/text_block.h" namespace Data { @@ -72,40 +77,74 @@ class DocumentCustomEmoji final : public CustomEmojiWithData { public: DocumentCustomEmoji( const QString &data, - not_null document); + not_null document, + Fn update); void paint(QPainter &p, int x, int y) override; private: not_null _document; + std::shared_ptr _media; + std::unique_ptr _lottie; + Fn _update; + rpl::lifetime _lifetime; }; DocumentCustomEmoji::DocumentCustomEmoji( const QString &data, - not_null document) + not_null document, + Fn update) : CustomEmojiWithData(data) -, _document(document) { +, _document(document) +, _update(update) { } void DocumentCustomEmoji::paint(QPainter &p, int x, int y) { - const auto size = Ui::Emoji::GetSizeNormal() / style::DevicePixelRatio(); - p.fillRect(QRect{ x, y, size, size }, Qt::red); + if (!_media) { + _media = _document->createMediaView(); + _media->automaticLoad(_document->stickerSetOrigin(), nullptr); + } + if (_media->loaded() && !_lottie) { + const auto size = Ui::Emoji::GetSizeNormal(); + _lottie = ChatHelpers::LottiePlayerFromDocument( + _media.get(), + nullptr, + ChatHelpers::StickerLottieSize::MessageHistory, + QSize(size, size), + Lottie::Quality::High); + _lottie->updates() | rpl::start_with_next(_update, _lifetime); + } + if (_lottie && _lottie->ready()) { + const auto frame = _lottie->frame(); + p.drawImage( + QRect( + x, + y, + frame.width() / frame.devicePixelRatio(), + frame.height() / frame.devicePixelRatio()), + frame); + _lottie->markFrameShown(); + } } class ResolvingCustomEmoji final : public CustomEmojiWithData { public: - explicit ResolvingCustomEmoji(const QString &data); + ResolvingCustomEmoji(const QString &data, Fn update); void paint(QPainter &p, int x, int y) override; private: std::optional _resolved; + Fn _update; }; -ResolvingCustomEmoji::ResolvingCustomEmoji(const QString &data) -: CustomEmojiWithData(data) { +ResolvingCustomEmoji::ResolvingCustomEmoji( + const QString &data, + Fn update) +: CustomEmojiWithData(data) +, _update(update) { } void ResolvingCustomEmoji::paint(QPainter &p, int x, int y) { @@ -123,16 +162,17 @@ CustomEmojiManager::CustomEmojiManager(not_null owner) CustomEmojiManager::~CustomEmojiManager() = default; std::unique_ptr CustomEmojiManager::create( - const QString &data) { + const QString &data, + Fn update) { const auto parsed = ParseCustomEmojiData(data); if (!parsed.id) { return nullptr; } const auto document = _owner->document(parsed.id); if (!document->isNull()) { - return std::make_unique(data, document); + return std::make_unique(data, document, update); } - return std::make_unique(data); + return std::make_unique(data, update); } Main::Session &CustomEmojiManager::session() const { diff --git a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h index 22c36ac81..757b171f3 100644 --- a/Telegram/SourceFiles/data/stickers/data_custom_emoji.h +++ b/Telegram/SourceFiles/data/stickers/data_custom_emoji.h @@ -21,7 +21,8 @@ public: ~CustomEmojiManager(); [[nodiscard]] std::unique_ptr create( - const QString &data); + const QString &data, + Fn update); [[nodiscard]] Main::Session &session() const; [[nodiscard]] Session &owner() const; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index c5a5e115b..8c66affd4 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1497,7 +1497,9 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) { clearIsolatedEmoji(); const auto context = Core::MarkedTextContext{ - .session = &history()->session() + .session = &history()->session(), + .customEmojiRepaint = [=] { + history()->owner().requestItemRepaint(this); }, }; _text.setMarkedText( st::messageTextStyle, diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.cpp b/Telegram/SourceFiles/history/view/media/history_view_game.cpp index be2c622d7..f629ec710 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_game.cpp @@ -35,7 +35,9 @@ Game::Game( , _description(st::msgMinWidth - st::webPageLeft) { if (!consumed.text.isEmpty()) { const auto context = Core::MarkedTextContext{ - .session = &history()->session() + .session = &history()->session(), + .customEmojiRepaint = [=] { + history()->owner().requestViewRepaint(_parent); }, }; _description.setMarkedText( st::webPageDescriptionStyle, @@ -428,7 +430,9 @@ void Game::parentTextUpdated() { const auto consumed = media->consumedMessageText(); if (!consumed.text.isEmpty()) { const auto context = Core::MarkedTextContext{ - .session = &history()->session() + .session = &history()->session(), + .customEmojiRepaint = [=] { + history()->owner().requestViewRepaint(_parent); }, }; _description.setMarkedText( st::webPageDescriptionStyle, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index f660cd896..2be50fb68 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -200,7 +200,9 @@ Ui::Text::String Media::createCaption(not_null item) const { - st::msgPadding.right(); auto result = Ui::Text::String(minResizeWidth); const auto context = Core::MarkedTextContext{ - .session = &history()->session() + .session = &history()->session(), + .customEmojiRepaint = [=] { + history()->owner().requestViewRepaint(_parent); }, }; result.setMarkedText( st::messageTextStyle, diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 1cb344cd3..141135f35 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -210,7 +210,11 @@ QSize WebPage::countOptimalSize() { - st::msgPadding.right() - st::webPageLeft); } - auto context = Core::MarkedTextContext(); + auto context = Core::MarkedTextContext{ + .session = &history()->session(), + .customEmojiRepaint = [=] { + history()->owner().requestViewRepaint(_parent); }, + }; using MarkedTextContext = Core::MarkedTextContext; if (_data->siteName == qstr("Twitter")) { context.type = MarkedTextContext::HashtagMentionType::Twitter; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index a067d3525..84f7cc140 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2265,7 +2265,8 @@ void OverlayWidget::refreshCaption() { ? TimestampLinkBase(_document, _message->fullId()) : QString(); const auto context = Core::MarkedTextContext{ - .session = &_message->history()->session() + .session = &_message->history()->session(), + .customEmojiRepaint = [=] { update(); }, }; _caption.setMarkedText( st::mediaviewCaptionStyle,