Reuse SizeTag::Large emoji instances.

This commit is contained in:
John Preston 2022-07-25 11:30:38 +03:00
parent 076d5c756a
commit c51837cfdf
9 changed files with 178 additions and 354 deletions

View file

@ -156,13 +156,12 @@ protected:
void leaveEventHook(QEvent *e) override; void leaveEventHook(QEvent *e) override;
private: private:
using CustomInstance = Ui::CustomEmoji::SeparateInstance;
struct Element { struct Element {
not_null<DocumentData*> document; not_null<DocumentData*> document;
std::shared_ptr<Data::DocumentMedia> documentMedia; std::shared_ptr<Data::DocumentMedia> documentMedia;
Lottie::Animation *lottie = nullptr; Lottie::Animation *lottie = nullptr;
Media::Clip::ReaderPointer webm; Media::Clip::ReaderPointer webm;
CustomInstance *emoji = nullptr; Ui::Text::CustomEmoji *emoji = nullptr;
Ui::Animations::Simple overAnimation; Ui::Animations::Simple overAnimation;
mutable QImage premiumLock; mutable QImage premiumLock;
@ -185,7 +184,7 @@ private:
not_null<DocumentData*> document, not_null<DocumentData*> document,
int index); int index);
void setupEmoji(int index); void setupEmoji(int index);
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance( [[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
not_null<DocumentData*> document); not_null<DocumentData*> document);
void customEmojiRepaint(); void customEmojiRepaint();
@ -213,8 +212,7 @@ private:
base::flat_map< base::flat_map<
not_null<DocumentData*>, not_null<DocumentData*>,
std::unique_ptr<CustomInstance>> _instances; std::unique_ptr<Ui::Text::CustomEmoji>> _customEmoji;
std::unique_ptr<Ui::CustomEmoji::SimpleManager> _manager;
bool _repaintScheduled = false; bool _repaintScheduled = false;
StickersPack _pack; StickersPack _pack;
@ -515,7 +513,6 @@ StickerSetBox::Inner::Inner(
: RpWidget(parent) : RpWidget(parent)
, _controller(controller) , _controller(controller)
, _api(&_controller->session().mtp()) , _api(&_controller->session().mtp())
, _manager(std::make_unique<Ui::CustomEmoji::SimpleManager>())
, _setId(set.id) , _setId(set.id)
, _setAccessHash(set.accessHash) , _setAccessHash(set.accessHash)
, _setShortName(set.shortName) , _setShortName(set.shortName)
@ -1098,24 +1095,22 @@ void StickerSetBox::Inner::clipCallback(
void StickerSetBox::Inner::setupEmoji(int index) { void StickerSetBox::Inner::setupEmoji(int index) {
auto &element = _elements[index]; auto &element = _elements[index];
element.emoji = resolveCustomInstance(element.document); element.emoji = resolveCustomEmoji(element.document);
} }
auto StickerSetBox::Inner::resolveCustomInstance( not_null<Ui::Text::CustomEmoji*> StickerSetBox::Inner::resolveCustomEmoji(
not_null<DocumentData*> document) not_null<DocumentData*> document) {
-> not_null<CustomInstance*> { const auto i = _customEmoji.find(document);
const auto i = _instances.find(document); if (i != end(_customEmoji)) {
if (i != end(_instances)) {
return i->second.get(); return i->second.get();
} }
auto instance = _manager->make( auto emoji = document->session().data().customEmojiManager().create(
_controller->session().data().customEmojiManager().createLoader(
document,
Data::CustomEmojiManager::SizeTag::Large),
[=] { customEmojiRepaint(); });
return _instances.emplace(
document, document,
std::move(instance) [=] { customEmojiRepaint(); },
Data::CustomEmojiManager::SizeTag::Large);
return _customEmoji.emplace(
document,
std::move(emoji)
).first->second.get(); ).first->second.get();
} }
@ -1167,7 +1162,7 @@ void StickerSetBox::Inner::paintSticker(
(_singleSize.height() - size.height()) / 2); (_singleSize.height() - size.height()) / 2);
auto lottieFrame = QImage(); auto lottieFrame = QImage();
if (element.emoji) { if (element.emoji) {
element.emoji->object.paint( element.emoji->paint(
p, p,
ppos.x(), ppos.x(),
ppos.y(), ppos.y(),

View file

@ -100,14 +100,13 @@ private:
}; };
struct EmojiListWidget::CustomInstance : Ui::CustomEmoji::SeparateInstance { struct EmojiListWidget::CustomEmojiInstance {
using SeparateInstance::SeparateInstance; std::unique_ptr<Ui::Text::CustomEmoji> emoji;
bool recentOnly = false; bool recentOnly = false;
}; };
struct EmojiListWidget::RecentOne { struct EmojiListWidget::RecentOne {
CustomInstance *instance = nullptr; Ui::Text::CustomEmoji *custom = nullptr;
RecentEmojiId id; RecentEmojiId id;
}; };
@ -371,8 +370,7 @@ EmojiListWidget::EmojiListWidget(
std::make_unique<LocalStickersManager>(&controller->session())) std::make_unique<LocalStickersManager>(&controller->session()))
, _collapsedBg(st::emojiPanExpand.height / 2, st::emojiPanHeaderFg) , _collapsedBg(st::emojiPanExpand.height / 2, st::emojiPanHeaderFg)
, _picker(this) , _picker(this)
, _showPickerTimer([=] { showPicker(); }) , _showPickerTimer([=] { showPicker(); }) {
, _repaintTimer([=] { invokeRepaints(); }) {
setMouseTracking(true); setMouseTracking(true);
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
@ -419,88 +417,20 @@ EmojiListWidget::EmojiListWidget(
} }
EmojiListWidget::~EmojiListWidget() { EmojiListWidget::~EmojiListWidget() {
base::take(_instances); base::take(_customEmoji);
base::take(_repaints);
} }
void EmojiListWidget::repaintLater( void EmojiListWidget::repaintCustom(uint64 setId) {
DocumentId documentId, if (!_repaintsScheduled.emplace(setId).second) {
uint64 setId,
Ui::CustomEmoji::RepaintRequest request) {
if (_instances.empty() || !request.when) {
return; return;
} }
auto &repaint = _repaints[request.duration]; const auto repaintRecent = (setId == RecentEmojiSectionSetId());
if (repaint.when < request.when) {
repaint.when = request.when;
}
if (setId) {
repaint.ids.emplace(setId);
}
if (_recentCustomIds.contains(documentId)) {
repaint.ids.emplace(RecentEmojiSectionSetId());
}
scheduleRepaintTimer();
}
void EmojiListWidget::scheduleRepaintTimer() {
if (_repaintTimerScheduled) {
return;
}
_repaintTimerScheduled = true;
Ui::PostponeCall(this, [=] {
_repaintTimerScheduled = false;
auto next = crl::time();
for (const auto &[duration, bunch] : _repaints) {
if (!next || next > bunch.when) {
next = bunch.when;
}
}
if (next && (!_repaintNext || _repaintNext > next)) {
const auto now = crl::now();
if (now >= next) {
_repaintNext = 0;
_repaintTimer.cancel();
invokeRepaints();
} else {
_repaintNext = next;
_repaintTimer.callOnce(next - now);
}
}
});
}
void EmojiListWidget::invokeRepaints() {
_repaintNext = 0;
auto ids = base::flat_set<uint64>();
const auto now = crl::now();
for (auto i = begin(_repaints); i != end(_repaints);) {
if (i->second.when > now) {
++i;
continue;
}
if (ids.empty()) {
ids = std::move(i->second.ids);
} else {
for (const auto id : i->second.ids) {
ids.emplace(id);
}
}
i = _repaints.erase(i);
}
repaintCustom([&](uint64 id) { return ids.contains(id); });
scheduleRepaintTimer();
}
template <typename CheckId>
void EmojiListWidget::repaintCustom(CheckId checkId) {
enumerateSections([&](const SectionInfo &info) { enumerateSections([&](const SectionInfo &info) {
const auto repaint1 = (info.section == int(Section::Recent)) const auto repaint1 = repaintRecent
&& checkId(RecentEmojiSectionSetId()); && (info.section == int(Section::Recent));
const auto repaint2 = !repaint1 const auto repaint2 = !repaint1
&& (info.section >= kEmojiSectionCount) && (info.section >= kEmojiSectionCount)
&& checkId(_custom[info.section - kEmojiSectionCount].id); && (setId == _custom[info.section - kEmojiSectionCount].id);
if (repaint1 || repaint2) { if (repaint1 || repaint2) {
update( update(
0, 0,
@ -555,8 +485,8 @@ void EmojiListWidget::unloadCustomIn(const SectionInfo &info) {
if (!info.section && _recentPainted) { if (!info.section && _recentPainted) {
_recentPainted = false; _recentPainted = false;
for (const auto &single : _recent) { for (const auto &single : _recent) {
if (const auto instance = single.instance) { if (const auto custom = single.custom) {
instance->object.unload(); custom->unload();
} }
} }
return; return;
@ -569,7 +499,7 @@ void EmojiListWidget::unloadCustomIn(const SectionInfo &info) {
} }
custom.painted = false; custom.painted = false;
for (const auto &single : custom.list) { for (const auto &single : custom.list) {
single.instance->object.unload(); single.custom->unload();
} }
} }
@ -742,7 +672,7 @@ void EmojiListWidget::fillRecent() {
continue; continue;
} }
_recent.push_back({ _recent.push_back({
.instance = resolveCustomInstance(one.id), .custom = resolveCustomEmoji(one.id),
.id = one.id, .id = one.id,
}); });
if (document) { if (document) {
@ -756,7 +686,10 @@ void EmojiListWidget::fillRecent() {
void EmojiListWidget::paintEvent(QPaintEvent *e) { void EmojiListWidget::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
QRect r = e ? e->rect() : rect();
_repaintsScheduled.clear();
const auto r = e ? e->rect() : rect();
if (r != rect()) { if (r != rect()) {
p.setClipRect(r); p.setClipRect(r);
} }
@ -897,9 +830,9 @@ void EmojiListWidget::drawRecent(
if (const auto emoji = std::get_if<EmojiPtr>(&_recent[index].id.data)) { if (const auto emoji = std::get_if<EmojiPtr>(&_recent[index].id.data)) {
drawEmoji(p, position, *emoji); drawEmoji(p, position, *emoji);
} else { } else {
Assert(_recent[index].instance != nullptr); Assert(_recent[index].custom != nullptr);
position += _innerPosition; position += _innerPosition;
_recent[index].instance->object.paint( _recent[index].custom->paint(
p, p,
position.x(), position.x(),
position.y(), position.y(),
@ -931,7 +864,7 @@ void EmojiListWidget::drawCustom(
int index) { int index) {
position += _innerPosition; position += _innerPosition;
_custom[set].painted = true; _custom[set].painted = true;
_custom[set].list[index].instance->object.paint( _custom[set].list[index].custom->paint(
p, p,
position.x(), position.x(),
position.y(), position.y(),
@ -1408,7 +1341,7 @@ void EmojiListWidget::refreshCustom() {
for (const auto document : list) { for (const auto document : list) {
if (document->sticker()) { if (document->sticker()) {
set.push_back({ set.push_back({
.instance = resolveCustomInstance(document, setId), .custom = resolveCustomEmoji(document, setId),
.document = document, .document = document,
}); });
if (!premium && document->isPremiumEmoji()) { if (!premium && document->isPremiumEmoji()) {
@ -1443,98 +1376,75 @@ void EmojiListWidget::refreshCustom() {
update(); update();
} }
auto EmojiListWidget::customInstanceWithLoader( Fn<void()> EmojiListWidget::repaintCallback(
std::unique_ptr<Ui::CustomEmoji::Loader> loader, DocumentId documentId,
DocumentId documentId, uint64 setId) {
uint64 setId) return [=] {
-> std::unique_ptr<CustomInstance> { repaintCustom(setId);
const auto recentOnly = (setId == RecentEmojiSectionSetId());
const auto repaintDelayedSetId = !recentOnly ? setId : uint64(0);
const auto repaintDelayed = [=](
not_null<Ui::CustomEmoji::Instance*> instance,
Ui::CustomEmoji::RepaintRequest request) {
repaintLater(documentId, repaintDelayedSetId, request);
};
const auto repaintNow = [=] {
if (_recentCustomIds.contains(documentId)) { if (_recentCustomIds.contains(documentId)) {
const auto recentSetId = RecentEmojiSectionSetId(); repaintCustom(RecentEmojiSectionSetId());
repaintCustom([&](uint64 id) {
return (id == setId) || (id == recentSetId);
});
} else {
repaintCustom([&](uint64 id) {
return (id == setId);
});
} }
}; };
auto result = std::make_unique<CustomInstance>(
std::move(loader),
std::move(repaintDelayed),
std::move(repaintNow));
result->recentOnly = recentOnly;
return result;
} }
auto EmojiListWidget::resolveCustomInstance( not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
not_null<DocumentData*> document, not_null<DocumentData*> document,
uint64 setId) uint64 setId) {
-> not_null<CustomInstance*> {
Expects(document->sticker() != nullptr); Expects(document->sticker() != nullptr);
const auto documentId = document->id; const auto documentId = document->id;
const auto i = _instances.find(documentId); const auto i = _customEmoji.find(documentId);
const auto recentOnly = (i != end(_instances)) && i->second->recentOnly; const auto recentOnly = (i != end(_customEmoji)) && i->second.recentOnly;
if (i != end(_instances) && !recentOnly) { if (i != end(_customEmoji) && !recentOnly) {
return i->second.get(); return i->second.emoji.get();
} }
auto instance = customInstanceWithLoader( auto instance = document->owner().customEmojiManager().create(
document->owner().customEmojiManager().createLoader( document,
document, repaintCallback(documentId, setId),
Data::CustomEmojiManager::SizeTag::Large), Data::CustomEmojiManager::SizeTag::Large);
documentId,
setId);
if (recentOnly) { if (recentOnly) {
for (auto &recent : _recent) { for (auto &recent : _recent) {
if (recent.instance && recent.instance == i->second.get()) { if (recent.custom && recent.custom == i->second.emoji.get()) {
recent.instance = instance.get(); recent.custom = instance.get();
} }
} }
i->second = std::move(instance); i->second.emoji = std::move(instance);
return i->second.get(); i->second.recentOnly = false;
return i->second.emoji.get();
} }
return _instances.emplace( return _customEmoji.emplace(
documentId, documentId,
std::move(instance)).first->second.get(); CustomEmojiInstance{ .emoji = std::move(instance) }
).first->second.emoji.get();
} }
auto EmojiListWidget::resolveCustomInstance( Ui::Text::CustomEmoji *EmojiListWidget::resolveCustomEmoji(
RecentEmojiId customId) RecentEmojiId customId) {
-> CustomInstance* {
const auto &data = customId.data; const auto &data = customId.data;
if (const auto document = std::get_if<RecentEmojiDocument>(&data)) { if (const auto document = std::get_if<RecentEmojiDocument>(&data)) {
return resolveCustomInstance(document->id); return resolveCustomEmoji(document->id);
} else if (const auto emoji = std::get_if<EmojiPtr>(&data)) { } else if (const auto emoji = std::get_if<EmojiPtr>(&data)) {
return nullptr; return nullptr;
} }
Unexpected("Custom recent emoji id."); Unexpected("Custom recent emoji id.");
} }
auto EmojiListWidget::resolveCustomInstance( not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
DocumentId documentId) DocumentId documentId) {
-> not_null<CustomInstance*> { const auto i = _customEmoji.find(documentId);
const auto i = _instances.find(documentId); if (i != end(_customEmoji)) {
if (i != end(_instances)) { return i->second.emoji.get();
return i->second.get();
} }
return _instances.emplace( return _customEmoji.emplace(
documentId, documentId,
customInstanceWithLoader( CustomEmojiInstance{
session().data().customEmojiManager().createLoader( .emoji = session().data().customEmojiManager().create(
documentId, documentId,
repaintCallback(documentId, RecentEmojiSectionSetId()),
Data::CustomEmojiManager::SizeTag::Large), Data::CustomEmojiManager::SizeTag::Large),
documentId, .recentOnly = true,
RecentEmojiSectionSetId()) }
).first->second.get(); ).first->second.emoji.get();
} }
std::vector<StickerIcon> EmojiListWidget::fillIcons() { std::vector<StickerIcon> EmojiListWidget::fillIcons() {

View file

@ -112,9 +112,8 @@ private:
bool premiumRequired = false; bool premiumRequired = false;
bool collapsed = false; bool collapsed = false;
}; };
struct CustomInstance;
struct CustomOne { struct CustomOne {
not_null<CustomInstance*> instance; not_null<Ui::Text::CustomEmoji*> custom;
not_null<DocumentData*> document; not_null<DocumentData*> document;
}; };
struct CustomSet { struct CustomSet {
@ -129,6 +128,7 @@ private:
bool canRemove = false; bool canRemove = false;
bool premiumRequired = false; bool premiumRequired = false;
}; };
struct CustomEmojiInstance;
struct RightButton { struct RightButton {
QImage back; QImage back;
QImage backOver; QImage backOver;
@ -137,10 +137,6 @@ private:
int textWidth = 0; int textWidth = 0;
}; };
struct RecentOne; struct RecentOne;
struct RepaintSet {
base::flat_set<uint64> ids;
crl::time when = 0;
};
struct OverEmoji { struct OverEmoji {
int section = 0; int section = 0;
int index = 0; int index = 0;
@ -253,25 +249,17 @@ private:
int section); int section);
[[nodiscard]] QPoint buttonRippleTopLeft(int section) const; [[nodiscard]] QPoint buttonRippleTopLeft(int section) const;
void repaintLater( void repaintCustom(uint64 setId);
DocumentId documentId,
uint64 setId,
Ui::CustomEmoji::RepaintRequest request);
template <typename CheckId>
void repaintCustom(CheckId checkId);
void scheduleRepaintTimer();
void invokeRepaints();
void fillRecent(); void fillRecent();
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance( [[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
not_null<DocumentData*> document, not_null<DocumentData*> document,
uint64 setId); uint64 setId);
[[nodiscard]] CustomInstance *resolveCustomInstance( [[nodiscard]] Ui::Text::CustomEmoji *resolveCustomEmoji(
Core::RecentEmojiId customId); Core::RecentEmojiId customId);
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance( [[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
DocumentId documentId); DocumentId documentId);
[[nodiscard]] std::unique_ptr<CustomInstance> customInstanceWithLoader( [[nodiscard]] Fn<void()> repaintCallback(
std::unique_ptr<Ui::CustomEmoji::Loader> loader,
DocumentId documentId, DocumentId documentId,
uint64 setId); uint64 setId);
@ -281,10 +269,11 @@ private:
int _counts[kEmojiSectionCount]; int _counts[kEmojiSectionCount];
std::vector<RecentOne> _recent; std::vector<RecentOne> _recent;
base::flat_set<DocumentId> _recentCustomIds; base::flat_set<DocumentId> _recentCustomIds;
base::flat_set<uint64> _repaintsScheduled;
bool _recentPainted = false; bool _recentPainted = false;
QVector<EmojiPtr> _emoji[kEmojiSectionCount]; QVector<EmojiPtr> _emoji[kEmojiSectionCount];
std::vector<CustomSet> _custom; std::vector<CustomSet> _custom;
base::flat_map<DocumentId, std::unique_ptr<CustomInstance>> _instances; base::flat_map<DocumentId, CustomEmojiInstance> _customEmoji;
int _rowsLeft = 0; int _rowsLeft = 0;
int _columnCount = 1; int _columnCount = 1;
@ -305,11 +294,6 @@ private:
object_ptr<EmojiColorPicker> _picker; object_ptr<EmojiColorPicker> _picker;
base::Timer _showPickerTimer; base::Timer _showPickerTimer;
base::flat_map<crl::time, RepaintSet> _repaints;
bool _repaintTimerScheduled = false;
base::Timer _repaintTimer;
crl::time _repaintNext = 0;
rpl::event_stream<EmojiPtr> _chosen; rpl::event_stream<EmojiPtr> _chosen;
rpl::event_stream<TabbedSelector::FileChosen> _customChosen; rpl::event_stream<TabbedSelector::FileChosen> _customChosen;

View file

@ -46,7 +46,6 @@ SuggestionsWidget::SuggestionsWidget(
not_null<Main::Session*> session) not_null<Main::Session*> session)
: RpWidget(parent) : RpWidget(parent)
, _session(session) , _session(session)
, _manager(std::make_unique<Ui::CustomEmoji::SimpleManager>())
, _oneWidth(st::emojiSuggestionSize) , _oneWidth(st::emojiSuggestionSize)
, _padding(st::emojiSuggestionsPadding) { , _padding(st::emojiSuggestionsPadding) {
resize( resize(
@ -141,7 +140,7 @@ auto SuggestionsWidget::prependCustom(std::vector<Row> rows)
for (const auto &[position, one] : custom) { for (const auto &[position, one] : custom) {
result.push_back(Row(one.emoji, one.replacement)); result.push_back(Row(one.emoji, one.replacement));
result.back().document = one.document; result.back().document = one.document;
result.back().instance = resolveCustomInstance(one.document); result.back().custom = resolveCustomEmoji(one.document);
} }
for (auto &row : rows) { for (auto &row : rows) {
result.push_back(std::move(row)); result.push_back(std::move(row));
@ -149,21 +148,19 @@ auto SuggestionsWidget::prependCustom(std::vector<Row> rows)
return result; return result;
} }
auto SuggestionsWidget::resolveCustomInstance( not_null<Ui::Text::CustomEmoji*> SuggestionsWidget::resolveCustomEmoji(
not_null<DocumentData*> document) not_null<DocumentData*> document) {
-> not_null<CustomInstance*> { const auto i = _customEmoji.find(document);
const auto i = _instances.find(document); if (i != end(_customEmoji)) {
if (i != end(_instances)) {
return i->second.get(); return i->second.get();
} }
auto instance = _manager->make( auto emoji = document->session().data().customEmojiManager().create(
_session->data().customEmojiManager().createLoader(
document,
Data::CustomEmojiManager::SizeTag::Large),
[=] { customEmojiRepaint(); });
return _instances.emplace(
document, document,
std::move(instance) [=] { customEmojiRepaint(); },
Data::CustomEmojiManager::SizeTag::Large);
return _customEmoji.emplace(
document,
std::move(emoji)
).first->second.get(); ).first->second.get();
} }
@ -333,8 +330,8 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) {
const auto size = esize / style::DevicePixelRatio(); const auto size = esize / style::DevicePixelRatio();
const auto x = i * _oneWidth + (_oneWidth - size) / 2; const auto x = i * _oneWidth + (_oneWidth - size) / 2;
const auto y = (_oneWidth - size) / 2; const auto y = (_oneWidth - size) / 2;
if (row.instance) { if (row.custom) {
row.instance->object.paint(p, x, y, now, preview, false); row.custom->paint(p, x, y, now, preview, false);
} else { } else {
Ui::Emoji::Draw(p, emoji, esize, x, y); Ui::Emoji::Draw(p, emoji, esize, x, y);
} }

View file

@ -23,10 +23,9 @@ class InnerDropdown;
class InputField; class InputField;
} // namespace Ui } // namespace Ui
namespace Ui::CustomEmoji { namespace Ui::Text {
struct SeparateInstance; class CustomEmoji;
class SimpleManager; } // namespace Ui::Text
} // namespace Ui::CustomEmoji
namespace Ui::Emoji { namespace Ui::Emoji {
@ -48,11 +47,10 @@ public:
[[nodiscard]] rpl::producer<Chosen> triggered() const; [[nodiscard]] rpl::producer<Chosen> triggered() const;
private: private:
using CustomInstance = Ui::CustomEmoji::SeparateInstance;
struct Row { struct Row {
Row(not_null<EmojiPtr> emoji, const QString &replacement); Row(not_null<EmojiPtr> emoji, const QString &replacement);
CustomInstance *instance = nullptr; Ui::Text::CustomEmoji *custom = nullptr;
DocumentData *document = nullptr; DocumentData *document = nullptr;
not_null<EmojiPtr> emoji; not_null<EmojiPtr> emoji;
QString replacement; QString replacement;
@ -92,7 +90,7 @@ private:
void scrollTo(int value, anim::type animated = anim::type::instant); void scrollTo(int value, anim::type animated = anim::type::instant);
void stopAnimations(); void stopAnimations();
[[nodiscard]] not_null<CustomInstance*> resolveCustomInstance( [[nodiscard]] not_null<Ui::Text::CustomEmoji*> resolveCustomEmoji(
not_null<DocumentData*> document); not_null<DocumentData*> document);
void customEmojiRepaint(); void customEmojiRepaint();
@ -102,8 +100,7 @@ private:
base::flat_map< base::flat_map<
not_null<DocumentData*>, not_null<DocumentData*>,
std::unique_ptr<CustomInstance>> _instances; std::unique_ptr<Ui::Text::CustomEmoji>> _customEmoji;
std::unique_ptr<Ui::CustomEmoji::SimpleManager> _manager;
bool _repaintScheduled = false; bool _repaintScheduled = false;
std::optional<QPoint> _lastMousePosition; std::optional<QPoint> _lastMousePosition;

View file

@ -361,35 +361,59 @@ CustomEmojiManager::CustomEmojiManager(not_null<Session*> owner)
CustomEmojiManager::~CustomEmojiManager() = default; CustomEmojiManager::~CustomEmojiManager() = default;
template <typename LoaderFactory>
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create( std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
QStringView data, DocumentId documentId,
Fn<void()> update) { Fn<void()> update,
const auto parsed = ParseCustomEmojiData(data); SizeTag tag,
if (!parsed.id) { LoaderFactory factory) {
return nullptr; auto &instances = _instances[SizeIndex(tag)];
} auto i = instances.find(documentId);
auto i = _instances.find(parsed.id); if (i == end(instances)) {
if (i == end(_instances)) {
using Loading = Ui::CustomEmoji::Loading; using Loading = Ui::CustomEmoji::Loading;
auto loader = createLoader(parsed.id, SizeTag::Normal);
const auto repaint = [=]( const auto repaint = [=](
not_null<Ui::CustomEmoji::Instance*> instance, not_null<Ui::CustomEmoji::Instance*> instance,
Ui::CustomEmoji::RepaintRequest request) { Ui::CustomEmoji::RepaintRequest request) {
repaintLater(instance, request); repaintLater(instance, request);
}; };
i = _instances.emplace( i = instances.emplace(
parsed.id, documentId,
std::make_unique<Ui::CustomEmoji::Instance>(Loading{ std::make_unique<Ui::CustomEmoji::Instance>(Loading{
std::move(loader), factory(),
Ui::CustomEmoji::Preview() Ui::CustomEmoji::Preview()
}, std::move(repaint))).first; }, std::move(repaint))).first;
} }
return std::make_unique<Ui::CustomEmoji::Object>( return std::make_unique<Ui::CustomEmoji::Object>(
i->second.get(), i->second.get(),
std::move(update)); std::move(update));
} }
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
QStringView data,
Fn<void()> update,
SizeTag tag) {
const auto parsed = ParseCustomEmojiData(data);
return parsed.id ? create(parsed.id, std::move(update), tag) : nullptr;
}
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
DocumentId documentId,
Fn<void()> update,
SizeTag tag) {
return create(documentId, std::move(update), tag, [&] {
return createLoader(documentId, tag);
});
}
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
not_null<DocumentData*> document,
Fn<void()> update,
SizeTag tag) {
return create(document->id, std::move(update), tag, [&] {
return createLoader(document, tag);
});
}
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) {
@ -405,7 +429,8 @@ std::unique_ptr<Ui::CustomEmoji::Loader> CustomEmojiManager::createLoader(
CustomEmojiId{ .selfId = selfId, .id = documentId }, CustomEmojiId{ .selfId = selfId, .id = documentId },
tag); tag);
if (result->resolving()) { if (result->resolving()) {
_loaders[documentId].push_back(base::make_weak(result.get())); const auto i = SizeIndex(tag);
_loaders[i][documentId].push_back(base::make_weak(result.get()));
_pendingForRequest.emplace(documentId); _pendingForRequest.emplace(documentId);
if (!_requestId && _pendingForRequest.size() == 1) { if (!_requestId && _pendingForRequest.size() == 1) {
crl::on_main(this, [=] { request(); }); crl::on_main(this, [=] { request(); });
@ -439,10 +464,12 @@ void CustomEmojiManager::request() {
for (const auto &entry : result.v) { for (const auto &entry : result.v) {
const auto document = _owner->processDocument(entry); const auto document = _owner->processDocument(entry);
const auto id = document->id; const auto id = document->id;
if (const auto loaders = _loaders.take(id)) { for (auto &loaders : _loaders) {
for (const auto &weak : *loaders) { if (const auto list = loaders.take(id)) {
if (const auto strong = weak.get()) { for (const auto &weak : *list) {
strong->resolved(document); if (const auto strong = weak.get()) {
strong->resolved(document);
}
} }
} }
} }
@ -479,6 +506,13 @@ void CustomEmojiManager::requestSetFor(not_null<DocumentData*> document) {
}); });
} }
int CustomEmojiManager::SizeIndex(SizeTag tag) {
const auto result = static_cast<int>(tag);
Ensures(result >= 0 && result < 2);
return result;
}
void CustomEmojiManager::requestFinished() { void CustomEmojiManager::requestFinished() {
_requestId = 0; _requestId = 0;
if (!_pendingForRequest.empty()) { if (!_pendingForRequest.empty()) {

View file

@ -40,7 +40,16 @@ public:
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create( [[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
QStringView data, QStringView data,
Fn<void()> update); Fn<void()> update,
SizeTag tag = SizeTag::Normal);
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
DocumentId documentId,
Fn<void()> update,
SizeTag tag = SizeTag::Normal);
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
not_null<DocumentData*> document,
Fn<void()> update,
SizeTag tag = SizeTag::Normal);
[[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader( [[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader(
not_null<DocumentData*> document, not_null<DocumentData*> document,
@ -69,14 +78,22 @@ private:
void invokeRepaints(); void invokeRepaints();
void requestSetFor(not_null<DocumentData*> document); void requestSetFor(not_null<DocumentData*> document);
template <typename LoaderFactory>
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
DocumentId documentId,
Fn<void()> update,
SizeTag tag,
LoaderFactory factory);
[[nodiscard]] static int SizeIndex(SizeTag tag);
const not_null<Session*> _owner; const not_null<Session*> _owner;
base::flat_map< base::flat_map<
uint64, uint64,
std::unique_ptr<Ui::CustomEmoji::Instance>> _instances; std::unique_ptr<Ui::CustomEmoji::Instance>> _instances[2];
base::flat_map< base::flat_map<
uint64, uint64,
std::vector<base::weak_ptr<CustomEmojiLoader>>> _loaders; std::vector<base::weak_ptr<CustomEmojiLoader>>> _loaders[2];
base::flat_set<uint64> _pendingForRequest; base::flat_set<uint64> _pendingForRequest;
mtpRequestId _requestId = 0; mtpRequestId _requestId = 0;

View file

@ -648,84 +648,4 @@ void Object::repaint() {
_repaint(); _repaint();
} }
SeparateInstance::SeparateInstance(
std::unique_ptr<Loader> loader,
Fn<void(not_null<Instance*>, RepaintRequest)> repaintLater,
Fn<void()> repaint)
: emoji(Loading(std::move(loader), Preview()), std::move(repaintLater))
, object(&emoji, std::move(repaint)) {
}
SimpleManager::SimpleManager()
: _repaintTimer([=] { invokeRepaints(); }) {
}
std::unique_ptr<SeparateInstance> SimpleManager::make(
std::unique_ptr<Loader> loader,
Fn<void()> repaint) {
auto repaintLater = [=](
not_null<Instance*> instance,
RepaintRequest request) {
if (!request.when) {
return;
}
auto &when = _repaints[request.duration];
if (when < request.when) {
when = request.when;
}
if (_repaintTimerScheduled) {
return;
}
scheduleRepaintTimer();
};
_simpleRepaint = repaint;
return std::make_unique<SeparateInstance>(
std::move(loader),
std::move(repaintLater),
std::move(repaint));
}
void SimpleManager::scheduleRepaintTimer() {
_repaintTimerScheduled = true;
Ui::PostponeCall(this, [=] {
_repaintTimerScheduled = false;
auto next = crl::time();
for (const auto &[duration, when] : _repaints) {
if (!next || next > when) {
next = when;
}
}
if (next && (!_repaintNext || _repaintNext > next)) {
const auto now = crl::now();
if (now >= next) {
_repaintNext = 0;
_repaintTimer.cancel();
invokeRepaints();
} else {
_repaintNext = next;
_repaintTimer.callOnce(next - now);
}
}
});
}
void SimpleManager::invokeRepaints() {
_repaintNext = 0;
auto invoke = false;
const auto now = crl::now();
for (auto i = begin(_repaints); i != end(_repaints);) {
if (i->second > now) {
++i;
continue;
}
invoke = true;
i = _repaints.erase(i);
}
if (invoke && _simpleRepaint) {
_simpleRepaint();
}
scheduleRepaintTimer();
}
} // namespace Ui::CustomEmoji } // namespace Ui::CustomEmoji

View file

@ -267,34 +267,4 @@ private:
}; };
struct SeparateInstance {
SeparateInstance(
std::unique_ptr<Loader> loader,
Fn<void(not_null<Instance*>, RepaintRequest)> repaintLater,
Fn<void()> repaint);
Instance emoji;
Object object;
};
class SimpleManager final : public base::has_weak_ptr {
public:
SimpleManager();
[[nodiscard]] std::unique_ptr<SeparateInstance> make(
std::unique_ptr<Loader> loader,
Fn<void()> repaint);
private:
void scheduleRepaintTimer();
void invokeRepaints();
base::flat_map<crl::time, crl::time> _repaints;
bool _repaintTimerScheduled = false;
crl::time _repaintNext = 0;
base::Timer _repaintTimer;
Fn<void()> _simpleRepaint;
};
} // namespace Ui::CustomEmoji } // namespace Ui::CustomEmoji