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