From 3238cf2b4b4be03ebe147c2aacb3fa3a16f9fa6c Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 4 Jul 2022 12:49:31 +0400
Subject: [PATCH] Support custom emoji in reply / pinned / forward bars.

---
 Telegram/SourceFiles/history/history_item.h   |  2 +-
 .../history/history_item_components.cpp       |  9 ++++-
 .../SourceFiles/history/history_widget.cpp    | 33 ++++++++++++-----
 .../history/view/history_view_element.cpp     |  3 ++
 .../history/view/history_view_pinned_bar.cpp  | 36 ++++++++++++-------
 .../history/view/history_view_pinned_bar.h    |  6 ++--
 .../view/history_view_replies_section.cpp     | 21 +++++++----
 .../inline_bots/bot_attach_web_view.cpp       |  1 +
 Telegram/SourceFiles/ui/chat/message_bar.cpp  | 15 ++++++--
 Telegram/SourceFiles/ui/chat/message_bar.h    |  7 +++-
 Telegram/SourceFiles/ui/chat/pinned_bar.cpp   | 14 ++++++--
 Telegram/SourceFiles/ui/chat/pinned_bar.h     |  4 ++-
 12 files changed, 112 insertions(+), 39 deletions(-)

diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h
index 0fb8d59cf..7ff9bbb31 100644
--- a/Telegram/SourceFiles/history/history_item.h
+++ b/Telegram/SourceFiles/history/history_item.h
@@ -431,6 +431,7 @@ public:
 
 	void updateDate(TimeId newDate);
 	[[nodiscard]] bool canUpdateDate() const;
+	void customEmojiRepaint();
 
 	[[nodiscard]] TimeId ttlDestroyAt() const {
 		return _ttlDestroyAt;
@@ -463,7 +464,6 @@ protected:
 	MessageFlags _flags = 0;
 
 	void invalidateChatListEntry();
-	void customEmojiRepaint();
 
 	void setGroupId(MessageGroupId groupId);
 
diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp
index 379fd7ffc..8379c4878 100644
--- a/Telegram/SourceFiles/history/history_item_components.cpp
+++ b/Telegram/SourceFiles/history/history_item_components.cpp
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/view/history_view_service_message.h"
 #include "history/view/media/history_view_document.h"
 #include "core/click_handler_types.h"
+#include "core/ui_integration.h"
 #include "layout/layout_position.h"
 #include "mainwindow.h"
 #include "media/audio/media_audio.h"
@@ -256,10 +257,15 @@ bool HistoryMessageReply::updateData(
 	}
 
 	if (replyToMsg) {
+		const auto context = Core::MarkedTextContext{
+			.session = &holder->history()->session(),
+			.customEmojiRepaint = [=] { holder->customEmojiRepaint(); },
+		};
 		replyToText.setMarkedText(
 			st::messageTextStyle,
 			replyToMsg->inReplyText(),
-			Ui::DialogTextOptions());
+			Ui::DialogTextOptions(),
+			context);
 
 		updateName(holder);
 
@@ -447,6 +453,7 @@ void HistoryMessageReply::paint(
 				p.setTextPalette(inBubble
 					? stm->replyTextPalette
 					: st->imgReplyTextPalette());
+				holder->prepareCustomEmojiPaint(p, replyToText);
 				replyToText.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top() + st::msgServiceNameFont->height, w - st::msgReplyBarSkip - previewSkip, w + 2 * x);
 				p.setTextPalette(stm->textPalette);
 			}
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 271c35d40..58d1f911b 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -149,6 +149,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "chat_helpers/emoji_suggestions_widget.h"
 #include "core/crash_reports.h"
 #include "core/shortcuts.h"
+#include "core/ui_integration.h"
 #include "support/support_common.h"
 #include "support/support_autocomplete.h"
 #include "support/support_preload.h"
@@ -6365,10 +6366,10 @@ void HistoryWidget::checkPinnedBarState() {
 		return;
 	}
 
