Support colored emoji statuses.
This commit is contained in:
		
							parent
							
								
									923e725e18
								
							
						
					
					
						commit
						2d07539892
					
				
					 17 changed files with 224 additions and 56 deletions
				
			
		|  | @ -466,6 +466,8 @@ PRIVATE | ||||||
|     data/data_download_manager.h |     data/data_download_manager.h | ||||||
|     data/data_drafts.cpp |     data/data_drafts.cpp | ||||||
|     data/data_drafts.h |     data/data_drafts.h | ||||||
|  |     data/data_emoji_statuses.cpp | ||||||
|  |     data/data_emoji_statuses.h | ||||||
|     data/data_folder.cpp |     data/data_folder.cpp | ||||||
|     data/data_folder.h |     data/data_folder.h | ||||||
|     data/data_file_click_handler.cpp |     data/data_file_click_handler.cpp | ||||||
|  |  | ||||||
|  | @ -687,6 +687,9 @@ int PeerListRow::paintNameIconGetWidth( | ||||||
| 				? st::dialogsPremiumIconOver | 				? st::dialogsPremiumIconOver | ||||||
| 				: st::dialogsPremiumIcon), | 				: st::dialogsPremiumIcon), | ||||||
| 			.scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg), | 			.scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg), | ||||||
|  | 			.premiumFg = &(selected | ||||||
|  | 				? st::dialogsVerifiedIconBgOver | ||||||
|  | 				: st::dialogsVerifiedIconBg), | ||||||
| 			.preview = st::windowBgOver->c, | 			.preview = st::windowBgOver->c, | ||||||
| 			.customEmojiRepaint = repaint, | 			.customEmojiRepaint = repaint, | ||||||
| 			.now = now, | 			.now = now, | ||||||
|  |  | ||||||
|  | @ -435,6 +435,9 @@ EmojiListWidget::EmojiListWidget( | ||||||
| 		resizeToWidth(width()); | 		resizeToWidth(width()); | ||||||
| 	}, lifetime()); | 	}, lifetime()); | ||||||
| 
 | 
 | ||||||
|  | 	if (_mode == Mode::EmojiStatus) { | ||||||
|  | 		_emojiStatusColor = std::make_unique<Ui::Text::CustomEmojiColored>(); | ||||||
|  | 	} | ||||||
| 	rpl::single( | 	rpl::single( | ||||||
| 		rpl::empty | 		rpl::empty | ||||||
| 	) | rpl::then( | 	) | rpl::then( | ||||||
|  | @ -443,6 +446,12 @@ EmojiListWidget::EmojiListWidget( | ||||||
| 		initButton(_add, tr::lng_stickers_featured_add(tr::now), false); | 		initButton(_add, tr::lng_stickers_featured_add(tr::now), false); | ||||||
| 		initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true); | 		initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true); | ||||||
| 		initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true); | 		initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true); | ||||||
|  | 		if (const auto status = _emojiStatusColor.get()) { | ||||||
|  | 			status->color = anim::color( | ||||||
|  | 				st::stickerPanPremium1, | ||||||
|  | 				st::stickerPanPremium2, | ||||||
|  | 				0.5); | ||||||
|  | 		} | ||||||
| 	}, lifetime()); | 	}, lifetime()); | ||||||
| 
 | 
 | ||||||
