Support OnlyCustomEmoji unwrapped messages.
This commit is contained in:
parent
8a91c949c2
commit
7a88f9434e
|
@ -618,6 +618,8 @@ PRIVATE
|
||||||
history/view/media/history_view_call.cpp
|
history/view/media/history_view_call.cpp
|
||||||
history/view/media/history_view_contact.h
|
history/view/media/history_view_contact.h
|
||||||
history/view/media/history_view_contact.cpp
|
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.h
|
||||||
history/view/media/history_view_dice.cpp
|
history/view/media/history_view_dice.cpp
|
||||||
history/view/media/history_view_document.h
|
history/view/media/history_view_document.h
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "chat_helpers/stickers_emoji_image_loader.h"
|
#include "chat_helpers/stickers_emoji_image_loader.h"
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
|
#include "history/history.h"
|
||||||
#include "lottie/lottie_common.h"
|
#include "lottie/lottie_common.h"
|
||||||
#include "ui/emoji_config.h"
|
#include "ui/emoji_config.h"
|
||||||
#include "ui/text/text_isolated_emoji.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_file_origin.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
|
@ -121,7 +123,22 @@ EmojiPack::EmojiPack(not_null<Main::Session*> session)
|
||||||
EmojiPack::~EmojiPack() = default;
|
EmojiPack::~EmojiPack() = default;
|
||||||
|
|
||||||
bool EmojiPack::add(not_null<HistoryItem*> item) {
|
bool EmojiPack::add(not_null<HistoryItem*> 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);
|
_items[emoji].emplace(item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -129,16 +146,28 @@ bool EmojiPack::add(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiPack::remove(not_null<const HistoryItem*> item) {
|
void EmojiPack::remove(not_null<const HistoryItem*> item) {
|
||||||
Expects(item->isIsolatedEmoji());
|
Expects(item->isIsolatedEmoji() || item->isOnlyCustomEmoji());
|
||||||
|
|
||||||
const auto emoji = item->isolatedEmoji();
|
if (item->isOnlyCustomEmoji()) {
|
||||||
const auto i = _items.find(emoji);
|
if (const auto list = _onlyCustomItems.take(item)) {
|
||||||
Assert(i != end(_items));
|
for (const auto id : *list) {
|
||||||
const auto j = i->second.find(item);
|
const auto i = _onlyCustomWaiting.find(id);
|
||||||
Assert(j != end(i->second));
|
Assert(i != end(_onlyCustomWaiting));
|
||||||
i->second.erase(j);
|
i->second.remove(item);
|
||||||
if (i->second.empty()) {
|
if (i->second.empty()) {
|
||||||
_items.erase(i);
|
_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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,13 @@ private:
|
||||||
base::flat_map<EmojiPtr, std::weak_ptr<LargeEmojiImage>> _images;
|
base::flat_map<EmojiPtr, std::weak_ptr<LargeEmojiImage>> _images;
|
||||||
mtpRequestId _requestId = 0;
|
mtpRequestId _requestId = 0;
|
||||||
|
|
||||||
|
base::flat_map<
|
||||||
|
not_null<HistoryItem*>,
|
||||||
|
base::flat_set<DocumentId>> _onlyCustomItems;
|
||||||
|
base::flat_map<
|
||||||
|
DocumentId,
|
||||||
|
base::flat_set<not_null<HistoryItem*>>> _onlyCustomWaiting;
|
||||||
|
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
EmojiPtr,
|
EmojiPtr,
|
||||||
base::flat_map<int, not_null<DocumentData*>>> _animations;
|
base::flat_map<int, not_null<DocumentData*>>> _animations;
|
||||||
|
|
|
@ -268,8 +268,8 @@ enum class MessageFlag : uint32 {
|
||||||
// Outgoing message and failed to be sent.
|
// Outgoing message and failed to be sent.
|
||||||
SendingFailed = (1U << 26),
|
SendingFailed = (1U << 26),
|
||||||
|
|
||||||
// No media and only a several emoji text.
|
// No media and only a several emoji or an only custom emoji text.
|
||||||
IsolatedEmoji = (1U << 27),
|
SpecialOnlyEmoji = (1U << 27),
|
||||||
|
|
||||||
// Message existing in the message history.
|
// Message existing in the message history.
|
||||||
HistoryEntry = (1U << 28),
|
HistoryEntry = (1U << 28),
|
||||||
|
|
|
@ -35,11 +35,12 @@ constexpr auto kMaxPerRequest = 100;
|
||||||
using SizeTag = CustomEmojiManager::SizeTag;
|
using SizeTag = CustomEmojiManager::SizeTag;
|
||||||
|
|
||||||
[[nodiscard]] ChatHelpers::StickerLottieSize LottieSizeFromTag(SizeTag tag) {
|
[[nodiscard]] ChatHelpers::StickerLottieSize LottieSizeFromTag(SizeTag tag) {
|
||||||
|
// NB! onlyCustomEmoji dimensions caching uses last ::EmojiInteraction-s.
|
||||||
using LottieSize = ChatHelpers::StickerLottieSize;
|
using LottieSize = ChatHelpers::StickerLottieSize;
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case SizeTag::Normal: return LottieSize::MessageHistory;
|
case SizeTag::Normal: return LottieSize::EmojiInteraction;
|
||||||
case SizeTag::Large: return LottieSize::StickersPanel;
|
case SizeTag::Large: return LottieSize::EmojiInteractionReserved1;
|
||||||
case SizeTag::Isolated: return LottieSize::EmojiInteraction;
|
case SizeTag::Isolated: return LottieSize::EmojiInteractionReserved2;
|
||||||
}
|
}
|
||||||
Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag.");
|
Unexpected("SizeTag value in CustomEmojiManager-LottieSizeFromTag.");
|
||||||
}
|
}
|
||||||
|
@ -55,12 +56,6 @@ using SizeTag = CustomEmojiManager::SizeTag;
|
||||||
Unexpected("SizeTag value in CustomEmojiManager-SizeFromTag.");
|
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
|
} // namespace
|
||||||
|
|
||||||
class CustomEmojiLoader final
|
class CustomEmojiLoader final
|
||||||
|
@ -233,7 +228,7 @@ void CustomEmojiLoader::startCacheLookup(
|
||||||
lookup->process = std::make_unique<Process>(Process{
|
lookup->process = std::make_unique<Process>(Process{
|
||||||
.loaded = std::move(loaded),
|
.loaded = std::move(loaded),
|
||||||
});
|
});
|
||||||
const auto size = SizeFromTag(_tag);
|
const auto size = FrameSizeFromTag(_tag);
|
||||||
const auto weak = base::make_weak(&lookup->process->guard);
|
const auto weak = base::make_weak(&lookup->process->guard);
|
||||||
document->owner().cacheBigFile().get(key, [=](QByteArray value) {
|
document->owner().cacheBigFile().get(key, [=](QByteArray value) {
|
||||||
auto cache = Ui::CustomEmoji::Cache::FromSerialized(value, size);
|
auto cache = Ui::CustomEmoji::Cache::FromSerialized(value, size);
|
||||||
|
@ -286,7 +281,7 @@ void CustomEmojiLoader::check() {
|
||||||
load->process->lifetime.destroy();
|
load->process->lifetime.destroy();
|
||||||
|
|
||||||
const auto tag = _tag;
|
const auto tag = _tag;
|
||||||
const auto size = SizeFromTag(_tag);
|
const auto size = FrameSizeFromTag(_tag);
|
||||||
auto bytes = Lottie::ReadContent(data, filepath);
|
auto bytes = Lottie::ReadContent(data, filepath);
|
||||||
auto loader = [=] {
|
auto loader = [=] {
|
||||||
return std::make_unique<CustomEmojiLoader>(document, tag);
|
return std::make_unique<CustomEmojiLoader>(document, tag);
|
||||||
|
@ -348,7 +343,7 @@ Ui::CustomEmoji::Preview CustomEmojiLoader::preview() {
|
||||||
|| !dimensions.width()) {
|
|| !dimensions.width()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const auto scale = (SizeFromTag(_tag) * 1.)
|
const auto scale = (FrameSizeFromTag(_tag) * 1.)
|
||||||
/ (style::DevicePixelRatio() * dimensions.width());
|
/ (style::DevicePixelRatio() * dimensions.width());
|
||||||
return { document->createMediaView()->thumbnailPath(), scale };
|
return { document->createMediaView()->thumbnailPath(), scale };
|
||||||
};
|
};
|
||||||
|
@ -411,7 +406,7 @@ Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview(
|
||||||
if (j == end(other)) {
|
if (j == end(other)) {
|
||||||
continue;
|
continue;
|
||||||
} else if (const auto nonExact = j->second->imagePreview()) {
|
} else if (const auto nonExact = j->second->imagePreview()) {
|
||||||
const auto size = SizeFromTag(tag);
|
const auto size = FrameSizeFromTag(tag);
|
||||||
return {
|
return {
|
||||||
nonExact.image().scaled(
|
nonExact.image().scaled(
|
||||||
size,
|
size,
|
||||||
|
@ -451,6 +446,24 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CustomEmojiManager::resolved(QStringView data, Fn<void()> callback) {
|
||||||
|
return resolved(ParseCustomEmojiData(data).id, std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CustomEmojiManager::resolved(
|
||||||
|
DocumentId documentId,
|
||||||
|
Fn<void()> 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<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
|
std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
SizeTag tag) {
|
SizeTag tag) {
|
||||||
|
@ -510,6 +523,11 @@ void CustomEmojiManager::request() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (const auto callbacks = _resolvers.take(id)) {
|
||||||
|
for (const auto &callback : *callbacks) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
requestSetFor(document);
|
requestSetFor(document);
|
||||||
}
|
}
|
||||||
requestFinished();
|
requestFinished();
|
||||||
|
@ -623,6 +641,12 @@ Session &CustomEmojiManager::owner() const {
|
||||||
return *_owner;
|
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) {
|
QString SerializeCustomEmojiId(const CustomEmojiId &id) {
|
||||||
return QString::number(id.id)
|
return QString::number(id.id)
|
||||||
+ ':'
|
+ ':'
|
||||||
|
|
|
@ -54,6 +54,9 @@ public:
|
||||||
Fn<void()> update,
|
Fn<void()> update,
|
||||||
SizeTag tag = SizeTag::Normal);
|
SizeTag tag = SizeTag::Normal);
|
||||||
|
|
||||||
|
bool resolved(QStringView data, Fn<void()> callback);
|
||||||
|
bool resolved(DocumentId documentId, Fn<void()> callback);
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader(
|
[[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
SizeTag tag);
|
SizeTag tag);
|
||||||
|
@ -98,15 +101,16 @@ private:
|
||||||
|
|
||||||
std::array<
|
std::array<
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
uint64,
|
DocumentId,
|
||||||
std::unique_ptr<Ui::CustomEmoji::Instance>>,
|
std::unique_ptr<Ui::CustomEmoji::Instance>>,
|
||||||
kSizeCount> _instances;
|
kSizeCount> _instances;
|
||||||
std::array<
|
std::array<
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
uint64,
|
DocumentId,
|
||||||
std::vector<base::weak_ptr<CustomEmojiLoader>>>,
|
std::vector<base::weak_ptr<CustomEmojiLoader>>>,
|
||||||
kSizeCount> _loaders;
|
kSizeCount> _loaders;
|
||||||
base::flat_set<uint64> _pendingForRequest;
|
base::flat_map<DocumentId, std::vector<Fn<void()>>> _resolvers;
|
||||||
|
base::flat_set<DocumentId> _pendingForRequest;
|
||||||
mtpRequestId _requestId = 0;
|
mtpRequestId _requestId = 0;
|
||||||
|
|
||||||
base::flat_map<crl::time, RepaintBunch> _repaints;
|
base::flat_map<crl::time, RepaintBunch> _repaints;
|
||||||
|
@ -117,6 +121,8 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] int FrameSizeFromTag(CustomEmojiManager::SizeTag tag);
|
||||||
|
|
||||||
[[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id);
|
[[nodiscard]] QString SerializeCustomEmojiId(const CustomEmojiId &id);
|
||||||
[[nodiscard]] QString SerializeCustomEmojiId(
|
[[nodiscard]] QString SerializeCustomEmojiId(
|
||||||
not_null<DocumentData*> document);
|
not_null<DocumentData*> document);
|
||||||
|
|
|
@ -1311,6 +1311,10 @@ Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ui::Text::OnlyCustomEmoji HistoryItem::onlyCustomEmoji() const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
HistoryItem::~HistoryItem() {
|
HistoryItem::~HistoryItem() {
|
||||||
applyTTL(0);
|
applyTTL(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,12 @@ public:
|
||||||
return isGroupEssential() && isEmpty();
|
return isGroupEssential() && isEmpty();
|
||||||
}
|
}
|
||||||
[[nodiscard]] bool isIsolatedEmoji() const {
|
[[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 {
|
[[nodiscard]] bool hasViews() const {
|
||||||
return _flags & MessageFlag::HasViews;
|
return _flags & MessageFlag::HasViews;
|
||||||
|
@ -309,6 +314,7 @@ public:
|
||||||
ToPreviewOptions options) const;
|
ToPreviewOptions options) const;
|
||||||
[[nodiscard]] virtual TextWithEntities inReplyText() const;
|
[[nodiscard]] virtual TextWithEntities inReplyText() const;
|
||||||
[[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const;
|
[[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const;
|
||||||
|
[[nodiscard]] virtual Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const;
|
||||||
[[nodiscard]] virtual TextWithEntities originalText() const {
|
[[nodiscard]] virtual TextWithEntities originalText() const {
|
||||||
return TextWithEntities();
|
return TextWithEntities();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1491,7 +1491,7 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearIsolatedEmoji();
|
clearSpecialOnlyEmoji();
|
||||||
const auto context = Core::MarkedTextContext{
|
const auto context = Core::MarkedTextContext{
|
||||||
.session = &history()->session(),
|
.session = &history()->session(),
|
||||||
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
||||||
|
@ -1510,7 +1510,7 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
||||||
EnsureNonEmpty(),
|
EnsureNonEmpty(),
|
||||||
Ui::ItemTextOptions(this));
|
Ui::ItemTextOptions(this));
|
||||||
} else if (!_media) {
|
} else if (!_media) {
|
||||||
checkIsolatedEmoji();
|
checkSpecialOnlyEmoji();
|
||||||
}
|
}
|
||||||
|
|
||||||
_textWidth = -1;
|
_textWidth = -1;
|
||||||
|
@ -1523,7 +1523,7 @@ void HistoryMessage::reapplyText() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessage::setEmptyText() {
|
void HistoryMessage::setEmptyText() {
|
||||||
clearIsolatedEmoji();
|
clearSpecialOnlyEmoji();
|
||||||
_text.setMarkedText(
|
_text.setMarkedText(
|
||||||
st::messageTextStyle,
|
st::messageTextStyle,
|
||||||
{ QString(), EntitiesInText() },
|
{ QString(), EntitiesInText() },
|
||||||
|
@ -1533,17 +1533,17 @@ void HistoryMessage::setEmptyText() {
|
||||||
_textHeight = 0;
|
_textHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessage::clearIsolatedEmoji() {
|
void HistoryMessage::clearSpecialOnlyEmoji() {
|
||||||
if (!(_flags & MessageFlag::IsolatedEmoji)) {
|
if (!(_flags & MessageFlag::SpecialOnlyEmoji)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
history()->session().emojiStickersPack().remove(this);
|
history()->session().emojiStickersPack().remove(this);
|
||||||
_flags &= ~MessageFlag::IsolatedEmoji;
|
_flags &= ~MessageFlag::SpecialOnlyEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryMessage::checkIsolatedEmoji() {
|
void HistoryMessage::checkSpecialOnlyEmoji() {
|
||||||
if (history()->session().emojiStickersPack().add(this)) {
|
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();
|
return _text.toIsolatedEmoji();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ui::Text::OnlyCustomEmoji HistoryMessage::onlyCustomEmoji() const {
|
||||||
|
return _text.toOnlyCustomEmoji();
|
||||||
|
}
|
||||||
|
|
||||||
TextWithEntities HistoryMessage::originalText() const {
|
TextWithEntities HistoryMessage::originalText() const {
|
||||||
if (emptyText()) {
|
if (emptyText()) {
|
||||||
return { QString(), EntitiesInText() };
|
return { QString(), EntitiesInText() };
|
||||||
|
|
|
@ -180,6 +180,7 @@ public:
|
||||||
|
|
||||||
void setText(const TextWithEntities &textWithEntities) override;
|
void setText(const TextWithEntities &textWithEntities) override;
|
||||||
[[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const override;
|
[[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const override;
|
||||||
|
[[nodiscard]] Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const override;
|
||||||
[[nodiscard]] TextWithEntities originalText() const override;
|
[[nodiscard]] TextWithEntities originalText() const override;
|
||||||
[[nodiscard]] auto originalTextWithLocalEntities() const
|
[[nodiscard]] auto originalTextWithLocalEntities() const
|
||||||
-> TextWithEntities override;
|
-> TextWithEntities override;
|
||||||
|
@ -232,8 +233,8 @@ private:
|
||||||
|
|
||||||
[[nodiscard]] bool checkCommentsLinkedChat(ChannelId id) const;
|
[[nodiscard]] bool checkCommentsLinkedChat(ChannelId id) const;
|
||||||
|
|
||||||
void clearIsolatedEmoji();
|
void clearSpecialOnlyEmoji();
|
||||||
void checkIsolatedEmoji();
|
void checkSpecialOnlyEmoji();
|
||||||
|
|
||||||
// For an invoice button we replace the button text with a "Receipt" key.
|
// 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.
|
// It should show the receipt for the payed invoice. Still let mobile apps do that.
|
||||||
|
|
|
@ -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_media_grouped.h"
|
||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
#include "history/view/media/history_view_large_emoji.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_animation.h"
|
||||||
#include "history/view/history_view_react_button.h"
|
#include "history/view/history_view_react_button.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
@ -574,6 +575,13 @@ void Element::refreshMedia(Element *replacing) {
|
||||||
const auto session = &history()->session();
|
const auto session = &history()->session();
|
||||||
if (const auto media = _data->media()) {
|
if (const auto media = _data->media()) {
|
||||||
_media = media->createView(this, replacing);
|
_media = media->createView(this, replacing);
|
||||||
|
} else if (_data->isOnlyCustomEmoji()
|
||||||
|
&& Core::App().settings().largeEmoji()) {
|
||||||
|
_media = std::make_unique<UnwrappedMedia>(
|
||||||
|
this,
|
||||||
|
std::make_unique<CustomEmoji>(
|
||||||
|
this,
|
||||||
|
_data->onlyCustomEmoji()));
|
||||||
} else if (_data->isIsolatedEmoji()
|
} else if (_data->isIsolatedEmoji()
|
||||||
&& Core::App().settings().largeEmoji()) {
|
&& Core::App().settings().largeEmoji()) {
|
||||||
const auto emoji = _data->isolatedEmoji();
|
const auto emoji = _data->isolatedEmoji();
|
||||||
|
|
|
@ -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<Ui::Text::CustomEmoji>;
|
||||||
|
using StickerPtr = std::unique_ptr<Sticker>;
|
||||||
|
|
||||||
|
struct CustomEmojiSizeInfo {
|
||||||
|
LottieSize tag = LottieSize::MessageHistory;
|
||||||
|
float64 scale = 1.;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] const base::flat_map<int, CustomEmojiSizeInfo> &SizesInfo() {
|
||||||
|
// size = i->second.scale * st::maxAnimatedEmojiSize.
|
||||||
|
// CustomEmojiManager::SizeTag caching uses first ::EmojiInteraction-s.
|
||||||
|
using Info = CustomEmojiSizeInfo;
|
||||||
|
static auto result = base::flat_map<int, Info>{
|
||||||
|
{ 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<Element*> 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<Ui::Text::OnlyCustomEmoji::Item>::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<Sticker>(
|
||||||
|
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<LargeCustomEmoji>::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<StickerPtr>(&element)) {
|
||||||
|
paintSticker(p, x, y, sticker->get(), context, paused);
|
||||||
|
} else if (const auto custom = std::get_if<CustomPtr>(&element)) {
|
||||||
|
paintCustom(p, x, y, custom->get(), context, paused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomEmoji::paintSticker(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
not_null<Sticker*> 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<Ui::Text::CustomEmoji*> 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<StickerPtr>(&element)) {
|
||||||
|
(*sticker)->unloadHeavyPart();
|
||||||
|
} else if (const auto custom = std::get_if<CustomPtr>(&element)) {
|
||||||
|
(*custom)->unload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_hasHeavyPart = false;
|
||||||
|
for (const auto &line : _lines) {
|
||||||
|
for (const auto &element : line) {
|
||||||
|
unload(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -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<Sticker>,
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji>>;
|
||||||
|
|
||||||
|
class CustomEmoji final : public UnwrappedMedia::Content {
|
||||||
|
public:
|
||||||
|
CustomEmoji(
|
||||||
|
not_null<Element*> 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*> sticker,
|
||||||
|
const PaintContext &context,
|
||||||
|
bool paused);
|
||||||
|
void paintCustom(
|
||||||
|
Painter &p,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
not_null<Ui::Text::CustomEmoji*> emoji,
|
||||||
|
const PaintContext &context,
|
||||||
|
bool paused);
|
||||||
|
|
||||||
|
const not_null<Element*> _parent;
|
||||||
|
std::vector<std::vector<LargeCustomEmoji>> _lines;
|
||||||
|
QImage _selectedFrame;
|
||||||
|
int _singleSize = 0;
|
||||||
|
bool _hasHeavyPart = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -46,8 +46,8 @@ Dice::Dice(not_null<Element*> parent, not_null<Data::MediaDice*> dice)
|
||||||
|
|
||||||
Dice::~Dice() = default;
|
Dice::~Dice() = default;
|
||||||
|
|
||||||
QSize Dice::size() {
|
QSize Dice::countOptimalSize() {
|
||||||
return _start ? _start->size() : Sticker::EmojiSize();
|
return _start ? _start->countOptimalSize() : Sticker::EmojiSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickHandlerPtr Dice::link() {
|
ClickHandlerPtr Dice::link() {
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
Dice(not_null<Element*> parent, not_null<Data::MediaDice*> dice);
|
Dice(not_null<Element*> parent, not_null<Data::MediaDice*> dice);
|
||||||
~Dice();
|
~Dice();
|
||||||
|
|
||||||
QSize size() override;
|
QSize countOptimalSize() override;
|
||||||
void draw(
|
void draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
|
|
|
@ -68,7 +68,7 @@ LargeEmoji::~LargeEmoji() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize LargeEmoji::size() {
|
QSize LargeEmoji::countOptimalSize() {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
const auto count = _images.size()
|
const auto count = _images.size()
|
||||||
|
|
|
@ -10,10 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/media/history_view_media_unwrapped.h"
|
#include "history/view/media/history_view_media_unwrapped.h"
|
||||||
#include "ui/text/text_isolated_emoji.h"
|
#include "ui/text/text_isolated_emoji.h"
|
||||||
|
|
||||||
namespace Data {
|
|
||||||
struct FileOrigin;
|
|
||||||
} // namespace Data
|
|
||||||
|
|
||||||
namespace Stickers {
|
namespace Stickers {
|
||||||
struct LargeEmojiImage;
|
struct LargeEmojiImage;
|
||||||
} // namespace Stickers
|
} // namespace Stickers
|
||||||
|
@ -32,7 +28,7 @@ public:
|
||||||
const Ui::Text::IsolatedEmoji &emoji);
|
const Ui::Text::IsolatedEmoji &emoji);
|
||||||
~LargeEmoji();
|
~LargeEmoji();
|
||||||
|
|
||||||
QSize size() override;
|
QSize countOptimalSize() override;
|
||||||
void draw(
|
void draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
|
|
|
@ -32,6 +32,10 @@ auto UnwrappedMedia::Content::stickerTakeLottie(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSize UnwrappedMedia::Content::countCurrentSize(int newWidth) {
|
||||||
|
return countOptimalSize();
|
||||||
|
}
|
||||||
|
|
||||||
UnwrappedMedia::UnwrappedMedia(
|
UnwrappedMedia::UnwrappedMedia(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
std::unique_ptr<Content> content)
|
std::unique_ptr<Content> content)
|
||||||
|
@ -41,10 +45,10 @@ UnwrappedMedia::UnwrappedMedia(
|
||||||
|
|
||||||
QSize UnwrappedMedia::countOptimalSize() {
|
QSize UnwrappedMedia::countOptimalSize() {
|
||||||
_content->refreshLink();
|
_content->refreshLink();
|
||||||
_contentSize = DownscaledSize(_content->size(), Sticker::Size());
|
const auto optimal = _content->countOptimalSize();
|
||||||
auto maxWidth = _contentSize.width();
|
auto maxWidth = optimal.width();
|
||||||
const auto minimal = st::largeEmojiSize + 2 * st::largeEmojiOutline;
|
const auto minimal = st::emojiSize;
|
||||||
auto minHeight = std::max(_contentSize.height(), minimal);
|
auto minHeight = std::max(optimal.height(), minimal);
|
||||||
if (_parent->media() == this) {
|
if (_parent->media() == this) {
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
const auto via = item->Get<HistoryMessageVia>();
|
const auto via = item->Get<HistoryMessageVia>();
|
||||||
|
@ -53,17 +57,8 @@ QSize UnwrappedMedia::countOptimalSize() {
|
||||||
if (forwarded) {
|
if (forwarded) {
|
||||||
forwarded->create(via);
|
forwarded->create(via);
|
||||||
}
|
}
|
||||||
const auto additional = additionalWidth(via, reply, forwarded);
|
maxWidth += additionalWidth(via, reply, forwarded);
|
||||||
maxWidth += additional;
|
|
||||||
accumulate_max(maxWidth, _parent->reactionsOptimalWidth());
|
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()) {
|
if (const auto size = _parent->rightActionSize()) {
|
||||||
minHeight = std::max(
|
minHeight = std::max(
|
||||||
minHeight,
|
minHeight,
|
||||||
|
@ -76,34 +71,53 @@ QSize UnwrappedMedia::countOptimalSize() {
|
||||||
QSize UnwrappedMedia::countCurrentSize(int newWidth) {
|
QSize UnwrappedMedia::countCurrentSize(int newWidth) {
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
accumulate_min(newWidth, maxWidth());
|
accumulate_min(newWidth, maxWidth());
|
||||||
accumulate_max(newWidth, _parent->reactionsOptimalWidth());
|
_contentSize = _content->countCurrentSize(newWidth);
|
||||||
const auto isPageAttach = (_parent->media() != this);
|
auto newHeight = std::max(minHeight(), _contentSize.height());
|
||||||
if (!isPageAttach) {
|
_additionalOnTop = false;
|
||||||
const auto via = item->Get<HistoryMessageVia>();
|
if (_parent->media() != this) {
|
||||||
const auto reply = _parent->displayedReply();
|
return { newWidth, newHeight };
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
auto newHeight = minHeight();
|
if (_parent->hasOutLayout()
|
||||||
if (!isPageAttach
|
|
||||||
&& _parent->hasOutLayout()
|
|
||||||
&& !_parent->delegate()->elementIsChatWide()) {
|
&& !_parent->delegate()->elementIsChatWide()) {
|
||||||
// Add some height to isolated emoji for the timestamp info.
|
// Add some height to isolated emoji for the timestamp info.
|
||||||
const auto infoHeight = st::msgDateImgPadding.y() * 2
|
const auto infoHeight = st::msgDateImgPadding.y() * 2
|
||||||
+ st::msgDateFont->height;
|
+ st::msgDateFont->height;
|
||||||
const auto minimal = st::largeEmojiSize
|
const auto minimal = std::min(
|
||||||
+ 2 * st::largeEmojiOutline
|
st::largeEmojiSize + 2 * st::largeEmojiOutline,
|
||||||
+ (st::msgDateImgDelta + infoHeight);
|
_contentSize.height());
|
||||||
accumulate_max(newHeight, minimal);
|
accumulate_max(newHeight, minimal + st::msgDateImgDelta + infoHeight);
|
||||||
|
}
|
||||||
|
accumulate_max(newWidth, _parent->reactionsOptimalWidth());
|
||||||
|
const auto via = item->Get<HistoryMessageVia>();
|
||||||
|
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 };
|
return { newWidth, newHeight };
|
||||||
}
|
}
|
||||||
|
@ -116,22 +130,16 @@ void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const {
|
||||||
&& !_parent->delegate()->elementIsChatWide();
|
&& !_parent->delegate()->elementIsChatWide();
|
||||||
const auto inWebPage = (_parent->media() != this);
|
const auto inWebPage = (_parent->media() != this);
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
const auto via = inWebPage ? nullptr : item->Get<HistoryMessageVia>();
|
|
||||||
const auto reply = inWebPage ? nullptr : _parent->displayedReply();
|
|
||||||
const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo();
|
|
||||||
auto usex = 0;
|
auto usex = 0;
|
||||||
auto usew = maxWidth();
|
auto usew = _contentSize.width();
|
||||||
if (!inWebPage) {
|
if (!inWebPage && rightAligned) {
|
||||||
usew -= additionalWidth(via, reply, forwarded);
|
usex = width() - usew;
|
||||||
if (rightAligned) {
|
|
||||||
usex = width() - usew;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (rtl()) {
|
if (rtl()) {
|
||||||
usex = width() - usex - usew;
|
usex = width() - usex - usew;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto usey = rightAligned ? 0 : (height() - _contentSize.height());
|
const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height());
|
||||||
const auto useh = rightAligned
|
const auto useh = rightAligned
|
||||||
? std::max(
|
? std::max(
|
||||||
_contentSize.height(),
|
_contentSize.height(),
|
||||||
|
@ -144,6 +152,9 @@ void UnwrappedMedia::draw(Painter &p, const PaintContext &context) const {
|
||||||
|
|
||||||
if (!inWebPage && (context.skipDrawingParts
|
if (!inWebPage && (context.skipDrawingParts
|
||||||
!= PaintContext::SkipDrawingParts::Surrounding)) {
|
!= PaintContext::SkipDrawingParts::Surrounding)) {
|
||||||
|
const auto via = inWebPage ? nullptr : item->Get<HistoryMessageVia>();
|
||||||
|
const auto reply = inWebPage ? nullptr : _parent->displayedReply();
|
||||||
|
const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo();
|
||||||
drawSurrounding(p, inner, context, via, reply, forwarded);
|
drawSurrounding(p, inner, context, via, reply, forwarded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,10 +212,14 @@ void UnwrappedMedia::drawSurrounding(
|
||||||
InfoDisplayType::Background);
|
InfoDisplayType::Background);
|
||||||
}
|
}
|
||||||
auto replyRight = 0;
|
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)) {
|
if (const auto surrounding = surroundingInfo(via, reply, forwarded, rectw)) {
|
||||||
auto recth = surrounding.height;
|
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;
|
int recty = 0;
|
||||||
if (rtl()) rectx = width() - rectx - rectw;
|
if (rtl()) rectx = width() - rectx - rectw;
|
||||||
|
|
||||||
|
@ -254,16 +269,10 @@ PointState UnwrappedMedia::pointState(QPoint point) const {
|
||||||
&& !_parent->delegate()->elementIsChatWide();
|
&& !_parent->delegate()->elementIsChatWide();
|
||||||
const auto inWebPage = (_parent->media() != this);
|
const auto inWebPage = (_parent->media() != this);
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
const auto via = inWebPage ? nullptr : item->Get<HistoryMessageVia>();
|
|
||||||
const auto reply = inWebPage ? nullptr : _parent->displayedReply();
|
|
||||||
const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo();
|
|
||||||
auto usex = 0;
|
auto usex = 0;
|
||||||
auto usew = maxWidth();
|
auto usew = _contentSize.width();
|
||||||
if (!inWebPage) {
|
if (!inWebPage && rightAligned) {
|
||||||
usew -= additionalWidth(via, reply, forwarded);
|
usex = width() - usew;
|
||||||
if (rightAligned) {
|
|
||||||
usex = width() - usew;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (rtl()) {
|
if (rtl()) {
|
||||||
usex = width() - usex - usew;
|
usex = width() - usex - usew;
|
||||||
|
@ -271,7 +280,7 @@ PointState UnwrappedMedia::pointState(QPoint point) const {
|
||||||
|
|
||||||
const auto datey = height() - st::msgDateImgPadding.y() * 2
|
const auto datey = height() - st::msgDateImgPadding.y() * 2
|
||||||
- st::msgDateFont->height;
|
- st::msgDateFont->height;
|
||||||
const auto usey = rightAligned ? 0 : (height() - _contentSize.height());
|
const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height());
|
||||||
const auto useh = rightAligned
|
const auto useh = rightAligned
|
||||||
? std::max(_contentSize.height(), datey)
|
? std::max(_contentSize.height(), datey)
|
||||||
: _contentSize.height();
|
: _contentSize.height();
|
||||||
|
@ -295,22 +304,16 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const {
|
||||||
&& !_parent->delegate()->elementIsChatWide();
|
&& !_parent->delegate()->elementIsChatWide();
|
||||||
const auto inWebPage = (_parent->media() != this);
|
const auto inWebPage = (_parent->media() != this);
|
||||||
const auto item = _parent->data();
|
const auto item = _parent->data();
|
||||||
const auto via = inWebPage ? nullptr : item->Get<HistoryMessageVia>();
|
|
||||||
const auto reply = inWebPage ? nullptr : _parent->displayedReply();
|
|
||||||
const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo();
|
|
||||||
auto usex = 0;
|
auto usex = 0;
|
||||||
auto usew = maxWidth();
|
auto usew = _contentSize.width();
|
||||||
if (!inWebPage) {
|
if (!inWebPage && rightAligned) {
|
||||||
usew -= additionalWidth(via, reply, forwarded);
|
usex = width() - usew;
|
||||||
if (rightAligned) {
|
|
||||||
usex = width() - usew;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (rtl()) {
|
if (rtl()) {
|
||||||
usex = width() - usex - usew;
|
usex = width() - usex - usew;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto usey = rightAligned ? 0 : (height() - _contentSize.height());
|
const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height());
|
||||||
const auto useh = rightAligned
|
const auto useh = rightAligned
|
||||||
? std::max(
|
? std::max(
|
||||||
_contentSize.height(),
|
_contentSize.height(),
|
||||||
|
@ -319,11 +322,18 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const {
|
||||||
const auto inner = QRect(usex, usey, usew, useh);
|
const auto inner = QRect(usex, usey, usew, useh);
|
||||||
|
|
||||||
if (_parent->media() == this) {
|
if (_parent->media() == this) {
|
||||||
|
const auto via = inWebPage ? nullptr : item->Get<HistoryMessageVia>();
|
||||||
|
const auto reply = inWebPage ? nullptr : _parent->displayedReply();
|
||||||
|
const auto forwarded = inWebPage ? nullptr : getDisplayedForwardedInfo();
|
||||||
auto replyRight = 0;
|
auto replyRight = 0;
|
||||||
auto rectw = width() - inner.width() - st::msgReplyPadding.left();
|
auto rectw = _additionalOnTop
|
||||||
if (auto surrounding = surroundingInfo(via, reply, forwarded, rectw)) {
|
? 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;
|
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;
|
int recty = 0;
|
||||||
if (rtl()) rectx = width() - rectx - rectw;
|
if (rtl()) rectx = width() - rectx - rectw;
|
||||||
|
|
||||||
|
@ -420,10 +430,7 @@ QRect UnwrappedMedia::contentRectForReactions() const {
|
||||||
const auto reply = _parent->displayedReply();
|
const auto reply = _parent->displayedReply();
|
||||||
const auto forwarded = getDisplayedForwardedInfo();
|
const auto forwarded = getDisplayedForwardedInfo();
|
||||||
auto usex = 0;
|
auto usex = 0;
|
||||||
auto usew = maxWidth();
|
auto usew = _contentSize.width();
|
||||||
if (!inWebPage) {
|
|
||||||
usew -= additionalWidth(via, reply, forwarded);
|
|
||||||
}
|
|
||||||
accumulate_max(usew, _parent->reactionsOptimalWidth());
|
accumulate_max(usew, _parent->reactionsOptimalWidth());
|
||||||
if (rightAligned) {
|
if (rightAligned) {
|
||||||
usex = width() - usew;
|
usex = width() - usew;
|
||||||
|
@ -431,7 +438,7 @@ QRect UnwrappedMedia::contentRectForReactions() const {
|
||||||
if (rtl()) {
|
if (rtl()) {
|
||||||
usex = width() - usex - usew;
|
usex = width() - usex - usew;
|
||||||
}
|
}
|
||||||
const auto usey = rightAligned ? 0 : (height() - _contentSize.height());
|
const auto usey = rightAligned ? _topAdded : (height() - _contentSize.height());
|
||||||
const auto useh = rightAligned
|
const auto useh = rightAligned
|
||||||
? std::max(
|
? std::max(
|
||||||
_contentSize.height(),
|
_contentSize.height(),
|
||||||
|
|
|
@ -21,7 +21,8 @@ class UnwrappedMedia final : public Media {
|
||||||
public:
|
public:
|
||||||
class Content {
|
class Content {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] virtual QSize size() = 0;
|
[[nodiscard]] virtual QSize countOptimalSize() = 0;
|
||||||
|
[[nodiscard]] virtual QSize countCurrentSize(int newWidth);
|
||||||
|
|
||||||
virtual void draw(
|
virtual void draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
|
@ -161,6 +162,8 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<Content> _content;
|
std::unique_ptr<Content> _content;
|
||||||
QSize _contentSize;
|
QSize _contentSize;
|
||||||
|
int _topAdded = 0;
|
||||||
|
bool _additionalOnTop = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,8 @@ bool SlotMachine::isEndResolved() const {
|
||||||
return _end[0].has_value() || (_dice->value() != kWinValue);
|
return _end[0].has_value() || (_dice->value() != kWinValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize SlotMachine::size() {
|
QSize SlotMachine::countOptimalSize() {
|
||||||
return _pull ? _pull->size() : Sticker::EmojiSize();
|
return _pull ? _pull->countOptimalSize() : Sticker::EmojiSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClickHandlerPtr SlotMachine::link() {
|
ClickHandlerPtr SlotMachine::link() {
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
SlotMachine(not_null<Element*> parent, not_null<Data::MediaDice*> dice);
|
SlotMachine(not_null<Element*> parent, not_null<Data::MediaDice*> dice);
|
||||||
~SlotMachine();
|
~SlotMachine();
|
||||||
|
|
||||||
QSize size() override;
|
QSize countOptimalSize() override;
|
||||||
void draw(
|
void draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
|
|
|
@ -68,6 +68,10 @@ Sticker::Sticker(
|
||||||
: _parent(parent)
|
: _parent(parent)
|
||||||
, _data(data)
|
, _data(data)
|
||||||
, _replacements(replacements)
|
, _replacements(replacements)
|
||||||
|
, _cachingTag(ChatHelpers::StickerLottieSize::MessageHistory)
|
||||||
|
, _lottieOncePlayed(false)
|
||||||
|
, _premiumEffectPlayed(false)
|
||||||
|
, _nextLastDiceFrame(false)
|
||||||
, _skipPremiumEffect(skipPremiumEffect) {
|
, _skipPremiumEffect(skipPremiumEffect) {
|
||||||
if ((_dataMedia = _data->activeMediaView())) {
|
if ((_dataMedia = _data->activeMediaView())) {
|
||||||
dataMediaCreated();
|
dataMediaCreated();
|
||||||
|
@ -106,6 +110,10 @@ bool Sticker::hasPremiumEffect() const {
|
||||||
return !_skipPremiumEffect && _data->isPremiumSticker();
|
return !_skipPremiumEffect && _data->isPremiumSticker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Sticker::customEmojiPart() const {
|
||||||
|
return (_cachingTag != ChatHelpers::StickerLottieSize::MessageHistory);
|
||||||
|
}
|
||||||
|
|
||||||
bool Sticker::isEmojiSticker() const {
|
bool Sticker::isEmojiSticker() const {
|
||||||
return (_parent->data()->media() == nullptr);
|
return (_parent->data()->media() == nullptr);
|
||||||
}
|
}
|
||||||
|
@ -126,11 +134,11 @@ void Sticker::initSize() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize Sticker::size() {
|
QSize Sticker::countOptimalSize() {
|
||||||
if (_size.isEmpty()) {
|
if (_size.isEmpty()) {
|
||||||
initSize();
|
initSize();
|
||||||
}
|
}
|
||||||
return _size;
|
return DownscaledSize(_size, Size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sticker::readyToDrawLottie() {
|
bool Sticker::readyToDrawLottie() {
|
||||||
|
@ -184,6 +192,10 @@ void Sticker::draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
const QRect &r) {
|
const QRect &r) {
|
||||||
|
if (!customEmojiPart() && isEmojiSticker()) {
|
||||||
|
_parent->clearCustomEmojiRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
if (readyToDrawLottie()) {
|
if (readyToDrawLottie()) {
|
||||||
paintLottie(p, context, r);
|
paintLottie(p, context, r);
|
||||||
|
@ -257,7 +269,7 @@ void Sticker::paintLottie(
|
||||||
? true
|
? true
|
||||||
: (_diceIndex == 0)
|
: (_diceIndex == 0)
|
||||||
? false
|
? false
|
||||||
: (isEmojiSticker()
|
: ((!customEmojiPart() && isEmojiSticker())
|
||||||
|| !Core::App().settings().loopAnimatedStickers());
|
|| !Core::App().settings().loopAnimatedStickers());
|
||||||
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
|
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
|
||||||
const auto switchToNext = /*(_externalInfo.frame >= 0)
|
const auto switchToNext = /*(_externalInfo.frame >= 0)
|
||||||
|
@ -445,14 +457,21 @@ void Sticker::setDiceIndex(const QString &emoji, int index) {
|
||||||
_diceIndex = index;
|
_diceIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sticker::setCustomEmojiPart(
|
||||||
|
int size,
|
||||||
|
ChatHelpers::StickerLottieSize tag) {
|
||||||
|
_size = { size, size };
|
||||||
|
_cachingTag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
void Sticker::setupLottie() {
|
void Sticker::setupLottie() {
|
||||||
Expects(_dataMedia != nullptr);
|
Expects(_dataMedia != nullptr);
|
||||||
|
|
||||||
_lottie = ChatHelpers::LottiePlayerFromDocument(
|
_lottie = ChatHelpers::LottiePlayerFromDocument(
|
||||||
_dataMedia.get(),
|
_dataMedia.get(),
|
||||||
_replacements,
|
_replacements,
|
||||||
ChatHelpers::StickerLottieSize::MessageHistory,
|
_cachingTag,
|
||||||
size() * style::DevicePixelRatio(),
|
countOptimalSize() * style::DevicePixelRatio(),
|
||||||
Lottie::Quality::High);
|
Lottie::Quality::High);
|
||||||
checkPremiumEffectStart();
|
checkPremiumEffectStart();
|
||||||
lottieCreated();
|
lottieCreated();
|
||||||
|
@ -473,10 +492,10 @@ void Sticker::lottieCreated() {
|
||||||
_lottie->updates(
|
_lottie->updates(
|
||||||
) | rpl::start_with_next([=](Lottie::Update update) {
|
) | rpl::start_with_next([=](Lottie::Update update) {
|
||||||
v::match(update.data, [&](const Lottie::Information &information) {
|
v::match(update.data, [&](const Lottie::Information &information) {
|
||||||
_parent->history()->owner().requestViewResize(_parent);
|
_parent->customEmojiRepaint();
|
||||||
//markFramesTillExternal();
|
//markFramesTillExternal();
|
||||||
}, [&](const Lottie::DisplayFrameRequest &request) {
|
}, [&](const Lottie::DisplayFrameRequest &request) {
|
||||||
_parent->history()->owner().requestViewRepaint(_parent);
|
_parent->customEmojiRepaint();
|
||||||
});
|
});
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,10 @@ class SinglePlayer;
|
||||||
struct ColorReplacements;
|
struct ColorReplacements;
|
||||||
} // namespace Lottie
|
} // namespace Lottie
|
||||||
|
|
||||||
|
namespace ChatHelpers {
|
||||||
|
enum class StickerLottieSize : uint8;
|
||||||
|
} // namespace ChatHelpers
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class Sticker final
|
class Sticker final
|
||||||
|
@ -39,7 +43,7 @@ public:
|
||||||
~Sticker();
|
~Sticker();
|
||||||
|
|
||||||
void initSize();
|
void initSize();
|
||||||
QSize size() override;
|
QSize countOptimalSize() override;
|
||||||
void draw(
|
void draw(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
|
@ -65,6 +69,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDiceIndex(const QString &emoji, int index);
|
void setDiceIndex(const QString &emoji, int index);
|
||||||
|
void setCustomEmojiPart(int size, ChatHelpers::StickerLottieSize tag);
|
||||||
[[nodiscard]] bool atTheEnd() const {
|
[[nodiscard]] bool atTheEnd() const {
|
||||||
return (_frameIndex >= 0) && (_frameIndex + 1 == _framesCount);
|
return (_frameIndex >= 0) && (_frameIndex + 1 == _framesCount);
|
||||||
}
|
}
|
||||||
|
@ -92,6 +97,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] bool hasPremiumEffect() const;
|
[[nodiscard]] bool hasPremiumEffect() const;
|
||||||
|
[[nodiscard]] bool customEmojiPart() const;
|
||||||
[[nodiscard]] bool isEmojiSticker() const;
|
[[nodiscard]] bool isEmojiSticker() const;
|
||||||
void paintLottie(Painter &p, const PaintContext &context, const QRect &r);
|
void paintLottie(Painter &p, const PaintContext &context, const QRect &r);
|
||||||
bool paintPixmap(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;
|
int _diceIndex = -1;
|
||||||
mutable int _frameIndex = -1;
|
mutable int _frameIndex = -1;
|
||||||
mutable int _framesCount = -1;
|
mutable int _framesCount = -1;
|
||||||
mutable bool _lottieOncePlayed = false;
|
ChatHelpers::StickerLottieSize _cachingTag = {};
|
||||||
mutable bool _premiumEffectPlayed = false;
|
mutable bool _lottieOncePlayed : 1;
|
||||||
mutable bool _nextLastDiceFrame = false;
|
mutable bool _premiumEffectPlayed : 1;
|
||||||
bool _skipPremiumEffect = false;
|
mutable bool _nextLastDiceFrame : 1;
|
||||||
|
bool _skipPremiumEffect : 1;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d69b49fdd7bcb0b3414bc66fb34606dd56f695ba
|
Subproject commit 500731e1f9a4a8b98e388e7a06b91b41d8df7211
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4768e7ee03aa22f64f73dc13016d5bd94a047496
|
Subproject commit f27d756bcd7508250a3f7ff73bf6053308c18cd6
|
Loading…
Reference in New Issue