mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Optimize small emoji image caching and painting.
This commit is contained in:
parent
4050866b3b
commit
6f8c9f65cf
7 changed files with 172 additions and 263 deletions
|
@ -13,8 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
|
#include "data/data_document_media.h"
|
||||||
#include "base/timer_rpl.h"
|
#include "base/timer_rpl.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -55,6 +58,105 @@ rpl::producer<> Reactions::updates() const {
|
||||||
return _updated.events();
|
return _updated.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reactions::preloadImageFor(const QString &emoji) {
|
||||||
|
if (_images.contains(emoji)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &set = _images.emplace(emoji).first->second;
|
||||||
|
const auto i = ranges::find(_available, emoji, &Reaction::emoji);
|
||||||
|
const auto document = (i != end(_available))
|
||||||
|
? i->staticIcon.get()
|
||||||
|
: nullptr;
|
||||||
|
if (document) {
|
||||||
|
loadImage(set, document);
|
||||||
|
} else if (!_waitingForList) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Reactions::resolveImageFor(
|
||||||
|
const QString &emoji,
|
||||||
|
ImageSize size) {
|
||||||
|
const auto i = _images.find(emoji);
|
||||||
|
if (i == end(_images)) {
|
||||||
|
preloadImageFor(emoji);
|
||||||
|
}
|
||||||
|
auto &set = (i != end(_images)) ? i->second : _images[emoji];
|
||||||
|
switch (size) {
|
||||||
|
case ImageSize::BottomInfo: return set.bottomInfo;
|
||||||
|
case ImageSize::InlineList: return set.inlineList;
|
||||||
|
}
|
||||||
|
Unexpected("ImageSize in Reactions::resolveImageFor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::resolveImages() {
|
||||||
|
for (auto &[emoji, set] : _images) {
|
||||||
|
if (!set.bottomInfo.isNull() || set.media) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto i = ranges::find(_available, emoji, &Reaction::emoji);
|
||||||
|
const auto document = (i != end(list()))
|
||||||
|
? i->staticIcon.get()
|
||||||
|
: nullptr;
|
||||||
|
if (document) {
|
||||||
|
loadImage(set, document);
|
||||||
|
} else {
|
||||||
|
LOG(("API Error: Reaction for emoji '%1' not found!"
|
||||||
|
).arg(emoji));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::loadImage(
|
||||||
|
ImageSet &set,
|
||||||
|
not_null<DocumentData*> document) {
|
||||||
|
if (!set.bottomInfo.isNull()) {
|
||||||
|
return;
|
||||||
|
} else if (!set.media) {
|
||||||
|
set.media = document->createMediaView();
|
||||||
|
}
|
||||||
|
if (const auto image = set.media->getStickerLarge()) {
|
||||||
|
setImage(set, image->original());
|
||||||
|
} else if (!_imagesLoadLifetime) {
|
||||||
|
document->session().downloaderTaskFinished(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
downloadTaskFinished();
|
||||||
|
}, _imagesLoadLifetime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::setImage(ImageSet &set, QImage large) {
|
||||||
|
set.media = nullptr;
|
||||||
|
const auto scale = [&](int size) {
|
||||||
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
return Images::prepare(
|
||||||
|
large,
|
||||||
|
size * factor,
|
||||||
|
size * factor,
|
||||||
|
Images::Option::Smooth,
|
||||||
|
size,
|
||||||
|
size);
|
||||||
|
};
|
||||||
|
set.bottomInfo = scale(st::reactionInfoSize);
|
||||||
|
set.inlineList = scale(st::reactionBottomSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reactions::downloadTaskFinished() {
|
||||||
|
auto hasOne = false;
|
||||||
|
for (auto &[emoji, set] : _images) {
|
||||||
|
if (!set.media) {
|
||||||
|
continue;
|
||||||
|
} else if (const auto image = set.media->getStickerLarge()) {
|
||||||
|
setImage(set, image->original());
|
||||||
|
} else {
|
||||||
|
hasOne = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasOne) {
|
||||||
|
_imagesLoadLifetime.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Reaction> Reactions::Filtered(
|
std::vector<Reaction> Reactions::Filtered(
|
||||||
const std::vector<Reaction> &reactions,
|
const std::vector<Reaction> &reactions,
|
||||||
const std::vector<QString> &emoji) {
|
const std::vector<QString> &emoji) {
|
||||||
|
@ -101,6 +203,10 @@ void Reactions::request() {
|
||||||
_available.push_back(*parsed);
|
_available.push_back(*parsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_waitingForList) {
|
||||||
|
_waitingForList = false;
|
||||||
|
resolveImages();
|
||||||
|
}
|
||||||
_updated.fire({});
|
_updated.fire({});
|
||||||
}, [&](const MTPDmessages_availableReactionsNotModified &) {
|
}, [&](const MTPDmessages_availableReactionsNotModified &) {
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
|
class DocumentMedia;
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
struct Reaction {
|
struct Reaction {
|
||||||
|
@ -40,12 +41,32 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> updates() const;
|
[[nodiscard]] rpl::producer<> updates() const;
|
||||||
|
|
||||||
|
enum class ImageSize {
|
||||||
|
BottomInfo,
|
||||||
|
InlineList,
|
||||||
|
};
|
||||||
|
void preloadImageFor(const QString &emoji);
|
||||||
|
[[nodiscard]] QImage resolveImageFor(
|
||||||
|
const QString &emoji,
|
||||||
|
ImageSize size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct ImageSet {
|
||||||
|
QImage bottomInfo;
|
||||||
|
QImage inlineList;
|
||||||
|
std::shared_ptr<DocumentMedia> media;
|
||||||
|
};
|
||||||
|
|
||||||
void request();
|
void request();
|
||||||
|
|
||||||
[[nodiscard]] std::optional<Reaction> parse(
|
[[nodiscard]] std::optional<Reaction> parse(
|
||||||
const MTPAvailableReaction &entry);
|
const MTPAvailableReaction &entry);
|
||||||
|
|
||||||
|
void loadImage(ImageSet &set, not_null<DocumentData*> document);
|
||||||
|
void setImage(ImageSet &set, QImage large);
|
||||||
|
void resolveImages();
|
||||||
|
void downloadTaskFinished();
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
|
|
||||||
std::vector<Reaction> _available;
|
std::vector<Reaction> _available;
|
||||||
|
@ -54,6 +75,10 @@ private:
|
||||||
mtpRequestId _requestId = 0;
|
mtpRequestId _requestId = 0;
|
||||||
int32 _hash = 0;
|
int32 _hash = 0;
|
||||||
|
|
||||||
|
base::flat_map<QString, ImageSet> _images;
|
||||||
|
rpl::lifetime _imagesLoadLifetime;
|
||||||
|
bool _waitingForList = false;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,17 +18,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "data/data_message_reactions.h"
|
#include "data/data_message_reactions.h"
|
||||||
#include "data/data_session.h"
|
|
||||||
#include "data/data_document.h"
|
|
||||||
#include "data/data_document_media.h"
|
|
||||||
#include "main/main_session.h"
|
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
BottomInfo::BottomInfo(Data &&data)
|
BottomInfo::BottomInfo(
|
||||||
: _data(std::move(data)) {
|
not_null<::Data::Reactions*> reactionsOwner,
|
||||||
|
Data &&data)
|
||||||
|
: _reactionsOwner(reactionsOwner)
|
||||||
|
, _data(std::move(data)) {
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +226,11 @@ void BottomInfo::paintReactions(
|
||||||
y += st::msgDateFont->height;
|
y += st::msgDateFont->height;
|
||||||
widthLeft = availableWidth;
|
widthLeft = availableWidth;
|
||||||
}
|
}
|
||||||
|
if (reaction.image.isNull()) {
|
||||||
|
reaction.image = _reactionsOwner->resolveImageFor(
|
||||||
|
reaction.emoji,
|
||||||
|
::Data::Reactions::ImageSize::BottomInfo);
|
||||||
|
}
|
||||||
if (!reaction.image.isNull()) {
|
if (!reaction.image.isNull()) {
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
x,
|
x,
|
||||||
|
@ -362,56 +366,10 @@ QSize BottomInfo::countOptimalSize() {
|
||||||
BottomInfo::Reaction BottomInfo::prepareReactionWithEmoji(
|
BottomInfo::Reaction BottomInfo::prepareReactionWithEmoji(
|
||||||
const QString &emoji) {
|
const QString &emoji) {
|
||||||
auto result = Reaction{ .emoji = emoji };
|
auto result = Reaction{ .emoji = emoji };
|
||||||
auto &reactions = _data.owner->reactions();
|
_reactionsOwner->preloadImageFor(emoji);
|
||||||
const auto &list = reactions.list();
|
|
||||||
const auto i = ranges::find(
|
|
||||||
list,
|
|
||||||
emoji,
|
|
||||||
&::Data::Reaction::emoji);
|
|
||||||
const auto document = (i != end(list))
|
|
||||||
? i->staticIcon.get()
|
|
||||||
: nullptr;
|
|
||||||
if (document) {
|
|
||||||
loadReactionImage(result, document);
|
|
||||||
} else if (!_waitingForReactionsList) {
|
|
||||||
reactions.refresh();
|
|
||||||
reactions.updates(
|
|
||||||
) | rpl::filter([=] {
|
|
||||||
return _waitingForReactionsList;
|
|
||||||
}) | rpl::start_with_next([=] {
|
|
||||||
reactionsListLoaded();
|
|
||||||
}, _assetsLoadLifetime);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BottomInfo::reactionsListLoaded() {
|
|
||||||
_waitingForReactionsList = false;
|
|
||||||
if (assetsLoaded()) {
|
|
||||||
_assetsLoadLifetime.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &list = _data.owner->reactions().list();
|
|
||||||
for (auto &reaction : _reactions) {
|
|
||||||
if (!reaction.image.isNull() || reaction.media) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto i = ranges::find(
|
|
||||||
list,
|
|
||||||
reaction.emoji,
|
|
||||||
&::Data::Reaction::emoji);
|
|
||||||
const auto document = (i != end(list))
|
|
||||||
? i->staticIcon.get()
|
|
||||||
: nullptr;
|
|
||||||
if (document) {
|
|
||||||
loadReactionImage(reaction, document);
|
|
||||||
} else {
|
|
||||||
LOG(("API Error: Reaction for emoji '%1' not found!"
|
|
||||||
).arg(reaction.emoji));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BottomInfo::setReactionCount(Reaction &reaction, int count) {
|
void BottomInfo::setReactionCount(Reaction &reaction, int count) {
|
||||||
if (reaction.count == count) {
|
if (reaction.count == count) {
|
||||||
return;
|
return;
|
||||||
|
@ -425,72 +383,14 @@ void BottomInfo::setReactionCount(Reaction &reaction, int count) {
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BottomInfo::loadReactionImage(
|
|
||||||
Reaction &reaction,
|
|
||||||
not_null<DocumentData*> document) {
|
|
||||||
if (!reaction.image.isNull()) {
|
|
||||||
return;
|
|
||||||
} else if (!reaction.media) {
|
|
||||||
reaction.media = document->createMediaView();
|
|
||||||
}
|
|
||||||
if (const auto image = reaction.media->getStickerLarge()) {
|
|
||||||
setReactionImage(reaction, image->original());
|
|
||||||
} else if (!_waitingForDownloadTask) {
|
|
||||||
_waitingForDownloadTask = true;
|
|
||||||
document->session().downloaderTaskFinished(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
downloadTaskFinished();
|
|
||||||
}, _assetsLoadLifetime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BottomInfo::setReactionImage(Reaction &reaction, QImage large) {
|
|
||||||
reaction.media = nullptr;
|
|
||||||
const auto size = st::reactionInfoSize;
|
|
||||||
const auto factor = style::DevicePixelRatio();
|
|
||||||
reaction.image = Images::prepare(
|
|
||||||
std::move(large),
|
|
||||||
size * factor,
|
|
||||||
size * factor,
|
|
||||||
Images::Option::Smooth,
|
|
||||||
size,
|
|
||||||
size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BottomInfo::downloadTaskFinished() {
|
|
||||||
auto hasOne = false;
|
|
||||||
for (auto &reaction : _reactions) {
|
|
||||||
if (!reaction.media) {
|
|
||||||
continue;
|
|
||||||
} else if (const auto image = reaction.media->getStickerLarge()) {
|
|
||||||
setReactionImage(reaction, image->original());
|
|
||||||
} else {
|
|
||||||
hasOne = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasOne) {
|
|
||||||
_waitingForDownloadTask = false;
|
|
||||||
if (assetsLoaded()) {
|
|
||||||
_assetsLoadLifetime.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BottomInfo::assetsLoaded() const {
|
|
||||||
return !_waitingForReactionsList && !_waitingForDownloadTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
|
BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
|
||||||
using Flag = BottomInfo::Data::Flag;
|
using Flag = BottomInfo::Data::Flag;
|
||||||
|
|
||||||
const auto owner = &message->data()->history()->owner();
|
|
||||||
auto result = BottomInfo::Data{ .owner = owner };
|
|
||||||
|
|
||||||
const auto item = message->message();
|
const auto item = message->message();
|
||||||
|
|
||||||
|
auto result = BottomInfo::Data();
|
||||||
result.date = message->dateTime();
|
result.date = message->dateTime();
|
||||||
if (message->embedReactionsInBottomInfo()) {
|
if (message->embedReactionsInBottomInfo()) {
|
||||||
result.reactions = item->reactions();
|
result.reactions = item->reactions();
|
||||||
result.chosenReaction = item->chosenReaction();
|
|
||||||
}
|
}
|
||||||
if (message->hasOutLayout()) {
|
if (message->hasOutLayout()) {
|
||||||
result.flags |= Flag::OutLayout;
|
result.flags |= Flag::OutLayout;
|
||||||
|
|
|
@ -16,8 +16,7 @@ struct ChatPaintContext;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Session;
|
class Reactions;
|
||||||
class DocumentMedia;
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -41,16 +40,14 @@ public:
|
||||||
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
|
|
||||||
not_null<::Data::Session*> owner;
|
|
||||||
QDateTime date;
|
QDateTime date;
|
||||||
QString author;
|
QString author;
|
||||||
base::flat_map<QString, int> reactions;
|
base::flat_map<QString, int> reactions;
|
||||||
QString chosenReaction;
|
|
||||||
std::optional<int> views;
|
std::optional<int> views;
|
||||||
std::optional<int> replies;
|
std::optional<int> replies;
|
||||||
Flags flags;
|
Flags flags;
|
||||||
};
|
};
|
||||||
explicit BottomInfo(Data &&data);
|
BottomInfo(not_null<::Data::Reactions*> reactionsOwner, Data &&data);
|
||||||
|
|
||||||
void update(Data &&data, int availableWidth);
|
void update(Data &&data, int availableWidth);
|
||||||
|
|
||||||
|
@ -70,9 +67,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Reaction {
|
struct Reaction {
|
||||||
QImage image;
|
mutable QImage image;
|
||||||
QString emoji;
|
QString emoji;
|
||||||
std::shared_ptr<::Data::DocumentMedia> media;
|
|
||||||
QString countText;
|
QString countText;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int countTextWidth = 0;
|
int countTextWidth = 0;
|
||||||
|
@ -96,14 +92,9 @@ private:
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
void setReactionCount(Reaction &reaction, int count);
|
void setReactionCount(Reaction &reaction, int count);
|
||||||
void loadReactionImage(Reaction &reaction, not_null<DocumentData*> document);
|
|
||||||
void setReactionImage(Reaction &reaction, QImage large);
|
|
||||||
[[nodiscard]] Reaction prepareReactionWithEmoji(const QString &emoji);
|
[[nodiscard]] Reaction prepareReactionWithEmoji(const QString &emoji);
|
||||||
|
|
||||||
void reactionsListLoaded();
|
const not_null<::Data::Reactions*> _reactionsOwner;
|
||||||
void downloadTaskFinished();
|
|
||||||
[[nodiscard]] bool assetsLoaded() const;
|
|
||||||
|
|
||||||
Data _data;
|
Data _data;
|
||||||
Ui::Text::String _authorEditedDate;
|
Ui::Text::String _authorEditedDate;
|
||||||
Ui::Text::String _views;
|
Ui::Text::String _views;
|
||||||
|
@ -111,11 +102,6 @@ private:
|
||||||
std::vector<Reaction> _reactions;
|
std::vector<Reaction> _reactions;
|
||||||
int _reactionsMaxWidth = 0;
|
int _reactionsMaxWidth = 0;
|
||||||
int _dateWidth = 0;
|
int _dateWidth = 0;
|
||||||
|
|
||||||
rpl::lifetime _assetsLoadLifetime;
|
|
||||||
bool _waitingForReactionsList = false;
|
|
||||||
bool _waitingForDownloadTask = false;
|
|
||||||
|
|
||||||
bool _authorElided = false;
|
bool _authorElided = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -245,7 +245,9 @@ Message::Message(
|
||||||
not_null<HistoryMessage*> data,
|
not_null<HistoryMessage*> data,
|
||||||
Element *replacing)
|
Element *replacing)
|
||||||
: Element(delegate, data, replacing)
|
: Element(delegate, data, replacing)
|
||||||
, _bottomInfo(BottomInfoDataFromMessage(this)) {
|
, _bottomInfo(
|
||||||
|
&data->history()->owner().reactions(),
|
||||||
|
BottomInfoDataFromMessage(this)) {
|
||||||
initLogEntryOriginal();
|
initLogEntryOriginal();
|
||||||
initPsa();
|
initPsa();
|
||||||
refreshReactions();
|
refreshReactions();
|
||||||
|
@ -1924,7 +1926,9 @@ void Message::refreshReactions() {
|
||||||
using namespace Reactions;
|
using namespace Reactions;
|
||||||
auto data = InlineListDataFromMessage(this);
|
auto data = InlineListDataFromMessage(this);
|
||||||
if (!_reactions) {
|
if (!_reactions) {
|
||||||
_reactions = std::make_unique<InlineList>(std::move(data));
|
_reactions = std::make_unique<InlineList>(
|
||||||
|
&item->history()->owner().reactions(),
|
||||||
|
std::move(data));
|
||||||
} else {
|
} else {
|
||||||
_reactions->update(std::move(data), width());
|
_reactions->update(std::move(data), width());
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
#include "data/data_document.h"
|
|
||||||
#include "data/data_document_media.h"
|
|
||||||
#include "data/data_message_reactions.h"
|
#include "data/data_message_reactions.h"
|
||||||
#include "data/data_session.h"
|
|
||||||
#include "main/main_session.h"
|
|
||||||
#include "lang/lang_tag.h"
|
#include "lang/lang_tag.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
@ -27,8 +23,9 @@ constexpr auto kOutNonChosenOpacity = 0.18;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
InlineList::InlineList(Data &&data)
|
InlineList::InlineList(not_null<::Data::Reactions*> owner, Data &&data)
|
||||||
: _data(std::move(data)) {
|
: _owner(owner)
|
||||||
|
, _data(std::move(data)) {
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,56 +76,10 @@ void InlineList::layoutButtons() {
|
||||||
|
|
||||||
InlineList::Button InlineList::prepareButtonWithEmoji(const QString &emoji) {
|
InlineList::Button InlineList::prepareButtonWithEmoji(const QString &emoji) {
|
||||||
auto result = Button{ .emoji = emoji };
|
auto result = Button{ .emoji = emoji };
|
||||||
auto &reactions = _data.owner->reactions();
|
_owner->preloadImageFor(emoji);
|
||||||
const auto &list = reactions.list();
|
|
||||||
const auto i = ranges::find(
|
|
||||||
list,
|
|
||||||
emoji,
|
|
||||||
&::Data::Reaction::emoji);
|
|
||||||
const auto document = (i != end(list))
|
|
||||||
? i->staticIcon.get()
|
|
||||||
: nullptr;
|
|
||||||
if (document) {
|
|
||||||
loadButtonImage(result, document);
|
|
||||||
} else if (!_waitingForReactionsList) {
|
|
||||||
reactions.refresh();
|
|
||||||
reactions.updates(
|
|
||||||
) | rpl::filter([=] {
|
|
||||||
return _waitingForReactionsList;
|
|
||||||
}) | rpl::start_with_next([=] {
|
|
||||||
reactionsListLoaded();
|
|
||||||
}, _assetsLoadLifetime);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InlineList::reactionsListLoaded() {
|
|
||||||
_waitingForReactionsList = false;
|
|
||||||
if (assetsLoaded()) {
|
|
||||||
_assetsLoadLifetime.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &list = _data.owner->reactions().list();
|
|
||||||
for (auto &button : _buttons) {
|
|
||||||
if (!button.image.isNull() || button.media) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto i = ranges::find(
|
|
||||||
list,
|
|
||||||
button.emoji,
|
|
||||||
&::Data::Reaction::emoji);
|
|
||||||
const auto document = (i != end(list))
|
|
||||||
? i->staticIcon.get()
|
|
||||||
: nullptr;
|
|
||||||
if (document) {
|
|
||||||
loadButtonImage(button, document);
|
|
||||||
} else {
|
|
||||||
LOG(("API Error: Reaction for emoji '%1' not found!"
|
|
||||||
).arg(button.emoji));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InlineList::setButtonCount(Button &button, int count) {
|
void InlineList::setButtonCount(Button &button, int count) {
|
||||||
if (button.count == count) {
|
if (button.count == count) {
|
||||||
return;
|
return;
|
||||||
|
@ -138,61 +89,6 @@ void InlineList::setButtonCount(Button &button, int count) {
|
||||||
button.countTextWidth = st::semiboldFont->width(button.countText);
|
button.countTextWidth = st::semiboldFont->width(button.countText);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InlineList::loadButtonImage(
|
|
||||||
Button &button,
|
|
||||||
not_null<DocumentData*> document) {
|
|
||||||
if (!button.image.isNull()) {
|
|
||||||
return;
|
|
||||||
} else if (!button.media) {
|
|
||||||
button.media = document->createMediaView();
|
|
||||||
}
|
|
||||||
if (const auto image = button.media->getStickerLarge()) {
|
|
||||||
setButtonImage(button, image->original());
|
|
||||||
} else if (!_waitingForDownloadTask) {
|
|
||||||
_waitingForDownloadTask = true;
|
|
||||||
document->session().downloaderTaskFinished(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
downloadTaskFinished();
|
|
||||||
}, _assetsLoadLifetime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InlineList::setButtonImage(Button &button, QImage large) {
|
|
||||||
button.media = nullptr;
|
|
||||||
const auto size = st::reactionBottomSize;
|
|
||||||
const auto factor = style::DevicePixelRatio();
|
|
||||||
button.image = Images::prepare(
|
|
||||||
std::move(large),
|
|
||||||
size * factor,
|
|
||||||
size * factor,
|
|
||||||
Images::Option::Smooth,
|
|
||||||
size,
|
|
||||||
size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InlineList::downloadTaskFinished() {
|
|
||||||
auto hasOne = false;
|
|
||||||
for (auto &button : _buttons) {
|
|
||||||
if (!button.media) {
|
|
||||||
continue;
|
|
||||||
} else if (const auto image = button.media->getStickerLarge()) {
|
|
||||||
setButtonImage(button, image->original());
|
|
||||||
} else {
|
|
||||||
hasOne = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasOne) {
|
|
||||||
_waitingForDownloadTask = false;
|
|
||||||
if (assetsLoaded()) {
|
|
||||||
_assetsLoadLifetime.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InlineList::assetsLoaded() const {
|
|
||||||
return !_waitingForReactionsList && !_waitingForDownloadTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize InlineList::countOptimalSize() {
|
QSize InlineList::countOptimalSize() {
|
||||||
if (_buttons.empty()) {
|
if (_buttons.empty()) {
|
||||||
return _skipBlock;
|
return _skipBlock;
|
||||||
|
@ -282,7 +178,14 @@ void InlineList::paint(
|
||||||
p.setOpacity(1.);
|
p.setOpacity(1.);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.drawImage(inner.topLeft(), button.image);
|
if (button.image.isNull()) {
|
||||||
|
button.image = _owner->resolveImageFor(
|
||||||
|
button.emoji,
|
||||||
|
::Data::Reactions::ImageSize::InlineList);
|
||||||
|
}
|
||||||
|
if (!button.image.isNull()) {
|
||||||
|
p.drawImage(inner.topLeft(), button.image);
|
||||||
|
}
|
||||||
p.setPen(!inbubble
|
p.setPen(!inbubble
|
||||||
? st->msgServiceFg()
|
? st->msgServiceFg()
|
||||||
: !chosen
|
: !chosen
|
||||||
|
@ -304,11 +207,10 @@ void InlineList::paint(
|
||||||
}
|
}
|
||||||
|
|
||||||
InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
||||||
const auto owner = &message->data()->history()->owner();
|
|
||||||
auto result = InlineListData{ .owner = owner };
|
|
||||||
|
|
||||||
using Flag = InlineListData::Flag;
|
using Flag = InlineListData::Flag;
|
||||||
const auto item = message->message();
|
const auto item = message->message();
|
||||||
|
|
||||||
|
auto result = InlineListData();
|
||||||
result.reactions = item->reactions();
|
result.reactions = item->reactions();
|
||||||
result.chosenReaction = item->chosenReaction();
|
result.chosenReaction = item->chosenReaction();
|
||||||
result.flags = (message->hasOutLayout() ? Flag::OutLayout : Flag())
|
result.flags = (message->hasOutLayout() ? Flag::OutLayout : Flag())
|
||||||
|
|
|
@ -9,11 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "history/view/history_view_object.h"
|
#include "history/view/history_view_object.h"
|
||||||
|
|
||||||
class DocumentData;
|
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Session;
|
class Reactions;
|
||||||
class DocumentMedia;
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -35,7 +32,6 @@ struct InlineListData {
|
||||||
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
friend inline constexpr bool is_flag_type(Flag) { return true; };
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
|
|
||||||
not_null<Data::Session*> owner;
|
|
||||||
base::flat_map<QString, int> reactions;
|
base::flat_map<QString, int> reactions;
|
||||||
QString chosenReaction;
|
QString chosenReaction;
|
||||||
Flags flags = {};
|
Flags flags = {};
|
||||||
|
@ -44,7 +40,7 @@ struct InlineListData {
|
||||||
class InlineList final : public Object {
|
class InlineList final : public Object {
|
||||||
public:
|
public:
|
||||||
using Data = InlineListData;
|
using Data = InlineListData;
|
||||||
explicit InlineList(Data &&data);
|
InlineList(not_null<::Data::Reactions*> owner, Data &&data);
|
||||||
|
|
||||||
void update(Data &&data, int availableWidth);
|
void update(Data &&data, int availableWidth);
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
@ -61,9 +57,8 @@ public:
|
||||||
private:
|
private:
|
||||||
struct Button {
|
struct Button {
|
||||||
QRect geometry;
|
QRect geometry;
|
||||||
QImage image;
|
mutable QImage image;
|
||||||
QString emoji;
|
QString emoji;
|
||||||
std::shared_ptr<::Data::DocumentMedia> media;
|
|
||||||
ClickHandlerPtr link;
|
ClickHandlerPtr link;
|
||||||
QString countText;
|
QString countText;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -74,24 +69,15 @@ private:
|
||||||
void layoutButtons();
|
void layoutButtons();
|
||||||
|
|
||||||
void setButtonCount(Button &button, int count);
|
void setButtonCount(Button &button, int count);
|
||||||
void loadButtonImage(Button &button, not_null<DocumentData*> document);
|
|
||||||
void setButtonImage(Button &button, QImage large);
|
|
||||||
[[nodiscard]] Button prepareButtonWithEmoji(const QString &emoji);
|
[[nodiscard]] Button prepareButtonWithEmoji(const QString &emoji);
|
||||||
|
|
||||||
void reactionsListLoaded();
|
|
||||||
void downloadTaskFinished();
|
|
||||||
[[nodiscard]] bool assetsLoaded() const;
|
|
||||||
|
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
|
|
||||||
|
const not_null<::Data::Reactions*> _owner;
|
||||||
Data _data;
|
Data _data;
|
||||||
std::vector<Button> _buttons;
|
std::vector<Button> _buttons;
|
||||||
QSize _skipBlock;
|
QSize _skipBlock;
|
||||||
|
|
||||||
rpl::lifetime _assetsLoadLifetime;
|
|
||||||
bool _waitingForReactionsList = false;
|
|
||||||
bool _waitingForDownloadTask = false;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] InlineListData InlineListDataFromMessage(
|
[[nodiscard]] InlineListData InlineListDataFromMessage(
|
||||||
|
|
Loading…
Add table
Reference in a new issue