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