-	auto barContent = HistoryView::PinnedBarContent(
-		&session(),
-		_pinnedTracker->shownMessageId());
-	_pinnedBar = std::make_unique<Ui::PinnedBar>(this);
+	_pinnedBar = std::make_unique<Ui::PinnedBar>(this, [=] {
+		return controller()->isGifPausedAtLeastFor(
+			Window::GifPauseReason::Any);
+	});
 	rpl::combine(
 		Info::Profile::SharedMediaCountValue(
 			_peer,
@@ -6389,7 +6390,11 @@ void HistoryWidget::checkPinnedBarState() {
 	) | rpl::start_with_next([=](bool many, HistoryItem *item) {
 		refreshPinnedBarButton(many, item);
 	}, _pinnedBar->lifetime());
-	_pinnedBar->setContent(std::move(barContent));
+
+	_pinnedBar->setContent(HistoryView::PinnedBarContent(
+		&session(),
+		_pinnedTracker->shownMessageId(),
+		[bar = _pinnedBar.get()] { bar->update(); }));
 
 	controller()->adaptive().oneColumnValue(
 	) | rpl::start_with_next([=](bool one) {
@@ -7417,10 +7422,15 @@ void HistoryWidget::messageDataReceived(
 }
 
 void HistoryWidget::updateReplyEditText(not_null<HistoryItem*> item) {
+	const auto context = Core::MarkedTextContext{
+		.session = &session(),
+		.customEmojiRepaint = [=] { updateField(); },
+	};
 	_replyEditMsgText.setMarkedText(
 		st::messageTextStyle,
 		item->inReplyText(),
-		Ui::DialogTextOptions());
+		Ui::DialogTextOptions(),
+		context);
 	if (!_field->isHidden() || isRecording()) {
 		_fieldBarCancel->show();
 		updateMouseTracking();
@@ -7518,10 +7528,15 @@ void HistoryWidget::updateForwardingTexts() {
 		}
 	}
 	_toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions());
+	const auto context = Core::MarkedTextContext{
+		.session = &session(),
+		.customEmojiRepaint = [=] { updateField(); },
+	};
 	_toForwardText.setMarkedText(
 		st::messageTextStyle,
 		text,
-		Ui::DialogTextOptions());
+		Ui::DialogTextOptions(),
+		context);
 	_toForwardNameVersion = keepNames ? version : keepCaptions ? -1 : -2;
 }
 
@@ -7570,7 +7585,7 @@ void HistoryWidget::updateReplyToName() {
 }
 
 void HistoryWidget::updateField() {
-	auto fieldAreaTop = _scroll->y() + _scroll->height();
+	const auto fieldAreaTop = _scroll->y() + _scroll->height();
 	rtlupdate(0, fieldAreaTop, width(), height() - fieldAreaTop);
 }
 
@@ -7594,6 +7609,8 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
 		backh += st::historyReplyHeight;
 	}
 	auto drawWebPagePreview = (_previewData && _previewData->pendingTill >= 0) && !_replyForwardPressed;
