diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index f303c4b93..5615946d1 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_user.h" #include "data/data_session.h" +#include "data/data_cloud_file.h" #include "mainwidget.h" #include "mainwindow.h" #include "apiwrap.h" @@ -222,6 +223,7 @@ private: } not_null peer; + mutable std::shared_ptr userpic; Ui::Text::String name, status; }; void paintChat(Painter &p, const ChatRow &row, bool selected) const; @@ -1438,7 +1440,7 @@ void RevokePublicLinkBox::resizeEvent(QResizeEvent *e) { void RevokePublicLinkBox::Inner::paintChat(Painter &p, const ChatRow &row, bool selected) const { auto peer = row.peer; - peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); + peer->paintUserpicLeft(p, row.userpic, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); p.setPen(st::contactsNameFg); diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 34be632f2..65581f548 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -909,19 +909,20 @@ ConfirmInviteBox::ConfirmInviteBox( } } -std::vector> ConfirmInviteBox::GetParticipants( - not_null session, - const MTPDchatInvite &data) { +auto ConfirmInviteBox::GetParticipants( + not_null session, + const MTPDchatInvite &data) +-> std::vector { const auto participants = data.vparticipants(); if (!participants) { return {}; } const auto &v = participants->v; - auto result = std::vector>(); + auto result = std::vector(); result.reserve(v.size()); for (const auto &participant : v) { if (const auto user = session->data().processUser(participant)) { - result.push_back(user); + result.push_back(Participant{ user }); } } return result; @@ -946,12 +947,12 @@ void ConfirmInviteBox::prepare() { _userWidth = (st::confirmInviteUserPhotoSize + 2 * padding); int sumWidth = _participants.size() * _userWidth; int left = (st::boxWideWidth - sumWidth) / 2; - for (const auto user : _participants) { + for (const auto &participant : _participants) { auto name = new Ui::FlatLabel(this, st::confirmInviteUserName); name->resizeToWidth(st::confirmInviteUserPhotoSize + padding); - name->setText(user->firstName.isEmpty() - ? user->name - : user->firstName); + name->setText(participant.user->firstName.isEmpty() + ? participant.user->name + : participant.user->firstName); name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop); left += _userWidth; } @@ -993,9 +994,10 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) { int sumWidth = _participants.size() * _userWidth; int left = (width() - sumWidth) / 2; - for_const (auto user, _participants) { - user->paintUserpicLeft( + for (auto &participant : _participants) { + participant.user->paintUserpicLeft( p, + participant.userpic, left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, st::confirmInviteUserPhotoTop, width(), diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index daf242b16..db7ac9e7d 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { class PhotoMedia; +class CloudImageView; } // namespace Data namespace Main { @@ -225,7 +226,11 @@ protected: void paintEvent(QPaintEvent *e) override; private: - static std::vector> GetParticipants( + struct Participant { + not_null user; + std::shared_ptr userpic; + }; + static std::vector GetParticipants( not_null session, const MTPDchatInvite &data); @@ -236,7 +241,7 @@ private: object_ptr _status; std::shared_ptr _photo; std::unique_ptr _photoEmpty; - std::vector> _participants; + std::vector _participants; bool _isChannel = false; int _userWidth = 0; diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp index d0cea28d9..a20607aa3 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp @@ -78,6 +78,7 @@ private: }; struct PeerButton { not_null history; + std::shared_ptr userpic; Button button; }; @@ -185,8 +186,9 @@ void FilterChatsPreview::updateData( } for (const auto history : peers) { _removePeer.push_back({ - history, - makeButton([=] { removePeer(history); }) }); + .history = history, + .button = makeButton([=] { removePeer(history); }) + }); } refresh(); } @@ -203,7 +205,7 @@ int FilterChatsPreview::resizeGetHeight(int newWidth) { for (const auto &[flag, button] : _removeFlag) { moveNextButton(button.get()); } - for (const auto &[history, button] : _removePeer) { + for (const auto &[history, userpic, button] : _removePeer) { moveNextButton(button.get()); } return top; @@ -235,7 +237,7 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) { FilterChatsTypeName(flag)); top += st.height; } - for (const auto &[history, button] : _removePeer) { + for (auto &[history, userpic, button] : _removePeer) { const auto savedMessages = history->peer->isSelf(); if (savedMessages) { Ui::EmptyUserpic::PaintSavedMessages( @@ -253,6 +255,7 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) { } else { history->peer->paintUserpicLeft( p, + userpic, iconLeft, top + iconTop, width(), diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index a3a1b015e..b5e56f369 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -180,11 +180,12 @@ QString ExceptionRow::generateShortName() { PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() { const auto peer = this->peer(); const auto saved = peer->isSelf(); - return [=](Painter &p, int x, int y, int outerWidth, int size) { + auto userpic = saved ? nullptr : ensureUserpicView(); + return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { if (saved) { Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); } else { - peer->paintUserpicLeft(p, x, y, outerWidth, size); + peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size); } }; } diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 3ab014575..f99f27e62 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -41,8 +41,9 @@ PaintRoundImageCallback PaintUserpicCallback( Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); }; } - return [=](Painter &p, int x, int y, int outerWidth, int size) { - peer->paintUserpicLeft(p, x, y, outerWidth, size); + auto userpic = std::shared_ptr(); + return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { + peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size); }; } @@ -483,14 +484,22 @@ QString PeerListRow::generateShortName() { : peer()->shortName(); } +std::shared_ptr PeerListRow::ensureUserpicView() { + if (!_userpic) { + _userpic = peer()->createUserpicView(); + } + return _userpic; +} + PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() { const auto saved = _isSavedMessagesChat; const auto peer = this->peer(); - return [=](Painter &p, int x, int y, int outerWidth, int size) { + auto userpic = saved ? nullptr : ensureUserpicView(); + return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { if (saved) { Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); } else { - peer->paintUserpicLeft(p, x, y, outerWidth, size); + peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size); } }; } @@ -595,7 +604,7 @@ void PeerListRow::paintDisabledCheckUserpic( if (_isSavedMessagesChat) { Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2); } else { - peer()->paintUserpicLeft(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2); + peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, userpicRadius * 2); } { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index fbc95a30f..8cb07163b 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -7,11 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include #include "ui/rp_widget.h" #include "ui/empty_userpic.h" #include "boxes/abstract_box.h" #include "mtproto/sender.h" +#include "data/data_cloud_file.h" #include "base/timer.h" namespace style { @@ -85,6 +85,8 @@ public: return _id; } + [[nodiscard]] std::shared_ptr ensureUserpicView(); + [[nodiscard]] virtual QString generateName(); [[nodiscard]] virtual QString generateShortName(); [[nodiscard]] virtual auto generatePaintUserpicCallback() @@ -223,6 +225,7 @@ private: PeerListRowId _id = 0; PeerData *_peer = nullptr; + mutable std::shared_ptr _userpic; std::unique_ptr _ripple; std::unique_ptr _checkbox; Ui::Text::String _name; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index b13ad2db6..43bcd81dc 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_file_origin.h" #include "data/data_photo_media.h" +#include "data/data_cloud_file.h" #include "calls/calls_emoji_fingerprint.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -490,6 +491,7 @@ void Panel::finishAnimating() { void Panel::showControls() { Expects(_call != nullptr); + showChildren(); _decline->setVisible(_decline->toggled()); _cancel->setVisible(_cancel->toggled()); @@ -511,9 +513,8 @@ void Panel::hideAndDestroy() { } void Panel::processUserPhoto() { - if (!_user->userpicLoaded()) { - _user->loadUserpic(); - } + _userpic = _user->createUserpicView(); + _user->loadUserpic(); const auto photo = _user->userpicPhotoId() ? _user->owner().photo(_user->userpicPhotoId()).get() : nullptr; @@ -542,9 +543,8 @@ void Panel::refreshUserPhoto() { _photo->image(Data::PhotoSize::Large), _user->userpicPhotoOrigin()); } else if (_userPhoto.isNull()) { - const auto userpic = _user->currentUserpic(); createUserpicCache( - userpic ? userpic.get() : nullptr, + _userpic ? _userpic->image() : nullptr, _user->userpicOrigin()); } } diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index 518d20225..724825f34 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { class PhotoMedia; +class CloudImageView; } // namespace Data namespace Ui { @@ -116,6 +117,7 @@ private: Call *_call = nullptr; not_null _user; + std::shared_ptr _userpic; std::shared_ptr _photo; bool _useTransparency = true; diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index c791b9560..f815a660e 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -50,9 +50,9 @@ FieldAutocomplete::FieldAutocomplete( &_srows)); _inner->setGeometry(rect()); - connect(_inner, SIGNAL(mentionChosen(UserData*, FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(UserData*, FieldAutocomplete::ChooseMethod))); - connect(_inner, SIGNAL(hashtagChosen(QString, FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString, FieldAutocomplete::ChooseMethod))); - connect(_inner, SIGNAL(botCommandChosen(QString, FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString, FieldAutocomplete::ChooseMethod))); + connect(_inner, SIGNAL(mentionChosen(not_null,FieldAutocomplete::ChooseMethod)), this, SIGNAL(mentionChosen(not_null,FieldAutocomplete::ChooseMethod))); + connect(_inner, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod))); + connect(_inner, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(stickerChosen(not_null,FieldAutocomplete::ChooseMethod)), this, SIGNAL(stickerChosen(not_null,FieldAutocomplete::ChooseMethod))); connect(_inner, SIGNAL(mustScrollTo(int, int)), _scroll, SLOT(scrollToY(int, int))); @@ -152,7 +152,9 @@ void FieldAutocomplete::showStickers(EmojiPtr emoji) { } bool FieldAutocomplete::clearFilteredBotCommands() { - if (_brows.isEmpty()) return false; + if (_brows.empty()) { + return false; + } _brows.clear(); return true; } @@ -160,8 +162,8 @@ bool FieldAutocomplete::clearFilteredBotCommands() { namespace { template inline int indexOfInFirstN(const T &v, const U &elem, int last) { - for (auto b = v.cbegin(), i = b, e = b + qMax(v.size(), last); i != e; ++i) { - if (*i == elem) { + for (auto b = v.cbegin(), i = b, e = b + std::max(int(v.size()), last); i != e; ++i) { + if (i->user == elem) { return (i - b); } } @@ -241,12 +243,12 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { for_const (auto user, cRecentInlineBots()) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByUsername(user)) continue; - mrows.push_back(user); + mrows.push_back({ user }); ++recentInlineBots; } } if (_chat) { - auto ordered = QMultiMap>(); + auto sorted = base::flat_multi_map>(); const auto byOnline = [&](not_null user) { return Data::SortByOnlineValue(user, now); }; @@ -258,23 +260,19 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; - ordered.insertMulti(byOnline(user), user); + sorted.emplace(byOnline(user), user); } } for (const auto user : _chat->lastAuthors) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; - mrows.push_back(user); - if (!ordered.isEmpty()) { - ordered.remove(byOnline(user), user); - } + mrows.push_back({ user }); + sorted.remove(byOnline(user), user); } - if (!ordered.isEmpty()) { - for (auto i = ordered.cend(), b = ordered.cbegin(); i != b;) { - --i; - mrows.push_back(i.value()); - } + for (auto i = sorted.cend(), b = sorted.cbegin(); i != b;) { + --i; + mrows.push_back({ i->second }); } } else if (_channel && _channel->isMegagroup()) { QMultiMap ordered; @@ -286,7 +284,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { if (user->isInaccessible()) continue; if (!listAllSuggestions && filterNotPassedByName(user)) continue; if (indexOfInFirstN(mrows, user, recentInlineBots) >= 0) continue; - mrows.push_back(user); + mrows.push_back({ user }); } } } @@ -378,7 +376,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { continue; } } - brows.push_back(qMakePair(user, &user->botInfo->commands.at(j))); + brows.push_back({ user, &user->botInfo->commands.at(j) }); } } } @@ -390,7 +388,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { QString toFilter = (hasUsername || botStatus == 0 || botStatus == 2) ? user->botInfo->commands.at(j).command + '@' + user->username : user->botInfo->commands.at(j).command; if (!toFilter.startsWith(_filter, Qt::CaseInsensitive)/* || toFilter.size() == _filter.size()*/) continue; } - brows.push_back(qMakePair(user, &user->botInfo->commands.at(j))); + brows.push_back({ user, &user->botInfo->commands.at(j) }); } } } @@ -411,7 +409,7 @@ void FieldAutocomplete::rowsUpdated( internal::BotCommandRows &&brows, internal::StickerRows &&srows, bool resetScroll) { - if (mrows.isEmpty() && hrows.isEmpty() && brows.isEmpty() && srows.empty()) { + if (mrows.empty() && hrows.empty() && brows.empty() && srows.empty()) { if (!isHidden()) { hideAnimated(); } @@ -452,11 +450,11 @@ void FieldAutocomplete::recount(bool resetScroll) { int32 stickersPerRow = qMax(1, int32(_boundings.width() - 2 * st::stickerPanPadding) / int32(st::stickerPanSize.width())); int32 rows = rowscount(_srows.size(), stickersPerRow); h = st::stickerPanPadding + rows * st::stickerPanSize.height(); - } else if (!_mrows.isEmpty()) { + } else if (!_mrows.empty()) { h = _mrows.size() * st::mentionHeight; - } else if (!_hrows.isEmpty()) { + } else if (!_hrows.empty()) { h = _hrows.size() * st::mentionHeight; - } else if (!_brows.isEmpty()) { + } else if (!_brows.empty()) { h = _brows.size() * st::mentionHeight; } @@ -693,7 +691,11 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { } } else { int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1; - int32 last = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); + int32 last = !_mrows->empty() + ? _mrows->size() + : !_hrows->empty() + ? _hrows->size() + : _brows->size(); auto filter = _parent->filter(); bool hasUsername = filter.indexOf('@') > 0; int filterSize = filter.size(); @@ -705,12 +707,13 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { if (selected) { p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::mentionBgOver); int skip = (st::mentionHeight - st::smallCloseIconOver.height()) / 2; - if (!_hrows->isEmpty() || (!_mrows->isEmpty() && i < _recentInlineBotsInRows)) { + if (!_hrows->empty() || (!_mrows->empty() && i < _recentInlineBotsInRows)) { st::smallCloseIconOver.paint(p, QPoint(width() - st::smallCloseIconOver.width() - skip, i * st::mentionHeight + skip), width()); } } - if (!_mrows->isEmpty()) { - const auto user = _mrows->at(i); + if (!_mrows->empty()) { + auto &row = _mrows->at(i); + const auto user = row.user; auto first = (!filterIsEmpty && user->username.startsWith(filter, Qt::CaseInsensitive)) ? ('@' + user->username.mid(0, filterSize)) : QString(); auto second = first.isEmpty() ? (user->username.isEmpty() ? QString() : ('@' + user->username)) : user->username.mid(filterSize); auto firstwidth = st::mentionFont->width(first); @@ -732,7 +735,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { } } user->loadUserpic(); - user->paintUserpicLeft(p, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); + user->paintUserpicLeft(p, row.userpic, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); p.setPen(selected ? st::mentionNameFgOver : st::mentionNameFg); user->nameText().drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); @@ -744,7 +747,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { p.setPen(selected ? st::mentionFgOver : st::mentionFg); p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); } - } else if (!_hrows->isEmpty()) { + } else if (!_hrows->empty()) { QString hrow = _hrows->at(i); QString first = filterIsEmpty ? QString() : ('#' + hrow.mid(0, filterSize)); QString second = filterIsEmpty ? ('#' + hrow) : hrow.mid(filterSize); @@ -768,16 +771,17 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); } } else { - UserData *user = _brows->at(i).first; + auto &row = _brows->at(i); + const auto user = row.user; - const BotCommand *command = _brows->at(i).second; - QString toHighlight = command->command; + const auto command = row.command; + auto toHighlight = command->command; int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1); if (hasUsername || botStatus == 0 || botStatus == 2) { toHighlight += '@' + user->username; } user->loadUserpic(); - user->paintUserpicLeft(p, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); + user->paintUserpicLeft(p, row.userpic, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); auto commandText = '/' + toHighlight; @@ -820,7 +824,7 @@ void FieldAutocompleteInner::clearSel(bool hidden) { _overDelete = false; _mouseSelection = false; _lastMousePosition = std::nullopt; - setSel((_mrows->isEmpty() && _brows->isEmpty() && _hrows->isEmpty()) ? -1 : 0); + setSel((_mrows->empty() && _brows->empty() && _hrows->empty()) ? -1 : 0); if (hidden) { _down = -1; _previewShown = false; @@ -831,7 +835,13 @@ bool FieldAutocompleteInner::moveSel(int key) { _mouseSelection = false; _lastMousePosition = std::nullopt; - int32 maxSel = (_mrows->isEmpty() ? (_hrows->isEmpty() ? (_brows->isEmpty() ? _srows->size() : _brows->size()) : _hrows->size()) : _mrows->size()); + int32 maxSel = !_mrows->empty() + ? _mrows->size() + : !_hrows->empty() + ? _hrows->size() + : !_brows->empty() + ? _brows->size() + : _srows->size(); int32 direction = (key == Qt::Key_Up) ? -1 : (key == Qt::Key_Down ? 1 : 0); if (!_srows->empty()) { if (key == Qt::Key_Left) { @@ -862,20 +872,20 @@ bool FieldAutocompleteInner::chooseSelected(FieldAutocomplete::ChooseMethod meth emit stickerChosen((*_srows)[_sel].document, method); return true; } - } else if (!_mrows->isEmpty()) { + } else if (!_mrows->empty()) { if (_sel >= 0 && _sel < _mrows->size()) { - emit mentionChosen(_mrows->at(_sel), method); + emit mentionChosen(_mrows->at(_sel).user, method); return true; } - } else if (!_hrows->isEmpty()) { + } else if (!_hrows->empty()) { if (_sel >= 0 && _sel < _hrows->size()) { emit hashtagChosen('#' + _hrows->at(_sel), method); return true; } - } else if (!_brows->isEmpty()) { + } else if (!_brows->empty()) { if (_sel >= 0 && _sel < _brows->size()) { - UserData *user = _brows->at(_sel).first; - const BotCommand *command(_brows->at(_sel).second); + const auto user = _brows->at(_sel).user; + const auto command = _brows->at(_sel).command; int32 botStatus = _parent->chat() ? _parent->chat()->botStatus : ((_parent->channel() && _parent->channel()->isMegagroup()) ? _parent->channel()->mgInfo->botStatus : -1); if (botStatus == 0 || botStatus == 2 || _parent->filter().indexOf('@') > 0) { emit botCommandChosen('/' + command->command + '@' + user->username, method); @@ -895,9 +905,9 @@ void FieldAutocompleteInner::setRecentInlineBotsInRows(int32 bots) { void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) { selectByMouse(e->globalPos()); if (e->button() == Qt::LeftButton) { - if (_overDelete && _sel >= 0 && _sel < (_mrows->isEmpty() ? _hrows->size() : _recentInlineBotsInRows)) { + if (_overDelete && _sel >= 0 && _sel < (_mrows->empty() ? _hrows->size() : _recentInlineBotsInRows)) { bool removed = false; - if (_mrows->isEmpty()) { + if (_mrows->empty()) { QString toRemove = _hrows->at(_sel); RecentHashtagPack &recent(cRefRecentWriteHashtags()); for (RecentHashtagPack::iterator i = recent.begin(); i != recent.cend();) { @@ -909,7 +919,7 @@ void FieldAutocompleteInner::mousePressEvent(QMouseEvent *e) { } } } else { - UserData *toRemove = _mrows->at(_sel); + UserData *toRemove = _mrows->at(_sel).user; RecentInlineBots &recent(cRefRecentInlineBots()); int32 index = recent.indexOf(toRemove); if (index >= 0) { @@ -1066,8 +1076,12 @@ void FieldAutocompleteInner::selectByMouse(QPoint globalPosition) { _overDelete = false; } else { sel = mouse.y() / int32(st::mentionHeight); - maxSel = _mrows->isEmpty() ? (_hrows->isEmpty() ? _brows->size() : _hrows->size()) : _mrows->size(); - _overDelete = (!_hrows->isEmpty() || (!_mrows->isEmpty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false; + maxSel = !_mrows->empty() + ? _mrows->size() + : !_hrows->empty() + ? _hrows->size() + : _brows->size(); + _overDelete = (!_hrows->empty() || (!_mrows->empty() && sel < _recentInlineBotsInRows)) ? (mouse.x() >= width() - st::mentionHeight) : false; } if (sel < 0 || sel >= maxSel) { sel = -1; diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h index db7878350..c55cce2fd 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h @@ -28,6 +28,7 @@ class SessionController; namespace Data { class DocumentMedia; +class CloudImageView; } // namespace Data namespace internal { @@ -38,10 +39,21 @@ struct StickerSuggestion { std::unique_ptr animated; }; -using MentionRows = QList; -using HashtagRows = QList; -using BotCommandRows = QList>; +struct MentionRow { + not_null user; + std::shared_ptr userpic; +}; + +struct BotCommandRow { + not_null user; + not_null command; + std::shared_ptr userpic; +}; + +using HashtagRows = std::vector; +using BotCommandRows = std::vector; using StickerRows = std::vector; +using MentionRows = std::vector; class FieldAutocompleteInner; @@ -94,7 +106,7 @@ public: void hideFast(); signals: - void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const; + void mentionChosen(not_null user, FieldAutocomplete::ChooseMethod method) const; void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const; void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const; void stickerChosen(not_null sticker, FieldAutocomplete::ChooseMethod method) const; @@ -182,7 +194,7 @@ public: void rowsUpdated(); signals: - void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const; + void mentionChosen(not_null user, FieldAutocomplete::ChooseMethod method) const; void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const; void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const; void stickerChosen(not_null sticker, FieldAutocomplete::ChooseMethod method) const; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index aa632c02d..dba484ed4 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_channel.h" #include "data/data_file_origin.h" +#include "data/data_cloud_file.h" #include "ui/widgets/buttons.h" #include "ui/effects/animations.h" #include "ui/effects/ripple_animation.h" @@ -89,9 +90,10 @@ struct StickerIcon { mutable std::unique_ptr lottie; mutable QPixmap savedFrame; DocumentData *sticker = nullptr; + ChannelData *megagroup = nullptr; mutable std::shared_ptr thumbnailMedia; mutable std::shared_ptr stickerMedia; - ChannelData *megagroup = nullptr; + mutable std::shared_ptr megagroupUserpic; int pixw = 0; int pixh = 0; mutable rpl::lifetime lifetime; @@ -820,7 +822,7 @@ void StickersListWidget::Footer::paintSetIcon( } } } else if (icon.megagroup) { - icon.megagroup->paintUserpicLeft(p, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); + icon.megagroup->paintUserpicLeft(p, icon.megagroupUserpic, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); } else { const auto paintedIcon = [&] { if (icon.setId == Stickers::FeaturedSetId) { diff --git a/Telegram/SourceFiles/data/data_cloud_file.cpp b/Telegram/SourceFiles/data/data_cloud_file.cpp index 4b17a3cce..cdc969d25 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.cpp +++ b/Telegram/SourceFiles/data/data_cloud_file.cpp @@ -38,6 +38,36 @@ Image *CloudImageView::image() const { return _image.get(); } +void CloudImage::set( + not_null session, + const ImageWithLocation &data) { + const auto &was = _file.location.file().data; + const auto &now = data.location.file().data; + if (!data.location.valid()) { + _file.flags |= CloudFile::Flag::Cancelled; + _file.loader = nullptr; + _file.location = ImageLocation(); + _file.byteSize = 0; + _file.flags = CloudFile::Flag(); + _view = std::weak_ptr(); + } else if (was != now + && (!was.is() || now.is())) { + _file.location = ImageLocation(); + _view = std::weak_ptr(); + } + UpdateCloudFile( + _file, + data, + session->data().cache(), + kImageCacheTag, + [=](FileOrigin origin) { load(session, origin); }, + [=](QImage preloaded) { + if (const auto view = activeView()) { + view->set(session, data.preloaded); + } + }); +} + void CloudImage::update( not_null session, const ImageWithLocation &data) { @@ -103,6 +133,14 @@ std::shared_ptr CloudImage::activeView() { return _view.lock(); } +bool CloudImage::isCurrentView( + const std::shared_ptr &view) const { + if (!view) { + return empty(); + } + return !view.owner_before(_view) && !_view.owner_before(view); +} + void UpdateCloudFile( CloudFile &file, const ImageWithLocation &data, diff --git a/Telegram/SourceFiles/data/data_cloud_file.h b/Telegram/SourceFiles/data/data_cloud_file.h index cde52e5d8..e739fe645 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.h +++ b/Telegram/SourceFiles/data/data_cloud_file.h @@ -57,6 +57,11 @@ public: not_null session, const ImageWithLocation &data); + // This method will replace the location and zero the _view pointer. + void set( + not_null session, + const ImageWithLocation &data); + void update( not_null session, const ImageWithLocation &data); @@ -70,6 +75,8 @@ public: [[nodiscard]] std::shared_ptr createView(); [[nodiscard]] std::shared_ptr activeView(); + [[nodiscard]] bool isCurrentView( + const std::shared_ptr &view) const; private: CloudFile _file; diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index b7222e657..9d461258f 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -248,6 +248,7 @@ void Folder::loadUserpic() { void Folder::paintUserpic( Painter &p, + std::shared_ptr &view, int x, int y, int size) const { diff --git a/Telegram/SourceFiles/data/data_folder.h b/Telegram/SourceFiles/data/data_folder.h index eece5182f..a2442c70b 100644 --- a/Telegram/SourceFiles/data/data_folder.h +++ b/Telegram/SourceFiles/data/data_folder.h @@ -64,6 +64,7 @@ public: void loadUserpic() override; void paintUserpic( Painter &p, + std::shared_ptr &view, int x, int y, int size) const override; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index a29eaed90..7f85f08a1 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/view/history_view_element.h" #include "history/history_item.h" +#include "storage/file_download.h" #include "facades.h" #include "app.h" @@ -108,8 +109,7 @@ void PeerClickHandler::onClick(ClickContext context) const { PeerData::PeerData(not_null owner, PeerId id) : id(id) -, _owner(owner) -, _userpicEmpty(createEmptyUserpic()) { +, _owner(owner) { _nameText.setText(st::msgNameStyle, QString(), Ui::NameTextOptions()); } @@ -145,7 +145,8 @@ void PeerData::updateNameDelayed( } name = newName; _nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions()); - refreshEmptyUserpic(); + _userpicEmpty = nullptr; + Notify::PeerUpdate update(this); if (nameVersion++ > 1) { update.flags |= UpdateFlag::NameChanged; @@ -175,28 +176,22 @@ void PeerData::updateNameDelayed( } } -std::unique_ptr PeerData::createEmptyUserpic() const { - return std::make_unique( - Data::PeerUserpicColor(id), - name); -} - -void PeerData::refreshEmptyUserpic() const { - _userpicEmpty = useEmptyUserpic() ? createEmptyUserpic() : nullptr; +not_null PeerData::ensureEmptyUserpic() const { + if (!_userpicEmpty) { + _userpicEmpty = std::make_unique( + Data::PeerUserpicColor(id), + name); + } + return _userpicEmpty.get(); } ClickHandlerPtr PeerData::createOpenLink() { return std::make_shared(this); } -void PeerData::setUserpic( - PhotoId photoId, - const StorageImageLocation &location, - ImagePtr userpic) { +void PeerData::setUserpic(PhotoId photoId, const ImageLocation &location) { _userpicPhotoId = photoId; - _userpic = userpic; - _userpicLocation = location; - refreshEmptyUserpic(); + _userpic.set(&session(), ImageWithLocation{ .location = location }); } void PeerData::setUserpicPhoto(const MTPPhoto &data) { @@ -213,80 +208,109 @@ void PeerData::setUserpicPhoto(const MTPPhoto &data) { } } -ImagePtr PeerData::currentUserpic() const { - if (_userpic) { - _userpic->load(userpicOrigin()); - if (_userpic->loaded()) { - if (!useEmptyUserpic()) { - _userpicEmpty = nullptr; - } - return _userpic; - } +Image *PeerData::currentUserpic( + std::shared_ptr &view) const { + if (!_userpic.isCurrentView(view)) { + view = _userpic.createView(); + _userpic.load(&session(), userpicOrigin()); } - if (!_userpicEmpty) { - refreshEmptyUserpic(); + const auto image = view ? view->image() : nullptr; + if (image) { + _userpicEmpty = nullptr; } - return ImagePtr(); + return image; } -void PeerData::paintUserpic(Painter &p, int x, int y, int size) const { - if (auto userpic = currentUserpic()) { +void PeerData::paintUserpic( + Painter &p, + std::shared_ptr &view, + int x, + int y, + int size) const { + if (const auto userpic = currentUserpic(view)) { p.drawPixmap(x, y, userpic->pixCircled(userpicOrigin(), size, size)); } else { - _userpicEmpty->paint(p, x, y, x + size + x, size); + ensureEmptyUserpic()->paint(p, x, y, x + size + x, size); } } -void PeerData::paintUserpicRounded(Painter &p, int x, int y, int size) const { - if (auto userpic = currentUserpic()) { +void PeerData::paintUserpicRounded( + Painter &p, + std::shared_ptr &view, + int x, + int y, + int size) const { + if (const auto userpic = currentUserpic(view)) { p.drawPixmap(x, y, userpic->pixRounded(userpicOrigin(), size, size, ImageRoundRadius::Small)); } else { - _userpicEmpty->paintRounded(p, x, y, x + size + x, size); + ensureEmptyUserpic()->paintRounded(p, x, y, x + size + x, size); } } -void PeerData::paintUserpicSquare(Painter &p, int x, int y, int size) const { - if (auto userpic = currentUserpic()) { +void PeerData::paintUserpicSquare( + Painter &p, + std::shared_ptr &view, + int x, + int y, + int size) const { + if (const auto userpic = currentUserpic(view)) { p.drawPixmap(x, y, userpic->pix(userpicOrigin(), size, size)); } else { - _userpicEmpty->paintSquare(p, x, y, x + size + x, size); + ensureEmptyUserpic()->paintSquare(p, x, y, x + size + x, size); } } void PeerData::loadUserpic() { - _userpic->load(userpicOrigin()); + _userpic.load(&session(), userpicOrigin()); } -bool PeerData::userpicLoaded() const { - return _userpic->loaded(); +bool PeerData::hasUserpic() const { + return !_userpic.empty(); } -bool PeerData::useEmptyUserpic() const { - return !_userpicLocation.valid() - || !_userpic - || !_userpic->loaded(); +std::shared_ptr PeerData::activeUserpicView() { + return _userpic.empty() ? nullptr : _userpic.activeView(); } -InMemoryKey PeerData::userpicUniqueKey() const { - if (useEmptyUserpic()) { - if (!_userpicEmpty) { - refreshEmptyUserpic(); - } - return _userpicEmpty->uniqueKey(); +std::shared_ptr PeerData::createUserpicView() { + if (_userpic.empty()) { + return nullptr; } - return inMemoryKey(_userpicLocation); + auto result = _userpic.createView(); + _userpic.load(&session(), userpicPhotoOrigin()); + return result; } -void PeerData::saveUserpic(const QString &path, int size) const { - genUserpic(size).save(path, "PNG"); +bool PeerData::useEmptyUserpic( + std::shared_ptr &view) const { + return !currentUserpic(view); } -void PeerData::saveUserpicRounded(const QString &path, int size) const { - genUserpicRounded(size).save(path, "PNG"); +InMemoryKey PeerData::userpicUniqueKey( + std::shared_ptr &view) const { + return useEmptyUserpic(view) + ? ensureEmptyUserpic()->uniqueKey() + : inMemoryKey(_userpic.location()); } -QPixmap PeerData::genUserpic(int size) const { - if (auto userpic = currentUserpic()) { +void PeerData::saveUserpic( + std::shared_ptr &view, + const QString &path, + int size) const { + genUserpic(view, size).save(path, "PNG"); +} + +void PeerData::saveUserpicRounded( + std::shared_ptr &view, + const QString &path, + int size) const { + genUserpicRounded(view, size).save(path, "PNG"); +} + +QPixmap PeerData::genUserpic( + std::shared_ptr &view, + int size) const { + if (const auto userpic = currentUserpic(view)) { return userpic->pixCircled(userpicOrigin(), size, size); } auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); @@ -294,13 +318,15 @@ QPixmap PeerData::genUserpic(int size) const { result.fill(Qt::transparent); { Painter p(&result); - paintUserpic(p, 0, 0, size); + paintUserpic(p, view, 0, 0, size); } return App::pixmapFromImageInPlace(std::move(result)); } -QPixmap PeerData::genUserpicRounded(int size) const { - if (auto userpic = currentUserpic()) { +QPixmap PeerData::genUserpicRounded( + std::shared_ptr &view, + int size) const { + if (auto userpic = currentUserpic(view)) { return userpic->pixRounded(userpicOrigin(), size, size, ImageRoundRadius::Small); } auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); @@ -308,7 +334,7 @@ QPixmap PeerData::genUserpicRounded(int size) const { result.fill(Qt::transparent); { Painter p(&result); - paintUserpicRounded(p, 0, 0, size); + paintUserpicRounded(p, view, 0, 0, size); } return App::pixmapFromImageInPlace(std::move(result)); } @@ -327,49 +353,42 @@ void PeerData::updateUserpic( PhotoId photoId, MTP::DcId dcId, const MTPFileLocation &location) { - const auto size = kUserpicSize; - const auto loc = location.match([&]( + setUserpicChecked(photoId, location.match([&]( const MTPDfileLocationToBeDeprecated &deprecated) { - return StorageImageLocation( - StorageFileLocation( + return ImageLocation( + { StorageFileLocation( dcId, isSelf() ? peerToUser(id) : 0, MTP_inputPeerPhotoFileLocation( MTP_flags(0), input, deprecated.vvolume_id(), - deprecated.vlocal_id())), - size, - size); - }); - setUserpicChecked(photoId, loc, Images::Create(loc)); + deprecated.vlocal_id())) }, + kUserpicSize, + kUserpicSize); + })); } void PeerData::clearUserpic() { - const auto photoId = PhotoId(0); - const auto loc = StorageImageLocation(); - const auto photo = [&] { - if (isNotificationsUser()) { - auto image = Core::App().logoNoMargin().scaledToWidth( - kUserpicSize, - Qt::SmoothTransformation); - return _userpic - ? _userpic - : Images::Create(std::move(image), "PNG"); - } - return ImagePtr(); - }(); - setUserpicChecked(photoId, loc, photo); + //const auto photo = [&] { // #TODO optimize + // if (isNotificationsUser()) { + // auto image = Core::App().logoNoMargin().scaledToWidth( + // kUserpicSize, + // Qt::SmoothTransformation); + // return _userpic + // ? _userpic + // : Images::Create(std::move(image), "PNG"); + // } + // return ImagePtr(); + //}(); + setUserpicChecked(PhotoId(), ImageLocation()); } void PeerData::setUserpicChecked( PhotoId photoId, - const StorageImageLocation &location, - ImagePtr userpic) { - if (_userpicPhotoId != photoId - || _userpic.get() != userpic.get() - || _userpicLocation != location) { - setUserpic(photoId, location, userpic); + const ImageLocation &location) { + if (_userpicPhotoId != photoId || _userpic.location() != location) { + setUserpic(photoId, location); Notify::peerUpdatedDelayed(this, UpdateFlag::PhotoChanged); //if (const auto channel = asChannel()) { // #feed // if (const auto feed = channel->feed()) { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index c861e856c..e3ab68e64 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_types.h" #include "data/data_flags.h" #include "data/data_notify_settings.h" +#include "data/data_cloud_file.h" class PeerData; class UserData; @@ -43,6 +44,8 @@ using ChatRestrictions = MTPDchatBannedRights::Flags; namespace Data { +class CloudImageView; + class RestrictionCheckResult { public: [[nodiscard]] static RestrictionCheckResult Allowed() { @@ -234,44 +237,59 @@ public: return _nameFirstLetters; } - void setUserpic( - PhotoId photoId, - const StorageImageLocation &location, - ImagePtr userpic); + void setUserpic(PhotoId photoId, const ImageLocation &location); void setUserpicPhoto(const MTPPhoto &data); void paintUserpic( Painter &p, + std::shared_ptr &view, int x, int y, int size) const; void paintUserpicLeft( Painter &p, + std::shared_ptr &view, int x, int y, int w, int size) const { - paintUserpic(p, rtl() ? (w - x - size) : x, y, size); + paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size); } void paintUserpicRounded( Painter &p, + std::shared_ptr &view, int x, int y, int size) const; void paintUserpicSquare( Painter &p, + std::shared_ptr &view, int x, int y, int size) const; void loadUserpic(); - [[nodiscard]] bool userpicLoaded() const; - [[nodiscard]] bool useEmptyUserpic() const; - [[nodiscard]] InMemoryKey userpicUniqueKey() const; - void saveUserpic(const QString &path, int size) const; - void saveUserpicRounded(const QString &path, int size) const; - [[nodiscard]] QPixmap genUserpic(int size) const; - [[nodiscard]] QPixmap genUserpicRounded(int size) const; - [[nodiscard]] StorageImageLocation userpicLocation() const { - return _userpicLocation; + [[nodiscard]] bool hasUserpic() const; + [[nodiscard]] std::shared_ptr activeUserpicView(); + [[nodiscard]] std::shared_ptr createUserpicView(); + [[nodiscard]] bool useEmptyUserpic( + std::shared_ptr &view) const; + [[nodiscard]] InMemoryKey userpicUniqueKey( + std::shared_ptr &view) const; + void saveUserpic( + std::shared_ptr &view, + const QString &path, + int size) const; + void saveUserpicRounded( + std::shared_ptr &view, + const QString &path, + int size) const; + [[nodiscard]] QPixmap genUserpic( + std::shared_ptr &view, + int size) const; + [[nodiscard]] QPixmap genUserpicRounded( + std::shared_ptr &view, + int size) const; + [[nodiscard]] ImageLocation userpicLocation() const { + return _userpic.location(); } [[nodiscard]] bool userpicPhotoUnknown() const { return (_userpicPhotoId == kUnknownPhotoId); @@ -294,7 +312,8 @@ public: return _openLink; } - [[nodiscard]] ImagePtr currentUserpic() const; + [[nodiscard]] Image *currentUserpic( + std::shared_ptr &view) const; [[nodiscard]] bool canPinMessages() const; [[nodiscard]] bool canEditMessagesIndefinitely() const; @@ -356,24 +375,19 @@ protected: private: void fillNames(); - std::unique_ptr createEmptyUserpic() const; - void refreshEmptyUserpic() const; + [[nodiscard]] not_null ensureEmptyUserpic() const; [[nodiscard]] virtual auto unavailableReasons() const -> const std::vector &; - void setUserpicChecked( - PhotoId photoId, - const StorageImageLocation &location, - ImagePtr userpic); + void setUserpicChecked(PhotoId photoId, const ImageLocation &location); static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL); const not_null _owner; - ImagePtr _userpic; + mutable Data::CloudImage _userpic; PhotoId _userpicPhotoId = kUnknownPhotoId; mutable std::unique_ptr _userpicEmpty; - StorageImageLocation _userpicLocation; Ui::Text::String _nameText; Data::NotifySettings _notify; diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 82ea7f61d..237d46497 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -18,6 +18,7 @@ class Session; namespace Data { class Session; class Folder; +class CloudImageView; } // namespace Data namespace Dialogs { @@ -158,16 +159,18 @@ public: virtual void loadUserpic() = 0; virtual void paintUserpic( Painter &p, + std::shared_ptr &view, int x, int y, int size) const = 0; void paintUserpicLeft( Painter &p, + std::shared_ptr &view, int x, int y, int w, int size) const { - paintUserpic(p, rtl() ? (w - x - size) : x, y, size); + paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size); } TimeId chatListTimeId() const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index a1a7d5ba2..9dc59155e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer_values.h" #include "data/data_histories.h" #include "data/data_chat_filters.h" +#include "data/data_cloud_file.h" #include "base/unixtime.h" #include "lang/lang_keys.h" #include "mainwindow.h" @@ -734,7 +735,7 @@ void InnerWidget::paintPeerSearchResult( auto peer = result->peer; auto userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer); - userpicPeer->paintUserpicLeft(p, st::dialogsPadding.x(), st::dialogsPadding.y(), width(), st::dialogsPhotoSize); + userpicPeer->paintUserpicLeft(p, result->row.userpicView(), st::dialogsPadding.x(), st::dialogsPadding.y(), width(), st::dialogsPhotoSize); auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); @@ -810,7 +811,7 @@ void InnerWidget::paintSearchInChat(Painter &p) const { if (peer->isSelf()) { paintSearchInSaved(p, top, _searchInChatText); } else { - paintSearchInPeer(p, peer, top, _searchInChatText); + paintSearchInPeer(p, peer, _searchInChatUserpic, top, _searchInChatText); } //} else if (const auto feed = _searchInChat.feed()) { // #feed // paintSearchInFeed(p, feed, top, fullWidth, _searchInChatText); @@ -821,7 +822,7 @@ void InnerWidget::paintSearchInChat(Painter &p) const { top += st::dialogsSearchInHeight + st::lineWidth; p.setPen(st::dialogsTextFg); p.setTextPalette(st::dialogsSearchFromPalette); - paintSearchInPeer(p, from, top, _searchFromUserText); + paintSearchInPeer(p, from, _searchFromUserUserpic, top, _searchFromUserText); p.restoreTextPalette(); } } @@ -866,10 +867,11 @@ void InnerWidget::paintSearchInFilter( void InnerWidget::paintSearchInPeer( Painter &p, not_null peer, + std::shared_ptr &userpic, int top, const Ui::Text::String &text) const { const auto paintUserpic = [&](Painter &p, int x, int y, int size) { - peer->paintUserpicLeft(p, x, y, width(), size); + peer->paintUserpicLeft(p, userpic, x, y, width(), size); }; const auto icon = Layout::ChatTypeIcon(peer, false, false); paintSearchInFilter(p, paintUserpic, top, icon, text); @@ -2334,9 +2336,18 @@ void InnerWidget::searchInChat(Key key, UserData *from) { } if (_searchFromUser) { _cancelSearchFromUser->show(); + _searchFromUserUserpic = _searchFromUser->createUserpicView(); } else { _cancelSearchFromUser->hide(); + _searchFromUserUserpic = nullptr; } + + if (const auto peer = _searchInChat.peer()) { + _searchInChatUserpic = peer->createUserpicView(); + } else { + _searchInChatUserpic = nullptr; + } + _controller->dialogsListDisplayForced().set( _searchInChat || !_filter.isEmpty(), true); diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index 2618d0ada..93552c71e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -33,6 +33,10 @@ namespace Notify { struct PeerUpdate; } // namespace Notify +namespace Data { +class CloudImageView; +} // namespace Data + namespace Dialogs { class Row; @@ -283,6 +287,7 @@ private: void paintSearchInPeer( Painter &p, not_null peer, + std::shared_ptr &userpic, int top, const Ui::Text::String &text) const; void paintSearchInSaved( @@ -394,6 +399,8 @@ private: Key _searchInChat; History *_searchInMigrated = nullptr; UserData *_searchFromUser = nullptr; + mutable std::shared_ptr _searchInChatUserpic; + mutable std::shared_ptr _searchFromUserUserpic; Ui::Text::String _searchInChatText; Ui::Text::String _searchFromUserText; RowDescriptor _menuRow; diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index e424757c9..09b85112f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -273,6 +273,7 @@ void paintRow( } else { entry->paintUserpicLeft( p, + row->userpicView(), st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth, @@ -946,6 +947,7 @@ void PaintCollapsedRow( } else { folder->paintUserpicLeft( p, + row.userpicView(), (fullWidth - st::dialogsUnreadHeight) / 2, unreadTop, fullWidth, diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index f08568d9c..1e26442ad 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -138,12 +138,14 @@ void BasicRow::ensureOnlineUserpic() const { void BasicRow::PaintOnlineFrame( not_null data, - not_null peer) { + not_null peer, + std::shared_ptr &view) { data->frame.fill(Qt::transparent); Painter q(&data->frame); peer->paintUserpic( q, + view, 0, 0, st::dialogsPhotoSize); @@ -185,6 +187,7 @@ void BasicRow::paintUserpic( if (!allowOnline || online == 0.) { peer->paintUserpicLeft( p, + _userpic, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth, @@ -202,14 +205,14 @@ void BasicRow::paintUserpic( QImage::Format_ARGB32_Premultiplied); _onlineUserpic->frame.setDevicePixelRatio(cRetinaFactor()); } - const auto key = peer->userpicUniqueKey(); + const auto key = peer->userpicUniqueKey(_userpic); if (_onlineUserpic->online != online || _onlineUserpic->key != key || _onlineUserpic->active != active) { _onlineUserpic->online = online; _onlineUserpic->key = key; _onlineUserpic->active = active; - PaintOnlineFrame(_onlineUserpic.get(), peer); + PaintOnlineFrame(_onlineUserpic.get(), peer, _userpic); } p.drawImage(st::dialogsPadding, _onlineUserpic->frame); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index c717fd202..6871a38f0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; class HistoryItem; +namespace Data { +class CloudImageView; +} // namespace Data + namespace Ui { class RippleAnimation; } // namespace Ui @@ -48,6 +52,10 @@ public: int outerWidth, const QColor *colorOverride = nullptr) const; + std::shared_ptr &userpicView() const { + return _userpic; + } + private: struct OnlineUserpic { InMemoryKey key; @@ -60,8 +68,10 @@ private: void ensureOnlineUserpic() const; static void PaintOnlineFrame( not_null data, - not_null peer); + not_null peer, + std::shared_ptr &view); + mutable std::shared_ptr _userpic; mutable std::unique_ptr _ripple; mutable std::unique_ptr _onlineUserpic; mutable bool _online = false; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp index 5b7578ab5..d78e68c49 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp @@ -17,6 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_boxes.h" +namespace Data { +class CloudImageView; +} // namespace Data + namespace AdminLog { namespace { @@ -59,7 +63,8 @@ private: QRect _checkRect; - not_null _user; + const not_null _user; + std::shared_ptr _userpic; QString _statusText; bool _statusOnline = false; @@ -113,7 +118,7 @@ void UserCheckbox::paintEvent(QPaintEvent *e) { auto userpicLeft = _checkRect.x() + _checkRect.width() + st::adminLogFilterUserpicLeft; auto userpicTop = 0; - _user->paintUserpicLeft(p, userpicLeft, userpicTop, width(), st::contactsPhotoSize); + _user->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, width(), st::contactsPhotoSize); auto nameLeft = userpicLeft + st::contactsPhotoSize + st::contactsPadding.left(); auto nameTop = userpicTop + st::contactsNameTop; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index fdcbaff9e..a7ce8a355 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_media_types.h" #include "data/data_file_origin.h" +#include "data/data_cloud_file.h" #include "data/data_channel.h" #include "data/data_user.h" #include "facades.h" @@ -59,6 +60,7 @@ constexpr auto kMaxChannelAdmins = 200; constexpr auto kScrollDateHideTimeout = 1000; constexpr auto kEventsFirstPage = 20; constexpr auto kEventsPerPage = 50; +constexpr auto kClearUserpicsAfter = 50; } // namespace @@ -311,6 +313,11 @@ void InnerWidget::visibleTopBottomUpdated( _visibleTop = visibleTop; _visibleBottom = visibleBottom; + // Unload userpics. + if (_userpics.size() > kClearUserpicsAfter) { + _userpicsCache = std::move(_userpics); + } + updateVisibleTopItem(); checkPreloadMore(); if (scrolledUp) { @@ -813,6 +820,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) { return; } + const auto guard = gsl::finally([&] { + _userpicsCache.clear(); + }); + Painter p(this); auto ms = crl::now(); @@ -855,7 +866,14 @@ void InnerWidget::paintEvent(QPaintEvent *e) { const auto message = view->data()->toHistoryMessage(); Assert(message != nullptr); - message->from()->paintUserpicLeft(p, st::historyPhotoLeft, userpicTop, view->width(), st::msgPhotoSize); + const auto from = message->from(); + from->paintUserpicLeft( + p, + _userpics[from], + st::historyPhotoLeft, + userpicTop, + view->width(), + st::msgPhotoSize); } return true; }); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index dd00546d5..3d2d228e4 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -16,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/sender.h" #include "base/timer.h" +namespace Data { +class CloudImageView; +} // namespace Data + namespace Main { class Session; } // namespace Main @@ -230,6 +234,9 @@ private: std::set _eventIds; std::map, not_null> _itemsByData; base::flat_set _animatedStickersPlayed; + base::flat_map< + not_null, + std::shared_ptr> _userpics, _userpicsCache; int _itemsTop = 0; int _itemsWidth = 0; int _itemsHeight = 0; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 9dc9d4056..e8fee9386 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -2218,10 +2218,11 @@ void History::loadUserpic() { void History::paintUserpic( Painter &p, + std::shared_ptr &view, int x, int y, int size) const { - peer->paintUserpic(p, x, y, size); + peer->paintUserpic(p, view, x, y, size); } void History::startBuildingFrontBlock(int expectedItemsCount) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index adfa3d605..0687f305b 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -356,6 +356,7 @@ public: void loadUserpic() override; void paintUserpic( Painter &p, + std::shared_ptr &view, int x, int y, int size) const override; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index ead566496..f437c792a 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -68,6 +68,7 @@ namespace { constexpr auto kScrollDateHideTimeout = 1000; constexpr auto kUnloadHeavyPartsPages = 2; +constexpr auto kClearUserpicsAfter = 50; // Helper binary search for an item in a list that is not completely // above the given top of the visible area or below the given bottom of the visible area @@ -566,6 +567,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) { return; } + const auto guard = gsl::finally([&] { + _userpicsCache.clear(); + }); + Painter p(this); auto clip = e->rect(); auto ms = crl::now(); @@ -731,6 +736,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { if (const auto from = message->displayFrom()) { from->paintUserpicLeft( p, + _userpics[from], st::historyPhotoLeft, userpicTop, width(), @@ -2234,6 +2240,11 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) { scrollDateHideByTimer(); } + // Unload userpics. + if (_userpics.size() > kClearUserpicsAfter) { + _userpicsCache = std::move(_userpics); + } + // Unload lottie animations. const auto pages = kUnloadHeavyPartsPages; const auto from = _visibleAreaTop - pages * visibleAreaHeight; diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index f1dfcc312..f66dc9c4c 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { struct Group; +class CloudImageView; } // namespace Data namespace HistoryView { @@ -345,6 +346,9 @@ private: SelectedItems _selected; base::flat_set> _animatedStickersPlayed; + base::flat_map< + not_null, + std::shared_ptr> _userpics, _userpicsCache; MouseAction _mouseAction = MouseAction::None; TextSelectType _mouseSelectType = TextSelectType::Letters; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 74be742ed..ecb227f20 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -380,7 +380,9 @@ HistoryWidget::HistoryWidget( InitMessageField(controller, _field); _fieldAutocomplete->hide(); - connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*))); + connect(_fieldAutocomplete, &FieldAutocomplete::mentionChosen, this, [=](not_null user) { + onMentionInsert(user); + }); connect(_fieldAutocomplete, SIGNAL(hashtagChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); connect(_fieldAutocomplete, SIGNAL(botCommandChosen(QString,FieldAutocomplete::ChooseMethod)), this, SLOT(onHashtagOrBotCommandInsert(QString,FieldAutocomplete::ChooseMethod))); connect(_fieldAutocomplete, &FieldAutocomplete::stickerChosen, this, [=](not_null document) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index cc733a7b6..2cc353ca8 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -50,6 +50,7 @@ constexpr auto kPreloadedScreensCount = 4; constexpr auto kPreloadIfLessThanScreens = 2; constexpr auto kPreloadedScreensCountFull = kPreloadedScreensCount + 1 + kPreloadedScreensCount; +constexpr auto kClearUserpicsAfter = 50; } // namespace @@ -583,6 +584,11 @@ void ListWidget::visibleTopBottomUpdated( _visibleTop = visibleTop; _visibleBottom = visibleBottom; + // Unload userpics. + if (_userpics.size() > kClearUserpicsAfter) { + _userpicsCache = std::move(_userpics); + } + if (initializing) { checkUnreadBarCreation(); } @@ -1305,6 +1311,10 @@ void ListWidget::paintEvent(QPaintEvent *e) { return; } + const auto guard = gsl::finally([&] { + _userpicsCache.clear(); + }); + Painter p(this); auto ms = crl::now(); @@ -1346,6 +1356,7 @@ void ListWidget::paintEvent(QPaintEvent *e) { if (const auto from = message->displayFrom()) { from->paintUserpicLeft( p, + _userpics[from], st::historyPhotoLeft, userpicTop, view->width(), diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 549ebbf4e..18e6d4dd5 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -29,6 +29,7 @@ class SessionController; namespace Data { struct Group; +class CloudImageView; } // namespace Data namespace HistoryView { @@ -454,6 +455,9 @@ private: int _itemsHeight = 0; int _itemAverageHeight = 0; base::flat_set _animatedStickersPlayed; + base::flat_map< + not_null, + std::shared_ptr> _userpics, _userpicsCache; int _minHeight = 0; int _visibleTop = 0; diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp index 7b6cf07f6..4ff76fb0b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "data/data_media_types.h" +#include "data/data_cloud_file.h" #include "main/main_session.h" #include "app.h" #include "styles/style_history.h" @@ -165,7 +166,11 @@ void Contact::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim QRect rthumb(style::rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, paintw)); if (_contact) { - _contact->paintUserpic(p, rthumb.x(), rthumb.y(), st::msgFileThumbSize); + const auto was = (_userpic != nullptr); + _contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st::msgFileThumbSize); + if (!was && _userpic) { + history()->owner().registerHeavyViewPart(_parent); + } } else { _photoEmpty->paint(p, st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, paintw, st::msgFileThumbSize); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_contact.h b/Telegram/SourceFiles/history/view/media/history_view_contact.h index 58c96aaac..6dce7c858 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_contact.h +++ b/Telegram/SourceFiles/history/view/media/history_view_contact.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" +namespace Data { +class CloudImageView; +} // namespace Data + namespace Ui { class EmptyUserpic; } // namespace Ui @@ -55,6 +59,14 @@ public: // Should be called only by Data::Session. void updateSharedContactUserId(UserId userId) override; + void unloadHeavyPart() override { + _userpic = nullptr; + } + + bool hasHeavyPart() const override { + return (_userpic != nullptr); + } + private: QSize countOptimalSize() override; @@ -65,6 +77,7 @@ private: QString _fname, _lname, _phone; Ui::Text::String _name; std::unique_ptr _photoEmpty; + mutable std::shared_ptr _userpic; ClickHandlerPtr _linkl; int _linkw = 0; diff --git a/Telegram/SourceFiles/history/view/media/history_view_location.cpp b/Telegram/SourceFiles/history/view/media/history_view_location.cpp index 7645bfdc7..d4b9ea0b7 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_location.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_location.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "ui/image/image.h" #include "ui/text_options.h" +#include "data/data_session.h" #include "data/data_file_origin.h" #include "data/data_cloud_file.h" #include "app.h" @@ -56,6 +57,7 @@ void Location::ensureMediaCreated() const { } _media = _data->createView(); _data->load(&history()->session(), _parent->data()->fullId()); + history()->owner().registerHeavyViewPart(_parent); } QSize Location::countOptimalSize() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index 292c36670..95e04b7cf 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -479,9 +479,17 @@ void Poll::updateRecentVoters() { auto &&sliced = ranges::view::all( _poll->recentVoters ) | ranges::view::take(kShowRecentVotersCount); - const auto changed = !ranges::equal(_recentVoters, sliced); + const auto changed = !ranges::equal( + _recentVoters, + sliced, + ranges::equal_to(), + &RecentVoter::user); if (changed) { - _recentVoters = sliced | ranges::to_vector; + _recentVoters = ranges::view::all( + sliced + ) | ranges::views::transform([](not_null user) { + return RecentVoter{ user }; + }) | ranges::to_vector; } } @@ -858,14 +866,23 @@ void Poll::paintRecentVoters( ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg))->p; pen.setWidth(st::lineWidth); - for (const auto &recent : _recentVoters) { - recent->paintUserpic(p, x, y, size); + + auto created = false; + for (auto &recent : _recentVoters) { + const auto was = (recent.userpic != nullptr); + recent.user->paintUserpic(p, recent.userpic, x, y, size); + if (!was && recent.userpic) { + created = true; + } p.setPen(pen); p.setBrush(Qt::NoBrush); PainterHighQualityEnabler hq(p); p.drawEllipse(x, y, size, size); x -= st::historyPollRecentVoterSkip; } + if (created) { + history()->owner().registerHeavyViewPart(_parent); + } } void Poll::paintCloseByTimer( @@ -1401,6 +1418,21 @@ void Poll::clickHandlerPressedChanged( } } +void Poll::unloadHeavyPart() { + for (auto &recent : _recentVoters) { + recent.userpic = nullptr; + } +} + +bool Poll::hasHeavyPart() const { + for (auto &recent : _recentVoters) { + if (recent.userpic) { + return true; + } + } + return false; +} + void Poll::toggleRipple(Answer &answer, bool pressed) { if (pressed) { const auto outerWidth = width(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index c3e9b2c37..d9eea821a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "base/weak_ptr.h" +namespace Data { +class CloudImageView; +} // namespace Data + namespace Ui { class RippleAnimation; class FireworksAnimation; @@ -54,6 +58,9 @@ public: const ClickHandlerPtr &handler, bool pressed) override; + void unloadHeavyPart() override; + bool hasHeavyPart() const override; + private: struct AnswerAnimation; struct AnswersAnimation; @@ -61,6 +68,11 @@ private: struct Answer; struct CloseInformation; + struct RecentVoter { + not_null user; + mutable std::shared_ptr userpic; + }; + QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; @@ -186,7 +198,7 @@ private: Ui::Text::String _question; Ui::Text::String _subtitle; - std::vector> _recentVoters; + std::vector _recentVoters; QImage _recentVotersImage; std::vector _answers; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 2db6b7bc6..625671813 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2743,13 +2743,12 @@ void OverlayWidget::validatePhotoCurrentImage() { validatePhotoImage(_photoMedia->image(Data::PhotoSize::Small), true); validatePhotoImage(_photoMedia->thumbnailInline(), true); if (_staticContent.isNull() - && _peer && !_msgid - && _peer->userpicLoaded() - && _peer->userpicLocation().file().valid()) { - validatePhotoImage( - Images::Create(_peer->userpicLocation()).get(), - true); + && _peer + && _peer->hasUserpic()) { + if (const auto view = _peer->activeUserpicView()) { + validatePhotoImage(view->image(), true); + } } if (_staticContent.isNull()) { _photoMedia->wanted(Data::PhotoSize::Small, fileOrigin()); diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index 457f2e02c..285781264 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -461,7 +461,9 @@ bool Manager::Private::showNotification( const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - if (!_notificationManager || !_notifier || !_notificationFactory) return false; + if (!_notificationManager || !_notifier || !_notificationFactory) { + return false; + } ComPtr toastXml; bool withSubtitle = !subtitle.isEmpty(); @@ -476,9 +478,10 @@ bool Manager::Private::showNotification( hr = SetAudioSilent(toastXml.Get()); if (!SUCCEEDED(hr)) return false; + auto view = std::shared_ptr(); // #TODO optimize const auto key = hideNameAndPhoto ? InMemoryKey() - : peer->userpicUniqueKey(); + : peer->userpicUniqueKey(view); const auto userpicPath = _cachedUserpics.get(key, peer); const auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString(); diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index b859f1b46..ff778d45b 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -32,11 +32,11 @@ using UpdateFlag = Notify::PeerUpdate::Flag; } // namespace -GroupMembersWidget::Member::Member(UserData *user) : Item(user) { +GroupMembersWidget::Member::Member(not_null user) : Item(user) { } -UserData *GroupMembersWidget::Member::user() const { - return static_cast(peer); +not_null GroupMembersWidget::Member::user() const { + return static_cast(peer.get()); } GroupMembersWidget::GroupMembersWidget( diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.h b/Telegram/SourceFiles/profile/profile_block_group_members.h index a00d6b7e0..d3ab35219 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.h +++ b/Telegram/SourceFiles/profile/profile_block_group_members.h @@ -55,8 +55,8 @@ private: void refreshUserOnline(UserData *user); struct Member : public Item { - explicit Member(UserData *user); - UserData *user() const; + explicit Member(not_null user); + not_null user() const; TimeId onlineTextTill = 0; TimeId onlineTill = 0; diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index a0545df1d..382635504 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -10,13 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/ripple_animation.h" #include "ui/text_options.h" #include "data/data_peer.h" +#include "data/data_cloud_file.h" #include "main/main_session.h" #include "styles/style_profile.h" #include "styles/style_widgets.h" namespace Profile { -PeerListWidget::Item::Item(PeerData *peer) : peer(peer) { +PeerListWidget::Item::Item(not_null peer) : peer(peer) { } PeerListWidget::Item::~Item() = default; @@ -84,7 +85,7 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select } int skip = _st.photoPosition.x(); - item->peer->paintUserpicLeft(p, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize); + item->peer->paintUserpicLeft(p, item->userpic, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize); if (item->name.isEmpty()) { item->name.setText( diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.h b/Telegram/SourceFiles/profile/profile_block_peer_list.h index 3d95a9f67..0d03ce8f8 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.h +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "profile/profile_block_widget.h" +namespace Data { +class CloudImageView; +} // namespace Data + namespace Ui { class RippleAnimation; class PopupMenu; @@ -29,10 +33,11 @@ public: PeerListWidget(QWidget *parent, PeerData *peer, const QString &title, const style::PeerListItem &st, const QString &removeText); struct Item { - explicit Item(PeerData *peer); + explicit Item(not_null peer); ~Item(); - PeerData * const peer; + const not_null peer; + std::shared_ptr userpic; Ui::Text::String name; QString statusText; bool statusHasOnlineColor = false; diff --git a/Telegram/SourceFiles/storage/serialize_common.cpp b/Telegram/SourceFiles/storage/serialize_common.cpp index d8d38fe0c..5637b2cb4 100644 --- a/Telegram/SourceFiles/storage/serialize_common.cpp +++ b/Telegram/SourceFiles/storage/serialize_common.cpp @@ -127,7 +127,7 @@ std::optional readImageLocation( uint32 peerSize(not_null peer) { uint32 result = sizeof(quint64) + sizeof(quint64) - + storageImageLocationSize(peer->userpicLocation()); + + imageLocationSize(peer->userpicLocation()); if (peer->isUser()) { UserData *user = peer->asUser(); @@ -157,7 +157,7 @@ uint32 peerSize(not_null peer) { void writePeer(QDataStream &stream, PeerData *peer) { stream << quint64(peer->id) << quint64(peer->userpicPhotoId()); - writeStorageImageLocation(stream, peer->userpicLocation()); + writeImageLocation(stream, peer->userpicLocation()); if (const auto user = peer->asUser()) { stream << user->firstName @@ -207,7 +207,7 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) { return nullptr; } - const auto userpic = readStorageImageLocation(streamAppVersion, stream); + const auto userpic = readImageLocation(streamAppVersion, stream); auto userpicAccessHash = uint64(0); if (!userpic) { return nullptr; @@ -324,14 +324,13 @@ PeerData *readPeer(int streamAppVersion, QDataStream &stream) { } if (!loaded) { using LocationType = StorageFileLocation::Type; - const auto location = (userpic->valid() - && userpic->type() == LocationType::Legacy) + const auto location = (userpic->valid() && userpic->isLegacy()) ? userpic->convertToModern( LocationType::PeerPhoto, result->id, userpicAccessHash) : *userpic; - result->setUserpic(photoId, location, Images::Create(location)); + result->setUserpic(photoId, location); } return result; } diff --git a/Telegram/SourceFiles/ui/image/image_location.cpp b/Telegram/SourceFiles/ui/image/image_location.cpp index 68401c590..1b6553a86 100644 --- a/Telegram/SourceFiles/ui/image/image_location.cpp +++ b/Telegram/SourceFiles/ui/image/image_location.cpp @@ -590,6 +590,49 @@ InMemoryKey inMemoryKey(const StorageFileLocation &location) { return { key.high, key.low }; } +InMemoryKey inMemoryKey(const WebFileLocation &location) { + auto result = InMemoryKey(); + const auto &url = location.url(); + const auto sha = hashSha1(url.data(), url.size()); + bytes::copy( + bytes::object_as_span(&result), + bytes::make_span(sha).subspan(0, sizeof(result))); + return result; +} + +InMemoryKey inMemoryKey(const GeoPointLocation &location) { + return InMemoryKey( + (uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32) + | uint64(std::round(std::abs(location.lon + 360.) * 1000000)), + (uint64(location.width) << 32) | uint64(location.height)); +} + +InMemoryKey inMemoryKey(const PlainUrlLocation &location) { + auto result = InMemoryKey(); + const auto &url = location.url; + const auto sha = hashSha1(url.data(), url.size() * sizeof(QChar)); + bytes::copy( + bytes::object_as_span(&result), + bytes::make_span(sha).subspan(0, sizeof(result))); + return result; +} + +InMemoryKey inMemoryKey(const InMemoryLocation &location) { + auto result = InMemoryKey(); + const auto &data = location.bytes; + const auto sha = hashSha1(data.data(), data.size()); + bytes::copy( + bytes::object_as_span(&result), + bytes::make_span(sha).subspan(0, sizeof(result))); + return result; +} + +InMemoryKey inMemoryKey(const DownloadLocation &location) { + return location.data.match([](const auto &data) { + return inMemoryKey(data); + }); +} + StorageImageLocation::StorageImageLocation( const StorageFileLocation &file, int width, diff --git a/Telegram/SourceFiles/ui/image/image_location.h b/Telegram/SourceFiles/ui/image/image_location.h index bbd2c84eb..a041e2818 100644 --- a/Telegram/SourceFiles/ui/image/image_location.h +++ b/Telegram/SourceFiles/ui/image/image_location.h @@ -96,7 +96,6 @@ public: [[nodiscard]] static const StorageFileLocation &Invalid(); -private: friend bool operator==( const StorageFileLocation &a, const StorageFileLocation &b); @@ -104,6 +103,7 @@ private: const StorageFileLocation &a, const StorageFileLocation &b); +private: uint16 _dcId = 0; Type _type = Type::Legacy; uint8 _sizeLetter = 0; @@ -203,7 +203,6 @@ public: return result; } -private: friend inline bool operator==( const StorageImageLocation &a, const StorageImageLocation &b) { @@ -215,6 +214,7 @@ private: return (a._file < b._file); } +private: StorageFileLocation _file; int _width = 0; int _height = 0; @@ -381,6 +381,30 @@ struct PlainUrlLocation { } }; +inline bool operator!=( + const PlainUrlLocation &a, + const PlainUrlLocation &b) { + return !(a == b); +} + +inline bool operator>( + const PlainUrlLocation &a, + const PlainUrlLocation &b) { + return (b < a); +} + +inline bool operator<=( + const PlainUrlLocation &a, + const PlainUrlLocation &b) { + return !(b < a); +} + +inline bool operator>=( + const PlainUrlLocation &a, + const PlainUrlLocation &b) { + return !(a < b); +} + struct InMemoryLocation { QByteArray bytes; @@ -396,6 +420,30 @@ struct InMemoryLocation { } }; +inline bool operator!=( + const InMemoryLocation &a, + const InMemoryLocation &b) { + return !(a == b); +} + +inline bool operator>( + const InMemoryLocation &a, + const InMemoryLocation &b) { + return (b < a); +} + +inline bool operator<=( + const InMemoryLocation &a, + const InMemoryLocation &b) { + return !(b < a); +} + +inline bool operator>=( + const InMemoryLocation &a, + const InMemoryLocation &b) { + return !(a < b); +} + class DownloadLocation { public: base::variant< @@ -423,7 +471,6 @@ public: bool refreshFileReference(const QByteArray &data); bool refreshFileReference(const Data::UpdatedFileReferences &updates); -private: friend inline bool operator==( const DownloadLocation &a, const DownloadLocation &b) { @@ -437,6 +484,30 @@ private: }; +inline bool operator!=( + const DownloadLocation &a, + const DownloadLocation &b) { + return !(a == b); +} + +inline bool operator>( + const DownloadLocation &a, + const DownloadLocation &b) { + return (b < a); +} + +inline bool operator<=( + const DownloadLocation &a, + const DownloadLocation &b) { + return !(b < a); +} + +inline bool operator>=( + const DownloadLocation &a, + const DownloadLocation &b) { + return !(a < b); +} + class ImageLocation { public: ImageLocation() = default; @@ -496,7 +567,6 @@ public: return result; } -private: friend inline bool operator==( const ImageLocation &a, const ImageLocation &b) { @@ -508,12 +578,37 @@ private: return (a._file < b._file); } +private: DownloadLocation _file; int _width = 0; int _height = 0; }; +inline bool operator!=( + const ImageLocation &a, + const ImageLocation &b) { + return !(a == b); +} + +inline bool operator>( + const ImageLocation &a, + const ImageLocation &b) { + return (b < a); +} + +inline bool operator<=( + const ImageLocation &a, + const ImageLocation &b) { + return !(b < a); +} + +inline bool operator>=( + const ImageLocation &a, + const ImageLocation &b) { + return !(a < b); +} + struct ImageWithLocation { ImageLocation location; int bytesCount = 0; @@ -543,21 +638,14 @@ inline InMemoryKey inMemoryKey(const StorageImageLocation &location) { return inMemoryKey(location.file()); } -inline InMemoryKey inMemoryKey(const WebFileLocation &location) { - auto result = InMemoryKey(); - const auto &url = location.url(); - const auto sha = hashSha1(url.data(), url.size()); - bytes::copy( - bytes::object_as_span(&result), - bytes::make_span(sha).subspan(0, sizeof(result))); - return result; -} +InMemoryKey inMemoryKey(const WebFileLocation &location); +InMemoryKey inMemoryKey(const GeoPointLocation &location); +InMemoryKey inMemoryKey(const PlainUrlLocation &location); +InMemoryKey inMemoryKey(const InMemoryLocation &location); +InMemoryKey inMemoryKey(const DownloadLocation &location); -inline InMemoryKey inMemoryKey(const GeoPointLocation &location) { - return InMemoryKey( - (uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32) - | uint64(std::round(std::abs(location.lon + 360.) * 1000000)), - (uint64(location.width) << 32) | uint64(location.height)); +inline InMemoryKey inMemoryKey(const ImageLocation &location) { + return inMemoryKey(location.file()); } inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) { diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index 1b6e84b17..cd98e6ca3 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_folder.h" #include "data/data_channel.h" +#include "data/data_cloud_file.h" #include "history/history.h" #include "core/file_utilities.h" #include "core/application.h" @@ -633,15 +634,17 @@ void UserpicButton::setupPeerViewers() { Notify::PeerUpdateViewer( _peer, Notify::PeerUpdate::Flag::PhotoChanged - ) | rpl::start_with_next([this] { + ) | rpl::start_with_next([=] { processNewPeerPhoto(); update(); }, lifetime()); base::ObservableViewer( _peer->session().downloaderTaskFinished() - ) | rpl::start_with_next([this] { - if (_waiting && _peer->userpicLoaded()) { + ) | rpl::filter([=] { + return _waiting; + }) | rpl::start_with_next([=] { + if (!_userpicView || _userpicView->image()) { _waiting = false; startNewPhotoShowing(); } @@ -776,7 +779,8 @@ QPoint UserpicButton::prepareRippleStartPosition() const { void UserpicButton::processPeerPhoto() { Expects(_peer != nullptr); - _waiting = !_peer->userpicLoaded(); + _userpicView = _peer->createUserpicView(); + _waiting = _userpicView && !_userpicView->image(); if (_waiting) { _peer->loadUserpic(); } @@ -951,17 +955,17 @@ void UserpicButton::prepareUserpicPixmap() { p.drawEllipse(0, 0, size, size); }; _userpicHasImage = _peer - ? (_peer->currentUserpic() || _role != Role::ChangePhoto) + ? (_peer->currentUserpic(_userpicView) || _role != Role::ChangePhoto) : false; _userpic = CreateSquarePixmap(size, [&](Painter &p) { if (_userpicHasImage) { - _peer->paintUserpic(p, 0, 0, _st.photoSize); + _peer->paintUserpic(p, _userpicView, 0, 0, _st.photoSize); } else { paintButton(p, _st.changeButton.textBg); } }); _userpicUniqueKey = _userpicHasImage - ? _peer->userpicUniqueKey() + ? _peer->userpicUniqueKey(_userpicView) : InMemoryKey(); } // // #feed diff --git a/Telegram/SourceFiles/ui/special_buttons.h b/Telegram/SourceFiles/ui/special_buttons.h index f69476787..6f2cca86c 100644 --- a/Telegram/SourceFiles/ui/special_buttons.h +++ b/Telegram/SourceFiles/ui/special_buttons.h @@ -15,9 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class PeerData; -namespace Ui { -class InfiniteRadialAnimation; -} // namespace Ui +namespace Data { +class CloudImageView; +} // namespace Data namespace Window { class SessionController; @@ -25,6 +25,8 @@ class SessionController; namespace Ui { +class InfiniteRadialAnimation; + class HistoryDownButton : public RippleButton { public: HistoryDownButton(QWidget *parent, const style::TwoIconButton &st); @@ -220,6 +222,7 @@ private: const style::UserpicButton &_st; Window::SessionController *_controller = nullptr; PeerData *_peer = nullptr; + std::shared_ptr _userpicView; QString _cropTitle; Role _role = Role::ChangePhoto; bool _notShownYet = true; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index f3349d8a8..32eb32dbb 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -536,9 +536,10 @@ Notification::Notification( int shift, Direction shiftDirection) : Widget(manager, startPosition, shift, shiftDirection) +, _peer(peer) , _started(crl::now()) , _history(history) -, _peer(peer) +, _userpicView(_peer->createUserpicView()) , _author(author) , _item(item) , _forwardedCount(forwardedCount) @@ -550,7 +551,7 @@ Notification::Notification( auto position = computePosition(st::notifyMinHeight); updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight); - _userpicLoaded = _peer ? _peer->userpicLoaded() : true; + _userpicLoaded = (_userpicView->image() != nullptr); updateNotifyDisplay(); _hideTimer.setSingleShot(true); @@ -674,7 +675,7 @@ void Notification::actionsOpacityCallback() { } void Notification::updateNotifyDisplay() { - if (!_history || !_peer || (!_item && _forwardedCount < 2)) return; + if (!_history || (!_item && _forwardedCount < 2)) return; const auto options = Manager::getNotificationOptions(_item); _hideReplyButton = options.hideReplyButton; @@ -696,8 +697,9 @@ void Notification::updateNotifyDisplay() { Ui::EmptyUserpic::PaintSavedMessages(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); _userpicLoaded = true; } else { + _userpicView = _history->peer->createUserpicView(); _history->peer->loadUserpic(); - _history->peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); + _history->peer->paintUserpicLeft(p, _userpicView, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); } } else { p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), manager()->hiddenUserpicPlaceholder()); @@ -792,7 +794,11 @@ void Notification::updateNotifyDisplay() { } void Notification::updatePeerPhoto() { - if (_userpicLoaded || !_peer || !_peer->userpicLoaded()) { + if (_userpicLoaded) { + return; + } + _userpicView = _peer->createUserpicView(); + if (_userpicView && !_userpicView->image()) { return; } _userpicLoaded = true; @@ -800,9 +806,16 @@ void Notification::updatePeerPhoto() { auto img = _cache.toImage(); { Painter p(&img); - _peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); + _peer->paintUserpicLeft( + p, + _userpicView, + st::notifyPhotoPos.x(), + st::notifyPhotoPos.y(), + width(), + st::notifyPhotoSize); } _cache = App::pixmapFromImageInPlace(std::move(img)); + _userpicView = nullptr; update(); } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 43179b691..06e2d5af3 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -16,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include +namespace Data { +class CloudImageView; +} // namespace Data + namespace Ui { class IconButton; class RoundButton; @@ -232,6 +236,8 @@ private: void updateGeometry(int x, int y, int width, int height) override; void actionsOpacityCallback(); + const not_null _peer; + QPixmap _cache; bool _hideReplyButton = false; @@ -242,7 +248,7 @@ private: crl::time _started; History *_history = nullptr; - PeerData *_peer = nullptr; + std::shared_ptr _userpicView; QString _author; HistoryItem *_item = nullptr; int _forwardedCount = 0; diff --git a/Telegram/SourceFiles/window/notifications_utilities.cpp b/Telegram/SourceFiles/window/notifications_utilities.cpp index 43faaf1dd..d32e72ee6 100644 --- a/Telegram/SourceFiles/window/notifications_utilities.cpp +++ b/Telegram/SourceFiles/window/notifications_utilities.cpp @@ -46,15 +46,16 @@ QString CachedUserpics::get(const InMemoryKey &key, PeerData *peer) { } v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value(), 16) + qsl(".png"); if (key.first || key.second) { + auto userpic = std::shared_ptr(); // #TODO optimize userpic if (peer->isSelf()) { - const auto method = _type == Type::Rounded + const auto method = (_type == Type::Rounded) ? Ui::EmptyUserpic::GenerateSavedMessagesRounded : Ui::EmptyUserpic::GenerateSavedMessages; method(st::notifyMacPhotoSize).save(v.path, "PNG"); } else if (_type == Type::Rounded) { - peer->saveUserpicRounded(v.path, st::notifyMacPhotoSize); + peer->saveUserpicRounded(userpic, v.path, st::notifyMacPhotoSize); } else { - peer->saveUserpic(v.path, st::notifyMacPhotoSize); + peer->saveUserpic(userpic, v.path, st::notifyMacPhotoSize); } } else { Core::App().logoNoMargin().save(v.path, "PNG"); diff --git a/Telegram/SourceFiles/window/notifications_utilities.h b/Telegram/SourceFiles/window/notifications_utilities.h index f8402e46d..eede77d5b 100644 --- a/Telegram/SourceFiles/window/notifications_utilities.h +++ b/Telegram/SourceFiles/window/notifications_utilities.h @@ -36,7 +36,7 @@ private: Type _type = Type::Rounded; struct Image { - crl::time until; + crl::time until = 0; QString path; }; using Images = QMap; diff --git a/Telegram/lib_base b/Telegram/lib_base index dd96d4d48..3fc4f2699 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit dd96d4d482afae4dfd16d0d0f91ca814b9a652a5 +Subproject commit 3fc4f2699e53ec225bb5052542255302e14b5ce4