| 	if (!descriptor.customRecentList.empty()) { | 	if (!descriptor.customRecentList.empty()) { | ||||||
|  | @ -765,10 +774,11 @@ void EmojiListWidget::fillRecent() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) { | void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) { | ||||||
| 	Expects(_recent.empty()); |  | ||||||
| 
 |  | ||||||
| 	const auto test = session().isTestMode(); | 	const auto test = session().isTestMode(); | ||||||
| 
 | 	const auto owner = &session().data(); | ||||||
|  | 	const auto statuses = &owner->emojiStatuses(); | ||||||
|  | 	const auto manager = &owner->customEmojiManager(); | ||||||
|  | 	_recent.clear(); | ||||||
| 	_recent.reserve(list.size()); | 	_recent.reserve(list.size()); | ||||||
| 	for (const auto &id : list) { | 	for (const auto &id : list) { | ||||||
| 		if (!id && _mode == Mode::EmojiStatus) { | 		if (!id && _mode == Mode::EmojiStatus) { | ||||||
|  | @ -777,7 +787,7 @@ void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) { | ||||||
| 		} else { | 		} else { | ||||||
| 			_recent.push_back({ | 			_recent.push_back({ | ||||||
| 				.custom = resolveCustomRecent(id), | 				.custom = resolveCustomRecent(id), | ||||||
| 				.id = { RecentEmojiDocument{.id = id, .test = test } }, | 				.id = { RecentEmojiDocument{ .id = id, .test = test } }, | ||||||
| 			}); | 			}); | ||||||
| 			_recentCustomIds.emplace(id); | 			_recentCustomIds.emplace(id); | ||||||
| 		} | 		} | ||||||
|  | @ -963,7 +973,21 @@ void EmojiListWidget::drawRecent( | ||||||
| 		bool paused, | 		bool paused, | ||||||
| 		int index) { | 		int index) { | ||||||
| 	_recentPainted = true; | 	_recentPainted = true; | ||||||
| 	if (const auto emoji = std::get_if<EmojiPtr>(&_recent[index].id.data)) { | 	auto &recent = _recent[index]; | ||||||
|  | 	if (const auto custom = recent.custom) { | ||||||
|  | 		position += _innerPosition + _customPosition; | ||||||
|  | 		const auto paintContext = Ui::Text::CustomEmoji::Context{ | ||||||
|  | 			.preview = st::windowBgRipple->c, | ||||||
|  | 			.colored = _emojiStatusColor.get(), | ||||||
|  | 			.size = QSize(_customSingleSize, _customSingleSize), | ||||||
|  | 			.now = now, | ||||||
|  | 			.scale = context.progress, | ||||||
|  | 			.position = position, | ||||||
|  | 			.paused = paused, | ||||||
|  | 			.scaled = context.expanding, | ||||||
|  | 		}; | ||||||
|  | 		custom->paint(p, paintContext); | ||||||
|  | 	} else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) { | ||||||
| 		if (_mode == Mode::EmojiStatus) { | 		if (_mode == Mode::EmojiStatus) { | ||||||
| 			position += QPoint( | 			position += QPoint( | ||||||
| 				(_singleSize.width() - st::stickersPremium.width()) / 2, | 				(_singleSize.width() - st::stickersPremium.width()) / 2, | ||||||
|  | @ -973,18 +997,6 @@ void EmojiListWidget::drawRecent( | ||||||
| 		} else { | 		} else { | ||||||
| 			drawEmoji(p, context, position, *emoji); | 			drawEmoji(p, context, position, *emoji); | ||||||
| 		} | 		} | ||||||
| 	} else if (const auto custom = _recent[index].custom) { |  | ||||||
| 		position += _innerPosition + _customPosition; |  | ||||||
| 		const auto paintContext = Ui::Text::CustomEmoji::Context{ |  | ||||||
| 			.preview = st::windowBgRipple->c, |  | ||||||
| 			.size = QSize(_customSingleSize, _customSingleSize), |  | ||||||
| 			.now = now, |  | ||||||
| 			.scale = context.progress, |  | ||||||
| 			.position = position, |  | ||||||
| 			.paused = paused, |  | ||||||
| 			.scaled = context.expanding, |  | ||||||
| 		}; |  | ||||||
| 		custom->paint(p, paintContext); |  | ||||||
| 	} else { | 	} else { | ||||||
| 		Unexpected("Empty custom emoji in EmojiListWidget::drawRecent."); | 		Unexpected("Empty custom emoji in EmojiListWidget::drawRecent."); | ||||||
| 	} | 	} | ||||||
|  | @ -1013,9 +1025,12 @@ void EmojiListWidget::drawCustom( | ||||||
| 		int set, | 		int set, | ||||||
| 		int index) { | 		int index) { | ||||||
| 	position += _innerPosition + _customPosition; | 	position += _innerPosition + _customPosition; | ||||||
| 	_custom[set].painted = true; | 	auto &custom = _custom[set]; | ||||||
| 	_custom[set].list[index].custom->paint(p, { | 	custom.painted = true; | ||||||
|  | 	auto &entry = custom.list[index]; | ||||||
|  | 	entry.custom->paint(p, { | ||||||
| 		.preview = st::windowBgRipple->c, | 		.preview = st::windowBgRipple->c, | ||||||
|  | 		.colored = _emojiStatusColor.get(), | ||||||
| 		.size = QSize(_customSingleSize, _customSingleSize), | 		.size = QSize(_customSingleSize, _customSingleSize), | ||||||
| 		.now = now, | 		.now = now, | ||||||
| 		.scale = context.progress, | 		.scale = context.progress, | ||||||
|  |  | ||||||
|  | @ -37,6 +37,10 @@ namespace Ui::Emoji { | ||||||
| enum class Section; | enum class Section; | ||||||
| } // namespace Ui::Emoji
 | } // namespace Ui::Emoji
 | ||||||
| 
 | 
 | ||||||
|  | namespace Ui::Text { | ||||||
|  | struct CustomEmojiColored; | ||||||
|  | } // namespace Ui::Text
 | ||||||
|  | 
 | ||||||
| namespace Ui::CustomEmoji { | namespace Ui::CustomEmoji { | ||||||
| class Loader; | class Loader; | ||||||
| class Instance; | class Instance; | ||||||
|  | @ -294,6 +298,7 @@ private: | ||||||
| 	void displaySet(uint64 setId); | 	void displaySet(uint64 setId); | ||||||
| 	void removeSet(uint64 setId); | 	void removeSet(uint64 setId); | ||||||
| 
 | 
 | ||||||
|  | 	void refreshColoredStatuses(); | ||||||
| 	void initButton(RightButton &button, const QString &text, bool gradient); | 	void initButton(RightButton &button, const QString &text, bool gradient); | ||||||
| 	[[nodiscard]] std::unique_ptr<Ui::RippleAnimation> createButtonRipple( | 	[[nodiscard]] std::unique_ptr<Ui::RippleAnimation> createButtonRipple( | ||||||
| 		int section); | 		int section); | ||||||
|  | @ -328,6 +333,7 @@ private: | ||||||
| 	std::vector<RecentOne> _recent; | 	std::vector<RecentOne> _recent; | ||||||
| 	base::flat_set<DocumentId> _recentCustomIds; | 	base::flat_set<DocumentId> _recentCustomIds; | ||||||
| 	base::flat_set<uint64> _repaintsScheduled; | 	base::flat_set<uint64> _repaintsScheduled; | ||||||
|  | 	std::unique_ptr<Ui::Text::CustomEmojiColored> _emojiStatusColor; | ||||||
| 	bool _recentPainted = false; | 	bool _recentPainted = false; | ||||||
| 	QVector<EmojiPtr> _emoji[kEmojiSectionCount]; | 	QVector<EmojiPtr> _emoji[kEmojiSectionCount]; | ||||||
| 	std::vector<CustomSet> _custom; | 	std::vector<CustomSet> _custom; | ||||||
|  |  | ||||||
|  | @ -6,10 +6,12 @@ For license and copyright information please follow this link: | ||||||
| https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | ||||||
| */ | */ | ||||||
| #include "data/data_emoji_statuses.h" | #include "data/data_emoji_statuses.h" | ||||||
| //
 | 
 | ||||||
| #include "main/main_session.h" | #include "main/main_session.h" | ||||||
| #include "data/data_user.h" | #include "data/data_user.h" | ||||||
| #include "data/data_session.h" | #include "data/data_session.h" | ||||||
|  | #include "data/data_document.h" | ||||||
|  | #include "data/stickers/data_stickers.h" | ||||||
| #include "base/timer_rpl.h" | #include "base/timer_rpl.h" | ||||||
| #include "base/call_delayed.h" | #include "base/call_delayed.h" | ||||||
| #include "apiwrap.h" | #include "apiwrap.h" | ||||||
|  |  | ||||||
|  | @ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "data/stickers/data_custom_emoji.h" | #include "data/stickers/data_custom_emoji.h" | ||||||
| 
 | 
 | ||||||
| #include "chat_helpers/stickers_emoji_pack.h" | #include "chat_helpers/stickers_emoji_pack.h" | ||||||
|  | #include "main/main_account.h" | ||||||
|  | #include "main/main_app_config.h" | ||||||
| #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" | ||||||
|  | @ -78,7 +80,7 @@ public: | ||||||
| 		SizeTag tag, | 		SizeTag tag, | ||||||
| 		int sizeOverride); | 		int sizeOverride); | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] bool resolving() const; | 	[[nodiscard]] DocumentData *document() const; | ||||||
| 	void resolved(not_null<DocumentData*> document); | 	void resolved(not_null<DocumentData*> document); | ||||||
| 
 | 
 | ||||||
| 	QString entityData() override; | 	QString entityData() override; | ||||||
|  | @ -154,12 +156,16 @@ CustomEmojiLoader::CustomEmojiLoader( | ||||||
| 		&& sizeOverride <= std::numeric_limits<ushort>::max()); | 		&& sizeOverride <= std::numeric_limits<ushort>::max()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool CustomEmojiLoader::resolving() const { | DocumentData *CustomEmojiLoader::document() const { | ||||||
| 	return v::is<Resolve>(_state); | 	return v::match(_state, [](const Resolve &) { | ||||||
|  | 		return (DocumentData*)nullptr; | ||||||
|  | 	}, [](const auto &data) { | ||||||
|  | 		return data.document.get(); | ||||||
|  | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CustomEmojiLoader::resolved(not_null<DocumentData*> document) { | void CustomEmojiLoader::resolved(not_null<DocumentData*> document) { | ||||||
| 	Expects(resolving()); | 	Expects(v::is<Resolve>(_state)); | ||||||
| 
 | 
 | ||||||
| 	auto requested = std::move(v::get<Resolve>(_state).requested); | 	auto requested = std::move(v::get<Resolve>(_state).requested); | ||||||
| 	_state = Lookup{ document }; | 	_state = Lookup{ document }; | ||||||
|  | @ -385,6 +391,22 @@ Ui::CustomEmoji::Preview CustomEmojiLoader::preview() { | ||||||
| CustomEmojiManager::CustomEmojiManager(not_null<Session*> owner) | CustomEmojiManager::CustomEmojiManager(not_null<Session*> owner) | ||||||
| : _owner(owner) | : _owner(owner) | ||||||
| , _repaintTimer([=] { invokeRepaints(); }) { | , _repaintTimer([=] { invokeRepaints(); }) { | ||||||
|  | 	const auto appConfig = &owner->session().account().appConfig(); | ||||||
|  | 	appConfig->value( | ||||||
|  | 	) | rpl::take_while([=] { | ||||||
|  | 		return !_coloredSetId; | ||||||
|  | 	}) | rpl::start_with_next([=] { | ||||||
|  | 		const auto setId = appConfig->get<QString>( | ||||||
|  | 			"default_emoji_statuses_stickerset_id", | ||||||
|  | 			QString()).toULongLong(); | ||||||
|  | 		if (setId) { | ||||||
|  | 			_coloredSetId = setId; | ||||||
|  | 			auto pending = base::take(_coloredSetPending); | ||||||
|  | 			for (const auto &instance : pending[setId]) { | ||||||
|  | 				instance->setColored(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}, _lifetime); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CustomEmojiManager::~CustomEmojiManager() = default; | CustomEmojiManager::~CustomEmojiManager() = default; | ||||||
|  | @ -405,12 +427,20 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create( | ||||||
| 				Ui::CustomEmoji::RepaintRequest request) { | 				Ui::CustomEmoji::RepaintRequest request) { | ||||||
| 			repaintLater(instance, request); | 			repaintLater(instance, request); | ||||||
| 		}; | 		}; | ||||||
|  | 		auto [loader, setId] = factory(); | ||||||
| 		i = instances.emplace( | 		i = instances.emplace( | ||||||
| 			documentId, | 			documentId, | ||||||
| 			std::make_unique<Ui::CustomEmoji::Instance>(Loading{ | 			std::make_unique<Ui::CustomEmoji::Instance>(Loading{ | ||||||
| 				factory(), | 				std::move(loader), | ||||||
| 				prepareNonExactPreview(documentId, tag, sizeOverride) | 				prepareNonExactPreview(documentId, tag, sizeOverride) | ||||||
| 			}, std::move(repaint))).first; | 			}, std::move(repaint))).first; | ||||||
|  | 		if (_coloredSetId) { | ||||||
|  | 			if (_coloredSetId == setId) { | ||||||
|  | 				i->second->setColored(); | ||||||
|  | 			} | ||||||
|  | 		} else if (setId) { | ||||||
|  | 			_coloredSetPending[setId].emplace(i->second.get()); | ||||||
|  | 		} | ||||||
| 	} else if (!i->second->hasImagePreview()) { | 	} else if (!i->second->hasImagePreview()) { | ||||||
| 		auto preview = prepareNonExactPreview(documentId, tag, sizeOverride); | 		auto preview = prepareNonExactPreview(documentId, tag, sizeOverride); | ||||||
| 		if (preview.isImage()) { | 		if (preview.isImage()) { | ||||||
|  | @ -466,7 +496,7 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create( | ||||||
| 		SizeTag tag, | 		SizeTag tag, | ||||||
| 		int sizeOverride) { | 		int sizeOverride) { | ||||||
| 	return create(documentId, std::move(update), tag, sizeOverride, [&] { | 	return create(documentId, std::move(update), tag, sizeOverride, [&] { | ||||||
| 		return createLoader(documentId, tag, sizeOverride); | 		return createLoaderWithSetId(documentId, tag, sizeOverride); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -476,7 +506,7 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create( | ||||||
| 		SizeTag tag, | 		SizeTag tag, | ||||||
| 		int sizeOverride) { | 		int sizeOverride) { | ||||||
| 	return create(document->id, std::move(update), tag, sizeOverride, [&] { | 	return create(document->id, std::move(update), tag, sizeOverride, [&] { | ||||||
| 		return createLoader(document, tag, sizeOverride); | 		return createLoaderWithSetId(document, tag, sizeOverride); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -517,19 +547,43 @@ std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader( | ||||||
| 		not_null<DocumentData*> document, | 		not_null<DocumentData*> document, | ||||||
| 		SizeTag tag, | 		SizeTag tag, | ||||||
| 		int sizeOverride) { | 		int sizeOverride) { | ||||||
| 	return std::make_unique<CustomEmojiLoader>(document, tag, sizeOverride); | 	return createLoaderWithSetId(document, tag, sizeOverride).loader; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader( | std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader( | ||||||
| 		DocumentId documentId, | 		DocumentId documentId, | ||||||
| 		SizeTag tag, | 		SizeTag tag, | ||||||
| 		int sizeOverride) { | 		int sizeOverride) { | ||||||
|  | 	return createLoaderWithSetId(documentId, tag, sizeOverride).loader; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | auto CustomEmojiManager::createLoaderWithSetId( | ||||||
|  | 	not_null<DocumentData*> document, | ||||||
|  | 	SizeTag tag, | ||||||
|  | 	int sizeOverride | ||||||
|  | ) -> LoaderWithSetId { | ||||||
|  | 	const auto sticker = document->sticker(); | ||||||
|  | 	return { | ||||||
|  | 		std::make_unique<CustomEmojiLoader>(document, tag, sizeOverride), | ||||||
|  | 		sticker ? sticker->set.id : uint64() | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | auto CustomEmojiManager::createLoaderWithSetId( | ||||||
|  | 	DocumentId documentId, | ||||||
|  | 	SizeTag tag, | ||||||
|  | 	int sizeOverride | ||||||
|  | ) -> LoaderWithSetId { | ||||||
| 	auto result = std::make_unique<CustomEmojiLoader>( | 	auto result = std::make_unique<CustomEmojiLoader>( | ||||||
| 		_owner, | 		_owner, | ||||||
| 		CustomEmojiId{ .id = documentId }, | 		CustomEmojiId{ .id = documentId }, | ||||||
| 		tag, | 		tag, | ||||||
| 		sizeOverride); | 		sizeOverride); | ||||||
| 	if (result->resolving()) { | 	if (const auto document = result->document()) { | ||||||
|  | 		if (const auto sticker = document->sticker()) { | ||||||
|  | 			return { std::move(result), sticker->set.id }; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
| 		const auto i = SizeIndex(tag); | 		const auto i = SizeIndex(tag); | ||||||
| 		_loaders[i][documentId].push_back(base::make_weak(result.get())); | 		_loaders[i][documentId].push_back(base::make_weak(result.get())); | ||||||
| 		_pendingForRequest.emplace(documentId); | 		_pendingForRequest.emplace(documentId); | ||||||
|  | @ -537,8 +591,7 @@ std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader( | ||||||
| 			crl::on_main(this, [=] { request(); }); | 			crl::on_main(this, [=] { request(); }); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 	return { std::move(result), uint64() }; | ||||||
| 	return result; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QString CustomEmojiManager::lookupSetName(uint64 setId) { | QString CustomEmojiManager::lookupSetName(uint64 setId) { | ||||||
|  | @ -564,6 +617,38 @@ void CustomEmojiManager::request() { | ||||||
| 	)).done([=](const MTPVector<MTPDocument> &result) { | 	)).done([=](const MTPVector<MTPDocument> &result) { | ||||||
| 		for (const auto &entry : result.v) { | 		for (const auto &entry : result.v) { | ||||||
| 			const auto document = _owner->processDocument(entry); | 			const auto document = _owner->processDocument(entry); | ||||||
|  | 			fillColoredFlags(document); | ||||||
|  | 			processLoaders(document); | ||||||
|  | 			processListeners(document); | ||||||
|  | 			requestSetFor(document); | ||||||
|  | 		} | ||||||
|  | 		requestFinished(); | ||||||
|  | 	}).fail([=] { | ||||||
|  | 		LOG(("API Error: Failed to get documents for emoji.")); | ||||||
|  | 		requestFinished(); | ||||||
|  | 	}).send(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CustomEmojiManager::fillColoredFlags(not_null<DocumentData*> document) { | ||||||
|  | 	const auto id = document->id; | ||||||
|  | 	const auto sticker = document->sticker(); | ||||||
|  | 	const auto setId = sticker ? sticker->set.id : uint64(); | ||||||
|  | 	if (!setId || (_coloredSetId && setId != _coloredSetId)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	for (auto &instances : _instances) { | ||||||
|  | 		const auto i = instances.find(id); | ||||||
|  | 		if (i != end(instances)) { | ||||||
|  | 			if (setId == _coloredSetId) { | ||||||
|  | 				i->second->setColored(); | ||||||
|  | 			} else { | ||||||
|  | 				_coloredSetPending[setId].emplace(i->second.get()); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CustomEmojiManager::processLoaders(not_null<DocumentData*> document) { | ||||||
| 	const auto id = document->id; | 	const auto id = document->id; | ||||||
| 	for (auto &loaders : _loaders) { | 	for (auto &loaders : _loaders) { | ||||||
| 		if (const auto list = loaders.take(id)) { | 		if (const auto list = loaders.take(id)) { | ||||||
|  | @ -574,6 +659,10 @@ void CustomEmojiManager::request() { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CustomEmojiManager::processListeners(not_null<DocumentData*> document) { | ||||||
|  | 	const auto id = document->id; | ||||||
| 	if (const auto listeners = _resolvers.take(id)) { | 	if (const auto listeners = _resolvers.take(id)) { | ||||||
| 		for (const auto &listener : *listeners) { | 		for (const auto &listener : *listeners) { | ||||||
| 			const auto i = _listeners.find(listener); | 			const auto i = _listeners.find(listener); | ||||||
|  | @ -585,13 +674,6 @@ void CustomEmojiManager::request() { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 			requestSetFor(document); |  | ||||||
| 		} |  | ||||||
| 		requestFinished(); |  | ||||||
| 	}).fail([=] { |  | ||||||
| 		LOG(("API Error: Failed to get documents for emoji.")); |  | ||||||
| 		requestFinished(); |  | ||||||
| 	}).send(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CustomEmojiManager::requestSetFor(not_null<DocumentData*> document) { | void CustomEmojiManager::requestSetFor(not_null<DocumentData*> document) { | ||||||
|  |  | ||||||
|  | @ -84,6 +84,19 @@ private: | ||||||
| 		crl::time when = 0; | 		crl::time when = 0; | ||||||
| 		std::vector<base::weak_ptr<Ui::CustomEmoji::Instance>> instances; | 		std::vector<base::weak_ptr<Ui::CustomEmoji::Instance>> instances; | ||||||
| 	}; | 	}; | ||||||
|  | 	struct LoaderWithSetId { | ||||||
|  | 		std::unique_ptr<Ui::CustomEmoji::Loader> loader; | ||||||
|  | 		uint64 setId = 0; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	[[nodiscard]] LoaderWithSetId createLoaderWithSetId( | ||||||
|  | 		not_null<DocumentData*> document, | ||||||
|  | 		SizeTag tag, | ||||||
|  | 		int sizeOverride = 0); | ||||||
|  | 	[[nodiscard]] LoaderWithSetId createLoaderWithSetId( | ||||||
|  | 		DocumentId documentId, | ||||||
|  | 		SizeTag tag, | ||||||
|  | 		int sizeOverride = 0); | ||||||
| 
 | 
 | ||||||
| 	void request(); | 	void request(); | ||||||
| 	void requestFinished(); | 	void requestFinished(); | ||||||
|  | @ -92,6 +105,9 @@ private: | ||||||
| 		Ui::CustomEmoji::RepaintRequest request); | 		Ui::CustomEmoji::RepaintRequest request); | ||||||
| 	void scheduleRepaintTimer(); | 	void scheduleRepaintTimer(); | ||||||
| 	void invokeRepaints(); | 	void invokeRepaints(); | ||||||
|  | 	void fillColoredFlags(not_null<DocumentData*> document); | ||||||
|  | 	void processLoaders(not_null<DocumentData*> document); | ||||||
|  | 	void processListeners(not_null<DocumentData*> document); | ||||||
| 	void requestSetFor(not_null<DocumentData*> document); | 	void requestSetFor(not_null<DocumentData*> document); | ||||||
| 
 | 
 | ||||||
| 	[[nodiscard]] Ui::CustomEmoji::Preview prepareNonExactPreview( | 	[[nodiscard]] Ui::CustomEmoji::Preview prepareNonExactPreview( | ||||||
|  | @ -126,14 +142,23 @@ private: | ||||||
| 		not_null<Listener*>, | 		not_null<Listener*>, | ||||||
| 		base::flat_set<DocumentId>> _listeners; | 		base::flat_set<DocumentId>> _listeners; | ||||||
| 	base::flat_set<DocumentId> _pendingForRequest; | 	base::flat_set<DocumentId> _pendingForRequest; | ||||||
|  | 	base::flat_map< | ||||||
|  | 		uint64, | ||||||
|  | 		base::flat_set< | ||||||
|  | 			not_null<Ui::CustomEmoji::Instance*>>> _coloredSetPending; | ||||||
|  | 
 | ||||||
| 	mtpRequestId _requestId = 0; | 	mtpRequestId _requestId = 0; | ||||||
| 
 | 
 | ||||||
|  | 	uint64 _coloredSetId = 0; | ||||||
|  | 
 | ||||||
| 	base::flat_map<crl::time, RepaintBunch> _repaints; | 	base::flat_map<crl::time, RepaintBunch> _repaints; | ||||||
| 	crl::time _repaintNext = 0; | 	crl::time _repaintNext = 0; | ||||||
| 	base::Timer _repaintTimer; | 	base::Timer _repaintTimer; | ||||||
| 	bool _repaintTimerScheduled = false; | 	bool _repaintTimerScheduled = false; | ||||||
| 	bool _requestSetsScheduled = false; | 	bool _requestSetsScheduled = false; | ||||||
| 
 | 
 | ||||||
|  | 	rpl::lifetime _lifetime; | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| [[nodiscard]] int FrameSizeFromTag(CustomEmojiManager::SizeTag tag); | [[nodiscard]] int FrameSizeFromTag(CustomEmojiManager::SizeTag tag); | ||||||
|  |  | ||||||
|  | @ -828,6 +828,11 @@ void InnerWidget::paintPeerSearchResult( | ||||||
| 				: selected | 				: selected | ||||||
| 				? &st::dialogsScamFgOver | 				? &st::dialogsScamFgOver | ||||||
| 				: &st::dialogsScamFg), | 				: &st::dialogsScamFg), | ||||||
|  | 			.premiumFg = (active | ||||||
|  | 				? &st::dialogsVerifiedIconBgActive | ||||||
|  | 				: selected | ||||||
|  | 				? &st::dialogsVerifiedIconBgOver | ||||||
|  | 				: &st::dialogsVerifiedIconBg), | ||||||
| 			.preview = (active | 			.preview = (active | ||||||
| 				? st::dialogsScamFgActive | 				? st::dialogsScamFgActive | ||||||
| 				: selected | 				: selected | ||||||
|  |  | ||||||
|  | @ -593,6 +593,11 @@ void paintRow( | ||||||
| 						: selected | 						: selected | ||||||
| 						? &st::dialogsScamFgOver | 						? &st::dialogsScamFgOver | ||||||
| 						: &st::dialogsScamFg), | 						: &st::dialogsScamFg), | ||||||
|  | 					.premiumFg = (active | ||||||
|  | 						? &st::dialogsVerifiedIconBgActive | ||||||
|  | 						: selected | ||||||
|  | 						? &st::dialogsVerifiedIconBgOver | ||||||
|  | 						: &st::dialogsVerifiedIconBg), | ||||||
| 					.preview = (active | 					.preview = (active | ||||||
| 						? st::dialogsScamFgActive | 						? st::dialogsScamFgActive | ||||||
| 						: selected | 						: selected | ||||||
|  |  | ||||||
|  | @ -545,6 +545,7 @@ void TopBarWidget::paintTopBar(Painter &p) { | ||||||
| 				.verified = &st::dialogsVerifiedIcon, | 				.verified = &st::dialogsVerifiedIcon, | ||||||
| 				.premium = &st::dialogsPremiumIcon, | 				.premium = &st::dialogsPremiumIcon, | ||||||
| 				.scam = &st::attentionButtonFg, | 				.scam = &st::attentionButtonFg, | ||||||
|  | 				.premiumFg = &st::dialogsVerifiedIconBg, | ||||||
| 				.preview = st::windowBgOver->c, | 				.preview = st::windowBgOver->c, | ||||||
| 				.customEmojiRepaint = [=] { update(); }, | 				.customEmojiRepaint = [=] { update(); }, | ||||||
| 				.now = now, | 				.now = now, | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ InfoToggle { | ||||||
| InfoPeerBadge { | InfoPeerBadge { | ||||||
| 	verified: icon; | 	verified: icon; | ||||||
| 	premium: icon; | 	premium: icon; | ||||||
|  | 	premiumFg: color; | ||||||
| 	position: point; | 	position: point; | ||||||
| 	sizeTag: int; | 	sizeTag: int; | ||||||
| } | } | ||||||
|  | @ -316,6 +317,7 @@ infoPremiumStar: icon {{ "profile_premium", profileVerifiedCheckBg }}; | ||||||
| infoPeerBadge: InfoPeerBadge { | infoPeerBadge: InfoPeerBadge { | ||||||
| 	verified: infoVerifiedCheck; | 	verified: infoVerifiedCheck; | ||||||
| 	premium: infoPremiumStar; | 	premium: infoPremiumStar; | ||||||
|  | 	premiumFg: profileVerifiedCheckBg; | ||||||
| 	position: infoVerifiedCheckPosition; | 	position: infoVerifiedCheckPosition; | ||||||
| 	sizeTag: 1; // Large | 	sizeTag: 1; // Large | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -122,6 +122,7 @@ void BadgeView::setBadge(Badge badge, DocumentId emojiStatusId) { | ||||||
| 	_badge = badge; | 	_badge = badge; | ||||||
| 	_emojiStatusId = emojiStatusId; | 	_emojiStatusId = emojiStatusId; | ||||||
| 	_emojiStatus = nullptr; | 	_emojiStatus = nullptr; | ||||||
|  | 	_emojiStatusColored = nullptr; | ||||||
| 	_view.destroy(); | 	_view.destroy(); | ||||||
| 	if (_badge == Badge::None) { | 	if (_badge == Badge::None) { | ||||||
| 		_updated.fire({}); | 		_updated.fire({}); | ||||||
|  | @ -143,14 +144,19 @@ void BadgeView::setBadge(Badge badge, DocumentId emojiStatusId) { | ||||||
| 				_emojiStatusId, | 				_emojiStatusId, | ||||||
| 				[raw = _view.data()] { raw->update(); }, | 				[raw = _view.data()] { raw->update(); }, | ||||||
| 				tag); | 				tag); | ||||||
|  | 			_emojiStatusColored = std::make_unique< | ||||||
|  | 				Ui::Text::CustomEmojiColored | ||||||
|  | 			>(); | ||||||
| 			const auto emoji = Data::FrameSizeFromTag(tag) | 			const auto emoji = Data::FrameSizeFromTag(tag) | ||||||
| 				/ style::DevicePixelRatio(); | 				/ style::DevicePixelRatio(); | ||||||
| 			_view->resize(emoji, emoji); | 			_view->resize(emoji, emoji); | ||||||
| 			_view->paintRequest( | 			_view->paintRequest( | ||||||
| 			) | rpl::start_with_next([=, check = _view.data()]{ | 			) | rpl::start_with_next([=, check = _view.data()]{ | ||||||
| 				Painter p(check); | 				Painter p(check); | ||||||
|  | 				_emojiStatusColored->color = _st.premiumFg->c; | ||||||
| 				_emojiStatus->paint(p, { | 				_emojiStatus->paint(p, { | ||||||
| 					.preview = st::windowBgOver->c, | 					.preview = st::windowBgOver->c, | ||||||
|  | 					.colored = _emojiStatusColored.get(), | ||||||
| 					.now = crl::now(), | 					.now = crl::now(), | ||||||
| 					.paused = _animationPaused && _animationPaused(), | 					.paused = _animationPaused && _animationPaused(), | ||||||
| 				}); | 				}); | ||||||
|  |  | ||||||
|  | @ -33,6 +33,10 @@ template <typename Widget> | ||||||
| class SlideWrap; | class SlideWrap; | ||||||
| } // namespace Ui
 | } // namespace Ui
 | ||||||
| 
 | 
 | ||||||
|  | namespace Ui::Text { | ||||||
|  | struct CustomEmojiColored; | ||||||
|  | } // namespace Ui::Text
 | ||||||
|  | 
 | ||||||
| namespace Info { | namespace Info { | ||||||
| class Controller; | class Controller; | ||||||
| class Section; | class Section; | ||||||
|  | @ -73,6 +77,7 @@ private: | ||||||
| 	const not_null<PeerData*> _peer; | 	const not_null<PeerData*> _peer; | ||||||
| 	DocumentId _emojiStatusId = 0; | 	DocumentId _emojiStatusId = 0; | ||||||
| 	std::unique_ptr<Ui::Text::CustomEmoji> _emojiStatus; | 	std::unique_ptr<Ui::Text::CustomEmoji> _emojiStatus; | ||||||
|  | 	std::unique_ptr<Ui::Text::CustomEmojiColored> _emojiStatusColored; | ||||||
| 	base::flags<Badge> _allowed; | 	base::flags<Badge> _allowed; | ||||||
| 	Badge _badge = Badge(); | 	Badge _badge = Badge(); | ||||||
| 	Fn<void()> _premiumClickCallback; | 	Fn<void()> _premiumClickCallback; | ||||||
|  |  | ||||||
|  | @ -186,6 +186,7 @@ settingsInfoNameSkip: -1px; | ||||||
| settingsInfoUploadLeft: 6px; | settingsInfoUploadLeft: 6px; | ||||||
| settingsInfoPeerBadge: InfoPeerBadge { | settingsInfoPeerBadge: InfoPeerBadge { | ||||||
| 	premium: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBg }}; | 	premium: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBg }}; | ||||||
|  | 	premiumFg: dialogsVerifiedIconBg; | ||||||
| 	sizeTag: 0; // Normal | 	sizeTag: 0; // Normal | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -169,9 +169,12 @@ int PeerBadge::drawGetWidth( | ||||||
| 		} | 		} | ||||||
| 		if (!_emojiStatus) { | 		if (!_emojiStatus) { | ||||||
| 			_emojiStatus = std::make_unique<EmojiStatus>(); | 			_emojiStatus = std::make_unique<EmojiStatus>(); | ||||||
| 			const auto size = st::emojiSize * 1.; | 			const auto size = st::emojiSize; | ||||||
| 			const auto emoji = Ui::Text::AdjustCustomEmojiSize(st::emojiSize); | 			const auto emoji = Ui::Text::AdjustCustomEmojiSize(size); | ||||||
| 			_emojiStatus->skip = (st::emojiSize - emoji) / 2; | 			_emojiStatus->skip = (size - emoji) / 2; | ||||||
|  | 			_emojiStatus->colored = std::make_unique< | ||||||
|  | 				Ui::Text::CustomEmojiColored | ||||||
|  | 			>(); | ||||||
| 		} | 		} | ||||||
| 		if (_emojiStatus->id != id) { | 		if (_emojiStatus->id != id) { | ||||||
| 			auto &manager = peer->session().data().customEmojiManager(); | 			auto &manager = peer->session().data().customEmojiManager(); | ||||||
|  | @ -180,8 +183,10 @@ int PeerBadge::drawGetWidth( | ||||||
| 				id, | 				id, | ||||||
| 				descriptor.customEmojiRepaint); | 				descriptor.customEmojiRepaint); | ||||||
| 		} | 		} | ||||||
|  | 		_emojiStatus->colored->color = (*descriptor.premiumFg)->c; | ||||||
| 		_emojiStatus->emoji->paint(p, { | 		_emojiStatus->emoji->paint(p, { | ||||||
| 			.preview = descriptor.preview, | 			.preview = descriptor.preview, | ||||||
|  | 			.colored = _emojiStatus->colored.get(), | ||||||
| 			.now = descriptor.now, | 			.now = descriptor.now, | ||||||
| 			.position = QPoint( | 			.position = QPoint( | ||||||
| 				iconx - 2 * _emojiStatus->skip, | 				iconx - 2 * _emojiStatus->skip, | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| 
 | 
 | ||||||
| namespace Ui::Text { | namespace Ui::Text { | ||||||
| class CustomEmoji; | class CustomEmoji; | ||||||
|  | struct CustomEmojiColored; | ||||||
| } // namespace Ui::Text
 | } // namespace Ui::Text
 | ||||||
| 
 | 
 | ||||||
| namespace Ui { | namespace Ui { | ||||||
|  | @ -41,6 +42,7 @@ public: | ||||||
| 		const style::icon *verified = nullptr; | 		const style::icon *verified = nullptr; | ||||||
| 		const style::icon *premium = nullptr; | 		const style::icon *premium = nullptr; | ||||||
| 		const style::color *scam = nullptr; | 		const style::color *scam = nullptr; | ||||||
|  | 		const style::color *premiumFg = nullptr; | ||||||
| 		QColor preview; | 		QColor preview; | ||||||
| 		Fn<void()> customEmojiRepaint; | 		Fn<void()> customEmojiRepaint; | ||||||
| 		crl::time now = 0; | 		crl::time now = 0; | ||||||
|  | @ -58,6 +60,7 @@ private: | ||||||
| 	struct EmojiStatus { | 	struct EmojiStatus { | ||||||
| 		DocumentId id = 0; | 		DocumentId id = 0; | ||||||
| 		std::unique_ptr<Ui::Text::CustomEmoji> emoji; | 		std::unique_ptr<Ui::Text::CustomEmoji> emoji; | ||||||
|  | 		std::unique_ptr<Ui::Text::CustomEmojiColored> colored; | ||||||
| 		int skip = 0; | 		int skip = 0; | ||||||
| 	}; | 	}; | ||||||
| 	std::unique_ptr<EmojiStatus> _emojiStatus; | 	std::unique_ptr<EmojiStatus> _emojiStatus; | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit 6dc6309269beb64a69e3184b671e030a2969f00e | Subproject commit 51657b3c8a643c9ca2721029fd48f63390417042 | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 John Preston
						John Preston