+	p.setInactive(
+		controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
 	p.fillRect(myrtlrect(0, backy, width(), backh), st::historyReplyBg);
 	if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) {
 		auto replyLeft = st::historyReplySkip;
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 501d895e4..c932f83bd 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -944,6 +944,9 @@ void Element::unloadHeavyPart() {
 	if (_heavyCustomEmoji) {
 		_heavyCustomEmoji = false;
 		data()->_text.unloadCustomEmoji();
+		if (const auto reply = data()->Get<HistoryMessageReply>()) {
+			reply->replyToText.unloadCustomEmoji();
+		}
 	}
 }
 
diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp
index b76757584..7e14026d8 100644
--- a/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/view/history_view_pinned_tracker.h"
 #include "history/history_item.h"
 #include "history/history.h"
+#include "core/ui_integration.h"
 #include "base/weak_ptr.h"
 #include "apiwrap.h"
 #include "styles/style_chat.h"
@@ -23,16 +24,22 @@ namespace HistoryView {
 namespace {
 
 [[nodiscard]] Ui::MessageBarContent ContentWithoutPreview(
-		not_null<HistoryItem*> item) {
+		not_null<HistoryItem*> item,
+		Fn<void()> repaint) {
 	return Ui::MessageBarContent{
 		.text = item->inReplyText(),
+		.context = Core::MarkedTextContext{
+			.session = &item->history()->session(),
+			.customEmojiRepaint = std::move(repaint),
+		},
 	};
 }
 
 [[nodiscard]] Ui::MessageBarContent ContentWithPreview(
 		not_null<HistoryItem*> item,
-		Image *preview) {
-	auto result = ContentWithoutPreview(item);
+		Image *preview,
+		Fn<void()> repaint) {
+	auto result = ContentWithoutPreview(item, std::move(repaint));
 	if (!preview) {
 		static const auto kEmpty = [&] {
 			const auto size = st::historyReplyHeight * cIntRetinaFactor();
@@ -51,14 +58,15 @@ namespace {
 }
 
 [[nodiscard]] rpl::producer<Ui::MessageBarContent> ContentByItem(
-		not_null<HistoryItem*> item) {
+		not_null<HistoryItem*> item,
+		Fn<void()> repaint) {
 	return item->history()->session().changes().messageFlagsValue(
 		item,
 		Data::MessageUpdate::Flag::Edited
 	) | rpl::map([=]() -> rpl::producer<Ui::MessageBarContent> {
 		const auto media = item->media();
 		if (!media || !media->hasReplyPreview()) {
-			return rpl::single(ContentWithoutPreview(item));
+			return rpl::single(ContentWithoutPreview(item, repaint));
 		}
 		constexpr auto kFullLoaded = 2;
 		constexpr auto kSomeLoaded = 1;
@@ -82,7 +90,7 @@ namespace {
 		}) | rpl::then(
 			rpl::single(kFullLoaded)
 		) | rpl::map([=] {
-			return ContentWithPreview(item, media->replyPreview());
+			return ContentWithPreview(item, media->replyPreview(), repaint);
 		});
 	}) | rpl::flatten_latest();
 }
@@ -90,11 +98,12 @@ namespace {
 [[nodiscard]] rpl::producer<Ui::MessageBarContent> ContentByItemId(
 		not_null<Main::Session*> session,
 		FullMsgId id,
+		Fn<void()> repaint,
 		bool alreadyLoaded = false) {
 	if (!id) {
 		return rpl::single(Ui::MessageBarContent());
 	} else if (const auto item = session->data().message(id)) {
-		return ContentByItem(item);
+		return ContentByItem(item, repaint);
 	} else if (alreadyLoaded) {
 		return rpl::single(Ui::MessageBarContent()); // Deleted message?..
 	}
@@ -110,7 +119,7 @@ namespace {
 	return std::move(
 		load
 	) | rpl::then(rpl::deferred([=] {
-		return ContentByItemId(session, id, true);
+		return ContentByItemId(session, id, repaint, true);
 	}));
 }
 
@@ -137,20 +146,23 @@ auto WithPinnedTitle(not_null<Main::Session*> session, PinnedId id) {
 
 rpl::producer<Ui::MessageBarContent> MessageBarContentByItemId(
 		not_null<Main::Session*> session,
-		FullMsgId id) {
-	return ContentByItemId(session, id);
+		FullMsgId id,
+		Fn<void()> repaint) {
+	return ContentByItemId(session, id, std::move(repaint));
 }
 
 rpl::producer<Ui::MessageBarContent> PinnedBarContent(
 		not_null<Main::Session*> session,
-		rpl::producer<PinnedId> id) {
+		rpl::producer<PinnedId> id,
+		Fn<void()> repaint) {
 	return std::move(
 		id
 	) | rpl::distinct_until_changed(
 	) | rpl::map([=](PinnedId id) {
 		return ContentByItemId(
 			session,
-			id.message
+			id.message,
+			repaint
 		) | rpl::map(WithPinnedTitle(session, id));
 	}) | rpl::flatten_latest();
 }
diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_bar.h b/Telegram/SourceFiles/history/view/history_view_pinned_bar.h
index 605091399..535cabc6c 100644
--- a/Telegram/SourceFiles/history/view/history_view_pinned_bar.h
+++ b/Telegram/SourceFiles/history/view/history_view_pinned_bar.h
@@ -25,7 +25,8 @@ namespace HistoryView {
 
 [[nodiscard]] rpl::producer<Ui::MessageBarContent> MessageBarContentByItemId(
 	not_null<Main::Session*> session,
-	FullMsgId id);
+	FullMsgId id,
+	Fn<void()> repaint);
 
 enum class PinnedIdType;
 struct PinnedId {
@@ -47,7 +48,8 @@ struct PinnedId {
 };
 [[nodiscard]] rpl::producer<Ui::MessageBarContent> PinnedBarContent(
 	not_null<Main::Session*> session,
-	rpl::producer<PinnedId> id);
+	rpl::producer<PinnedId> id,
+	Fn<void()> repaint);
 
 [[nodiscard]] rpl::producer<HistoryItem*> PinnedBarItemWithReplyMarkup(
 	not_null<Main::Session*> session,
diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
index a37c55601..f39f7563a 100644
--- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
@@ -90,10 +90,12 @@ bool CanSendFiles(not_null<const QMimeData*> data) {
 
 rpl::producer<Ui::MessageBarContent> RootViewContent(
 		not_null<History*> history,
-		MsgId rootId) {
+		MsgId rootId,
+		Fn<void()> repaint) {
 	return MessageBarContentByItemId(
 		&history->session(),
-		FullMsgId(history->peer->id, rootId)
+		FullMsgId(history->peer->id, rootId),
+		std::move(repaint)
 	) | rpl::map([=](Ui::MessageBarContent &&content) {
 		const auto item = history->owner().message(history->peer, rootId);
 		if (!item) {
@@ -387,14 +389,19 @@ void RepliesWidget::setupRoot() {
 }
 
 void RepliesWidget::setupRootView() {
-	auto content = rpl::combine(
-		RootViewContent(_history, _rootId),
+	_rootView = std::make_unique<Ui::PinnedBar>(this, [=] {
+		return controller()->isGifPausedAtLeastFor(
+			Window::GifPauseReason::Any);
+	});
+	_rootView->setContent(rpl::combine(
+		RootViewContent(
+			_history,
+			_rootId,
+			[bar = _rootView.get()] { bar->update(); }),
 		_rootVisible.value()
 	) | rpl::map([=](Ui::MessageBarContent &&content, bool shown) {
 		return shown ? std::move(content) : Ui::MessageBarContent();
-	});
-	_rootView = std::make_unique<Ui::PinnedBar>(this);
-	_rootView->setContent(std::move(content));
+	}));
 
 	controller()->adaptive().oneColumnValue(
 	) | rpl::start_with_next([=](bool one) {
diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
index 7d7c73b79..f98b67194 100644
--- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
+++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp
@@ -250,6 +250,7 @@ void BotAction::validateIcon() {
 			_mask = QImage(
 				size * style::DevicePixelRatio(),
 				QImage::Format_ARGB32_Premultiplied);
+			_mask.setDevicePixelRatio(style::DevicePixelRatio());
 			_mask.fill(Qt::transparent);
 			{
 				auto p = QPainter(&_mask);
diff --git a/Telegram/SourceFiles/ui/chat/message_bar.cpp b/Telegram/SourceFiles/ui/chat/message_bar.cpp
index 9710811ca..cfce4ec85 100644
--- a/Telegram/SourceFiles/ui/chat/message_bar.cpp
+++ b/Telegram/SourceFiles/ui/chat/message_bar.cpp
@@ -41,9 +41,13 @@ namespace {
 
 } // namespace
 
-MessageBar::MessageBar(not_null<QWidget*> parent, const style::MessageBar &st)
+MessageBar::MessageBar(
+	not_null<QWidget*> parent,
+	const style::MessageBar &st,
+	Fn<bool()> customEmojiPaused)
 : _st(st)
-, _widget(parent) {
+, _widget(parent)
+, _customEmojiPaused(std::move(customEmojiPaused)) {
 	setup();
 
 	style::PaletteChanged(
@@ -57,6 +61,7 @@ void MessageBar::setup() {
 	_widget.paintRequest(
 	) | rpl::start_with_next([=](QRect rect) {
 		auto p = Painter(&_widget);
+		p.setInactive(_customEmojiPaused());
 		paint(p);
 	}, _widget.lifetime());
 }
@@ -187,7 +192,11 @@ void MessageBar::tweenTo(MessageBarContent &&content) {
 void MessageBar::updateFromContent(MessageBarContent &&content) {
 	_content = std::move(content);
 	_title.setText(_st.title, _content.title);
-	_text.setMarkedText(_st.text, _content.text, Ui::DialogTextOptions());
+	_text.setMarkedText(
+		_st.text,
+		_content.text,
+		Ui::DialogTextOptions(),
+		_content.context);
 	_image = prepareImage(_content.preview);
 }
 
diff --git a/Telegram/SourceFiles/ui/chat/message_bar.h b/Telegram/SourceFiles/ui/chat/message_bar.h
index 1009f1830..52ce084f7 100644
--- a/Telegram/SourceFiles/ui/chat/message_bar.h
+++ b/Telegram/SourceFiles/ui/chat/message_bar.h
@@ -23,13 +23,17 @@ struct MessageBarContent {
 	int count = 1;
 	QString title;
 	TextWithEntities text;
+	std::any context;
 	QImage preview;
 	style::margins margins;
 };
 
 class MessageBar final {
 public:
-	MessageBar(not_null<QWidget*> parent, const style::MessageBar &st);
+	MessageBar(
+		not_null<QWidget*> parent,
+		const style::MessageBar &st,
+		Fn<bool()> customEmojiPaused);
 
 	void set(MessageBarContent &&content);
 	void set(rpl::producer<MessageBarContent> content);
@@ -100,6 +104,7 @@ private:
 
 	const style::MessageBar &_st;
 	Ui::RpWidget _widget;
+	Fn<bool()> _customEmojiPaused;
 	MessageBarContent _content;
 	rpl::lifetime _contentLifetime;
 	Ui::Text::String _title, _text;
diff --git a/Telegram/SourceFiles/ui/chat/pinned_bar.cpp b/Telegram/SourceFiles/ui/chat/pinned_bar.cpp
index a8190490a..e9747fbe5 100644
--- a/Telegram/SourceFiles/ui/chat/pinned_bar.cpp
+++ b/Telegram/SourceFiles/ui/chat/pinned_bar.cpp
@@ -18,9 +18,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace Ui {
 
-PinnedBar::PinnedBar(not_null<QWidget*> parent)
+PinnedBar::PinnedBar(not_null<QWidget*> parent, Fn<bool()> customEmojiPaused)
 : _wrap(parent, object_ptr<RpWidget>(parent))
-, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget())) {
+, _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
+, _customEmojiPaused(std::move(customEmojiPaused)) {
 	_wrap.hide(anim::type::instant);
 	_shadow->hide();
 
@@ -133,7 +134,8 @@ void PinnedBar::createControls() {
 
 	_bar = std::make_unique<MessageBar>(
 		_wrap.entity(),
-		st::defaultMessageBar);
+		st::defaultMessageBar,
+		_customEmojiPaused);
 	if (_right.button) {
 		_right.button->raise();
 	}
@@ -205,6 +207,12 @@ void PinnedBar::raise() {
 	_shadow->raise();
 }
 
+void PinnedBar::update() {
+	if (_bar) {
+		_bar->widget()->update();
+	}
+}
+
 void PinnedBar::finishAnimating() {
 	_wrap.finishAnimating();
 }
diff --git a/Telegram/SourceFiles/ui/chat/pinned_bar.h b/Telegram/SourceFiles/ui/chat/pinned_bar.h
index 9e4ff87dc..b5fb8f9b8 100644
--- a/Telegram/SourceFiles/ui/chat/pinned_bar.h
+++ b/Telegram/SourceFiles/ui/chat/pinned_bar.h
@@ -22,12 +22,13 @@ class RpWidget;
 
 class PinnedBar final {
 public:
-	PinnedBar(not_null<QWidget*> parent);
+	PinnedBar(not_null<QWidget*> parent, Fn<bool()> customEmojiPaused);
 	~PinnedBar();
 
 	void show();
 	void hide();
 	void raise();
+	void update();
 	void finishAnimating();
 
 	void setShadowGeometryPostprocess(Fn<QRect(QRect)> postprocess);
@@ -60,6 +61,7 @@ private:
 	} _right;
 
 	std::unique_ptr<Ui::PlainShadow> _shadow;
+	Fn<bool()> _customEmojiPaused;
 	rpl::event_stream<> _barClicks;
 	Fn<QRect(QRect)> _shadowGeometryPostprocess;
 	bool _shouldBeShown = false;