Proof-of-concept animated custom emoji.

This commit is contained in:
John Preston 2022-06-24 10:49:11 +04:00
parent 21aa1323ec
commit 2e6733e433
9 changed files with 76 additions and 19 deletions

View file

@ -249,7 +249,9 @@ std::unique_ptr<Ui::Text::CustomEmoji> UiIntegration::createCustomEmoji(
if (!my || !my->session) { if (!my || !my->session) {
return nullptr; return nullptr;
} }
return my->session->data().customEmojiManager().create(data); return my->session->data().customEmojiManager().create(
data,
my->customEmojiRepaint);
} }
rpl::producer<> UiIntegration::forcePopupMenuHideRequests() { rpl::producer<> UiIntegration::forcePopupMenuHideRequests() {

View file

@ -28,6 +28,7 @@ struct MarkedTextContext {
Main::Session *session = nullptr; Main::Session *session = nullptr;
HashtagMentionType type = HashtagMentionType::Telegram; HashtagMentionType type = HashtagMentionType::Telegram;
Fn<void()> customEmojiRepaint;
}; };
class UiIntegration final : public Ui::Integration { class UiIntegration final : public Ui::Integration {

View file

@ -11,6 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_document.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" #include "ui/text/text_block.h"
namespace Data { namespace Data {
@ -72,40 +77,74 @@ class DocumentCustomEmoji final : public CustomEmojiWithData {
public: public:
DocumentCustomEmoji( DocumentCustomEmoji(
const QString &data, const QString &data,
not_null<DocumentData*> document); not_null<DocumentData*> document,
Fn<void()> update);
void paint(QPainter &p, int x, int y) override; void paint(QPainter &p, int x, int y) override;
private: private:
not_null<DocumentData*> _document; not_null<DocumentData*> _document;
std::shared_ptr<Data::DocumentMedia> _media;
std::unique_ptr<Lottie::SinglePlayer> _lottie;
Fn<void()> _update;
rpl::lifetime _lifetime;
}; };
DocumentCustomEmoji::DocumentCustomEmoji( DocumentCustomEmoji::DocumentCustomEmoji(
const QString &data, const QString &data,
not_null<DocumentData*> document) not_null<DocumentData*> document,
Fn<void()> update)
: CustomEmojiWithData(data) : CustomEmojiWithData(data)
, _document(document) { , _document(document)
, _update(update) {
} }
void DocumentCustomEmoji::paint(QPainter &p, int x, int y) { void DocumentCustomEmoji::paint(QPainter &p, int x, int y) {
const auto size = Ui::Emoji::GetSizeNormal() / style::DevicePixelRatio(); if (!_media) {
p.fillRect(QRect{ x, y, size, size }, Qt::red); _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 { class ResolvingCustomEmoji final : public CustomEmojiWithData {
public: public:
explicit ResolvingCustomEmoji(const QString &data); ResolvingCustomEmoji(const QString &data, Fn<void()> update);
void paint(QPainter &p, int x, int y) override; void paint(QPainter &p, int x, int y) override;
private: private:
std::optional<DocumentCustomEmoji> _resolved; std::optional<DocumentCustomEmoji> _resolved;
Fn<void()> _update;
}; };
ResolvingCustomEmoji::ResolvingCustomEmoji(const QString &data) ResolvingCustomEmoji::ResolvingCustomEmoji(
: CustomEmojiWithData(data) { const QString &data,
Fn<void()> update)
: CustomEmojiWithData(data)
, _update(update) {
} }
void ResolvingCustomEmoji::paint(QPainter &p, int x, int y) { void ResolvingCustomEmoji::paint(QPainter &p, int x, int y) {
@ -123,16 +162,17 @@ CustomEmojiManager::CustomEmojiManager(not_null<Session*> owner)
CustomEmojiManager::~CustomEmojiManager() = default; CustomEmojiManager::~CustomEmojiManager() = default;
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create( std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
const QString &data) { const QString &data,
Fn<void()> update) {
const auto parsed = ParseCustomEmojiData(data); const auto parsed = ParseCustomEmojiData(data);
if (!parsed.id) { if (!parsed.id) {
return nullptr; return nullptr;
} }
const auto document = _owner->document(parsed.id); const auto document = _owner->document(parsed.id);
if (!document->isNull()) { if (!document->isNull()) {
return std::make_unique<DocumentCustomEmoji>(data, document); return std::make_unique<DocumentCustomEmoji>(data, document, update);
} }
return std::make_unique<ResolvingCustomEmoji>(data); return std::make_unique<ResolvingCustomEmoji>(data, update);
} }
Main::Session &CustomEmojiManager::session() const { Main::Session &CustomEmojiManager::session() const {

View file

@ -21,7 +21,8 @@ public:
~CustomEmojiManager(); ~CustomEmojiManager();
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create( [[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
const QString &data); const QString &data,
Fn<void()> update);
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
[[nodiscard]] Session &owner() const; [[nodiscard]] Session &owner() const;

View file

@ -1497,7 +1497,9 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
clearIsolatedEmoji(); clearIsolatedEmoji();
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &history()->session() .session = &history()->session(),
.customEmojiRepaint = [=] {
history()->owner().requestItemRepaint(this); },
}; };
_text.setMarkedText( _text.setMarkedText(
st::messageTextStyle, st::messageTextStyle,

View file

@ -35,7 +35,9 @@ Game::Game(
, _description(st::msgMinWidth - st::webPageLeft) { , _description(st::msgMinWidth - st::webPageLeft) {
if (!consumed.text.isEmpty()) { if (!consumed.text.isEmpty()) {
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &history()->session() .session = &history()->session(),
.customEmojiRepaint = [=] {
history()->owner().requestViewRepaint(_parent); },
}; };
_description.setMarkedText( _description.setMarkedText(
st::webPageDescriptionStyle, st::webPageDescriptionStyle,
@ -428,7 +430,9 @@ void Game::parentTextUpdated() {
const auto consumed = media->consumedMessageText(); const auto consumed = media->consumedMessageText();
if (!consumed.text.isEmpty()) { if (!consumed.text.isEmpty()) {
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &history()->session() .session = &history()->session(),
.customEmojiRepaint = [=] {
history()->owner().requestViewRepaint(_parent); },
}; };
_description.setMarkedText( _description.setMarkedText(
st::webPageDescriptionStyle, st::webPageDescriptionStyle,

View file

@ -200,7 +200,9 @@ Ui::Text::String Media::createCaption(not_null<HistoryItem*> item) const {
- st::msgPadding.right(); - st::msgPadding.right();
auto result = Ui::Text::String(minResizeWidth); auto result = Ui::Text::String(minResizeWidth);
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &history()->session() .session = &history()->session(),
.customEmojiRepaint = [=] {
history()->owner().requestViewRepaint(_parent); },
}; };
result.setMarkedText( result.setMarkedText(
st::messageTextStyle, st::messageTextStyle,

View file

@ -210,7 +210,11 @@ QSize WebPage::countOptimalSize() {
- st::msgPadding.right() - st::msgPadding.right()
- st::webPageLeft); - st::webPageLeft);
} }
auto context = Core::MarkedTextContext(); auto context = Core::MarkedTextContext{
.session = &history()->session(),
.customEmojiRepaint = [=] {
history()->owner().requestViewRepaint(_parent); },
};
using MarkedTextContext = Core::MarkedTextContext; using MarkedTextContext = Core::MarkedTextContext;
if (_data->siteName == qstr("Twitter")) { if (_data->siteName == qstr("Twitter")) {
context.type = MarkedTextContext::HashtagMentionType::Twitter; context.type = MarkedTextContext::HashtagMentionType::Twitter;

View file

@ -2265,7 +2265,8 @@ void OverlayWidget::refreshCaption() {
? TimestampLinkBase(_document, _message->fullId()) ? TimestampLinkBase(_document, _message->fullId())
: QString(); : QString();
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &_message->history()->session() .session = &_message->history()->session(),
.customEmojiRepaint = [=] { update(); },
}; };
_caption.setMarkedText( _caption.setMarkedText(
st::mediaviewCaptionStyle, st::mediaviewCaptionStyle,