Support adaptive forum userpic rounding radius.

This commit is contained in:
John Preston 2022-12-05 16:18:10 +04:00
parent 2407ac50bc
commit cb653df0f6
100 changed files with 662 additions and 724 deletions

View file

@ -171,6 +171,11 @@ void CheckChatInvite(
} // namespace Api
struct ConfirmInviteBox::Participant {
not_null<UserData*> user;
Ui::PeerUserpicView userpic;
};
ConfirmInviteBox::ConfirmInviteBox(
QWidget*,
not_null<Main::Session*> session,
@ -356,7 +361,7 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
{ .options = Images::Option::RoundCircle }));
}
} else if (_photoEmpty) {
_photoEmpty->paint(
_photoEmpty->paintCircle(
p,
(width() - st::confirmInvitePhotoSize) / 2,
st::confirmInvitePhotoTop,

View file

@ -21,7 +21,6 @@ class SessionController;
} // namespace Window
namespace Data {
class CloudImageView;
class PhotoMedia;
} // namespace Data
@ -55,10 +54,7 @@ protected:
void paintEvent(QPaintEvent *e) override;
private:
struct Participant {
not_null<UserData*> user;
std::shared_ptr<Data::CloudImageView> userpic;
};
struct Participant;
struct ChatInvite {
QString title;
QString about;

View file

@ -114,7 +114,7 @@ struct Context {
struct Userpic {
not_null<PeerData*> peer;
QString customEntityData;
mutable std::shared_ptr<Data::CloudImageView> view;
mutable Ui::PeerUserpicView view;
mutable InMemoryKey uniqueKey;
};
@ -380,7 +380,7 @@ bool UpdateUserpics(
const auto peer = not_null{ resolved.peer };
const auto &data = ReactionEntityData(resolved.reaction);
const auto i = ranges::find(was, peer, &Userpic::peer);
if (i != end(was) && i->view) {
if (i != end(was) && i->view.cloud) {
now.push_back(std::move(*i));
now.back().customEntityData = data;
continue;

View file

@ -84,7 +84,7 @@ private:
};
struct PeerButton {
not_null<History*> history;
std::shared_ptr<Data::CloudImageView> userpic;
Ui::PeerUserpicView userpic;
Ui::Text::String name;
Button button;
};

View file

@ -157,7 +157,7 @@ PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
const auto peer = this->peer();
const auto saved = peer->isSelf();
const auto replies = peer->isRepliesChat();
auto userpic = saved ? nullptr : ensureUserpicView();
auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);

View file

@ -48,7 +48,7 @@ PaintRoundImageCallback PaintUserpicCallback(
};
}
}
auto userpic = std::shared_ptr<Data::CloudImageView>();
auto userpic = Ui::PeerUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
};
@ -635,8 +635,8 @@ QString PeerListRow::generateShortName() {
: peer()->shortName();
}
std::shared_ptr<Data::CloudImageView> &PeerListRow::ensureUserpicView() {
if (!_userpic) {
Ui::PeerUserpicView &PeerListRow::ensureUserpicView() {
if (!_userpic.cloud && peer()->hasUserpic()) {
_userpic = peer()->createUserpicView();
}
return _userpic;
@ -646,7 +646,7 @@ PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
const auto saved = _isSavedMessagesChat;
const auto replies = _isRepliesMessagesChat;
const auto peer = this->peer();
auto userpic = saved ? nullptr : ensureUserpicView();
auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
@ -840,10 +840,10 @@ void PeerListRow::lazyInitialize(const style::PeerListItem &st) {
void PeerListRow::createCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback) {
const auto generateRadius = [=] {
const auto generateRadius = [=](int size) {
return (!special() && peer()->isForum())
? ImageRoundRadius::Large
: ImageRoundRadius::Ellipse;
? int(size * Ui::ForumUserpicRadiusMultiplier())
: std::optional<int>();
};
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(
st,

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#include "ui/empty_userpic.h"
#include "ui/unread_badge.h"
#include "ui/userpic_view.h"
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
#include "data/data_cloud_file.h"
@ -84,7 +85,7 @@ public:
return _id;
}
[[nodiscard]] std::shared_ptr<Data::CloudImageView> &ensureUserpicView();
[[nodiscard]] Ui::PeerUserpicView &ensureUserpicView();
[[nodiscard]] virtual QString generateName();
[[nodiscard]] virtual QString generateShortName();
@ -262,7 +263,7 @@ private:
PeerListRowId _id = 0;
PeerData *_peer = nullptr;
mutable std::shared_ptr<Data::CloudImageView> _userpic;
mutable Ui::PeerUserpicView _userpic;
std::unique_ptr<Ui::RippleAnimation> _ripple;
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Ui::Text::String _name;

View file

@ -632,7 +632,7 @@ auto ChooseTopicBoxController::Row::generatePaintUserpicCallback()
int y,
int outerWidth,
int size) {
auto view = std::shared_ptr<Data::CloudImageView>();
auto view = Ui::PeerUserpicView();
p.translate(x, y);
_topic->paintUserpic(p, view, {
.st = &st::forumTopicRow,

View file

@ -1042,11 +1042,12 @@ void AddPermanentLinkBlock(
state->allUserpicsLoaded = ranges::all_of(
state->list,
[](const HistoryView::UserpicInRow &element) {
return !element.peer->hasUserpic() || element.view->image();
return !element.peer->hasUserpic()
|| !Ui::PeerUserpicLoading(element.view);
});
state->content = Ui::JoinedCountContent{
.count = state->count,
.userpics = state->cachedUserpics
.userpics = state->cachedUserpics,
};
};
value->value(
@ -1087,7 +1088,7 @@ void AddPermanentLinkBlock(
} else if (element.peer->userpicUniqueKey(element.view)
!= element.uniqueKey) {
pushing = true;
} else if (!element.view->image()) {
} else if (Ui::PeerUserpicLoading(element.view)) {
state->allUserpicsLoaded = false;
}
}

View file

@ -36,7 +36,7 @@ struct UserpicState {
PeerShortInfoUserpic current;
std::optional<UserPhotosSlice> userSlice;
PhotoId userpicPhotoId = PeerData::kUnknownPhotoId;
std::shared_ptr<Data::CloudImageView> userpicView;
Ui::PeerUserpicView userpicView;
std::shared_ptr<Data::PhotoMedia> photoView;
std::vector<std::shared_ptr<Data::PhotoMedia>> photoPreloads;
InMemoryKey userpicKey;
@ -76,27 +76,26 @@ void ProcessUserpic(
not_null<UserpicState*> state) {
state->current.videoDocument = nullptr;
state->userpicKey = peer->userpicUniqueKey(state->userpicView);
if (!state->userpicView) {
if (!state->userpicView.cloud) {
GenerateImage(
state,
peer->generateUserpicImage(
state->userpicView,
st::shortInfoWidth * style::DevicePixelRatio(),
ImageRoundRadius::None),
0),
false);
state->current.photoLoadingProgress = 1.;
state->photoView = nullptr;
return;
}
peer->loadUserpic();
const auto image = state->userpicView->image();
if (!image) {
if (Ui::PeerUserpicLoading(state->userpicView)) {
state->current.photoLoadingProgress = 0.;
state->current.photo = QImage();
state->waitingLoad = true;
return;
}
GenerateImage(state, image, true);
GenerateImage(state, *state->userpicView.cloud, true);
state->current.photoLoadingProgress = peer->userpicPhotoId() ? 0. : 1.;
state->photoView = nullptr;
}
@ -411,7 +410,7 @@ bool ProcessCurrent(
return state->waitingLoad
&& (state->photoView
? (!!state->photoView->image(Data::PhotoSize::Large))
: (state->userpicView && state->userpicView->image()));
: (!Ui::PeerUserpicLoading(state->userpicView)));
}) | rpl::start_with_next([=] {
push();
}, lifetime);

View file

@ -536,7 +536,7 @@ void ShareBox::applyFilterUpdate(const QString &query) {
}
PaintRoundImageCallback ForceRoundUserpicCallback(not_null<PeerData*> peer) {
auto userpic = std::shared_ptr<Data::CloudImageView>();
auto userpic = Ui::PeerUserpicView();
auto cache = std::make_shared<QImage>();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
const auto ratio = style::DevicePixelRatio();
@ -956,9 +956,9 @@ ShareBox::Inner::Chat::Chat(
st.checkbox,
updateCallback,
PaintUserpicCallback(peer, true),
[=] { return peer->isForum()
? ImageRoundRadius::Large
: ImageRoundRadius::Ellipse; })
[=](int size) { return peer->isForum()
? int(size * Ui::ForumUserpicRadiusMultiplier())
: std::optional<int>(); })
, name(st.checkbox.imageRadius * 2) {
}

View file

@ -25,7 +25,6 @@ class PowerSaveBlocker;
namespace Data {
class PhotoMedia;
class CloudImageView;
} // namespace Data
namespace Ui {

View file

@ -159,7 +159,12 @@ void Userpic::refreshPhoto() {
_userPhotoFull = true;
createCache(_photo->image(Data::PhotoSize::Thumbnail));
} else if (_userPhoto.isNull()) {
createCache(_userpic ? _userpic->image() : nullptr);
if (const auto cloud = _peer->userpicCloudImage(_userpic)) {
auto image = Image(base::duplicate(*cloud));
createCache(&image);
} else {
createCache(nullptr);
}
}
}
@ -200,7 +205,7 @@ void Userpic::createCache(Image *image) {
Ui::EmptyUserpic::UserpicColor(
Data::PeerColorIndex(_peer->id)),
_peer->name()
).paint(p, 0, 0, size, size);
).paintCircle(p, 0, 0, size, size);
}
//_userPhoto = Images::PixmapFast(Images::Round(
// std::move(filled),

View file

@ -8,13 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "ui/effects/animations.h"
class PeerData;
class Image;
namespace Data {
class CloudImageView;
class PhotoMedia;
} // namespace Data
@ -51,7 +51,7 @@ private:
Ui::RpWidget _content;
not_null<PeerData*> _peer;
std::shared_ptr<Data::CloudImageView> _userpic;
Ui::PeerUserpicView _userpic;
std::shared_ptr<Data::PhotoMedia> _photo;
Ui::Animations::Simple _mutedAnimation;
QPixmap _userPhoto;

View file

@ -350,7 +350,7 @@ void MembersRow::updateBlobAnimation(crl::time now) {
}
void MembersRow::ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int size) {
Expects(_blobsAnimation != nullptr);
@ -401,7 +401,7 @@ void MembersRow::paintBlobs(
void MembersRow::paintScaledUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &userpic,
Ui::PeerUserpicView &userpic,
int x,
int y,
int outerWidth,

View file

@ -19,6 +19,7 @@ struct GroupCallParticipant;
namespace Ui {
class RippleAnimation;
struct PeerUserpicView;
} // namespace Ui
namespace Calls::Group {
@ -180,7 +181,7 @@ private:
void setVolume(int volume);
void ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int size);
void paintBlobs(
Painter &p,
@ -190,7 +191,7 @@ private:
int sizeh, PanelMode mode);
void paintScaledUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &userpic,
Ui::PeerUserpicView &userpic,
int x,
int y,
int outerWidth,

View file

@ -59,7 +59,7 @@ private:
const not_null<QAction*> _dummyAction;
const style::Menu &_st;
const not_null<PeerData*> _peer;
std::shared_ptr<Data::CloudImageView> _userpicView;
Ui::PeerUserpicView _userpicView;
Ui::Text::String _text;
Ui::Text::String _name;

View file

@ -29,7 +29,6 @@ class PowerSaveBlocker;
namespace Data {
class PhotoMedia;
class CloudImageView;
class GroupCall;
} // namespace Data

View file

@ -462,7 +462,7 @@ void Viewport::RendererGL::validateUserpicFrame(
tileData.userpicFrame = tile->row()->peer()->generateUserpicImage(
tile->row()->ensureUserpicView(),
size.width(),
ImageRoundRadius::None);
0);
}
void Viewport::RendererGL::paintTile(

View file

@ -80,7 +80,7 @@ void Viewport::RendererSW::validateUserpicFrame(
tile->row()->peer()->generateUserpicImage(
tile->row()->ensureUserpicView(),
size.width(),
ImageRoundRadius::None),
0),
kBlurRadius);
}

View file

@ -165,6 +165,20 @@ struct FieldAutocomplete::StickerSuggestion {
QImage premiumLock;
};
struct FieldAutocomplete::MentionRow {
not_null<UserData*> user;
Ui::Text::String name;
Ui::PeerUserpicView userpic;
};
struct FieldAutocomplete::BotCommandRow {
not_null<UserData*> user;
QString command;
QString description;
Ui::PeerUserpicView userpic;
Ui::Text::String descriptionText;
};
FieldAutocomplete::FieldAutocomplete(
QWidget *parent,
not_null<Window::SessionController*> controller)

View file

@ -31,7 +31,6 @@ class SessionController;
namespace Data {
class DocumentMedia;
class CloudImageView;
} // namespace Data
namespace SendMenu {
@ -131,20 +130,8 @@ private:
class Inner;
friend class Inner;
struct StickerSuggestion;
struct MentionRow {
not_null<UserData*> user;
Ui::Text::String name;
std::shared_ptr<Data::CloudImageView> userpic;
};
struct BotCommandRow {
not_null<UserData*> user;
QString command;
QString description;
std::shared_ptr<Data::CloudImageView> userpic;
Ui::Text::String descriptionText;
};
struct MentionRow;
struct BotCommandRow;
using HashtagRows = std::vector<QString>;
using BotCommandRows = std::vector<BotCommandRow>;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_selector.h"
#include "mtproto/sender.h"
#include "ui/round_rect.h"
#include "ui/userpic_view.h"
namespace Ui {
class InputField;
@ -21,7 +22,6 @@ namespace Data {
class StickersSet;
class StickersSetThumbnailView;
class DocumentMedia;
class CloudImageView;
} // namespace Data
namespace Lottie {
@ -73,7 +73,7 @@ struct StickerIcon {
ChannelData *megagroup = nullptr;
mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia;
mutable std::shared_ptr<Data::DocumentMedia> stickerMedia;
mutable std::shared_ptr<Data::CloudImageView> megagroupUserpic;
mutable Ui::PeerUserpicView megagroupUserpic;
int pixw = 0;
int pixh = 0;
mutable rpl::lifetime lifetime;

View file

@ -22,13 +22,6 @@ CloudFile::~CloudFile() {
base::take(loader);
}
void CloudImageView::set(
not_null<Main::Session*> session,
QImage image) {
_image.emplace(std::move(image));
session->notifyDownloaderTaskFinished();
}
CloudImage::CloudImage() = default;
CloudImage::CloudImage(
@ -37,10 +30,6 @@ CloudImage::CloudImage(
update(session, data);
}
Image *CloudImageView::image() {
return _image ? &*_image : nullptr;
}
void CloudImage::set(
not_null<Main::Session*> session,
const ImageWithLocation &data) {
@ -52,11 +41,11 @@ void CloudImage::set(
_file.location = ImageLocation();
_file.byteSize = 0;
_file.flags = CloudFile::Flag();
_view = std::weak_ptr<CloudImageView>();
_view = std::weak_ptr<QImage>();
} else if (was != now
&& (!v::is<InMemoryLocation>(was) || v::is<InMemoryLocation>(now))) {
_file.location = ImageLocation();
_view = std::weak_ptr<CloudImageView>();
_view = std::weak_ptr<QImage>();
}
UpdateCloudFile(
_file,
@ -65,9 +54,7 @@ void CloudImage::set(
kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded, QByteArray) {
if (const auto view = activeView()) {
view->set(session, data.preloaded);
}
setToActive(session, std::move(preloaded));
});
}
@ -81,9 +68,7 @@ void CloudImage::update(
kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded, QByteArray) {
if (const auto view = activeView()) {
view->set(session, data.preloaded);
}
setToActive(session, std::move(preloaded));
});
}
@ -107,16 +92,14 @@ void CloudImage::load(not_null<Main::Session*> session, FileOrigin origin) {
const auto autoLoading = false;
const auto finalCheck = [=] {
if (const auto active = activeView()) {
return !active->image();
return active->isNull();
} else if (_file.flags & CloudFile::Flag::Loaded) {
return false;
}
return !(_file.flags & CloudFile::Flag::Loaded);
};
const auto done = [=](QImage result, QByteArray) {
if (const auto active = activeView()) {
active->set(session, std::move(result));
}
setToActive(session, std::move(result));
};
LoadCloudFile(
session,
@ -137,27 +120,37 @@ int CloudImage::byteSize() const {
return _file.byteSize;
}
std::shared_ptr<CloudImageView> CloudImage::createView() {
std::shared_ptr<QImage> CloudImage::createView() {
if (auto active = activeView()) {
return active;
}
auto view = std::make_shared<CloudImageView>();
auto view = std::make_shared<QImage>();
_view = view;
return view;
}
std::shared_ptr<CloudImageView> CloudImage::activeView() const {
std::shared_ptr<QImage> CloudImage::activeView() const {
return _view.lock();
}
bool CloudImage::isCurrentView(
const std::shared_ptr<CloudImageView> &view) const {
bool CloudImage::isCurrentView(const std::shared_ptr<QImage> &view) const {
if (!view) {
return empty();
}
return !view.owner_before(_view) && !_view.owner_before(view);
}
void CloudImage::setToActive(
not_null<Main::Session*> session,
QImage image) {
if (const auto view = activeView()) {
*view = image.isNull()
? Image::Empty()->original()
: std::move(image);
session->notifyDownloaderTaskFinished();
}
}
void UpdateCloudFile(
CloudFile &file,
const ImageWithLocation &data,

View file

@ -44,17 +44,6 @@ struct CloudFile final {
base::flags<Flag> flags;
};
class CloudImageView final {
public:
void set(not_null<Main::Session*> session, QImage image);
[[nodiscard]] Image *image();
private:
std::optional<Image> _image;
};
class CloudImage final {
public:
CloudImage();
@ -79,14 +68,16 @@ public:
[[nodiscard]] const ImageLocation &location() const;
[[nodiscard]] int byteSize() const;
[[nodiscard]] std::shared_ptr<CloudImageView> createView();
[[nodiscard]] std::shared_ptr<CloudImageView> activeView() const;
[[nodiscard]] std::shared_ptr<QImage> createView();
[[nodiscard]] std::shared_ptr<QImage> activeView() const;
[[nodiscard]] bool isCurrentView(
const std::shared_ptr<CloudImageView> &view) const;
const std::shared_ptr<QImage> &view) const;
private:
void setToActive(not_null<Main::Session*> session, QImage image);
CloudFile _file;
std::weak_ptr<CloudImageView> _view;
std::weak_ptr<QImage> _view;
};

View file

@ -216,7 +216,7 @@ void Folder::loadUserpic() {
void Folder::paintUserpic(
Painter &p,
std::shared_ptr<CloudImageView> &view,
Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const {
paintUserpic(
p,

View file

@ -57,7 +57,7 @@ public:
void loadUserpic() override;
void paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override;
void paintUserpic(Painter &p, int x, int y, int size) const;
void paintUserpic(

View file

@ -568,7 +568,7 @@ void ForumTopic::loadUserpic() {
void ForumTopic::paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const {
const auto &st = context.st;
auto position = QPoint(st->padding.left(), st->padding.top());

View file

@ -150,7 +150,7 @@ public:
void loadUserpic() override;
void paintUserpic(
Painter &p,
std::shared_ptr<CloudImageView> &view,
Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override;
void clearUserpicLoops();

View file

@ -191,7 +191,7 @@ void PeerData::updateNameDelayed(
}
}
_name = newName;
_userpicEmpty = nullptr;
invalidateEmptyUserpic();
auto flags = UpdateFlag::None | UpdateFlag::None;
auto oldFirstLetters = base::flat_set<QChar>();
@ -231,13 +231,17 @@ not_null<Ui::EmptyUserpic*> PeerData::ensureEmptyUserpic() const {
const auto user = asUser();
_userpicEmpty = std::make_unique<Ui::EmptyUserpic>(
Ui::EmptyUserpic::UserpicColor(Data::PeerColorIndex(id)),
user && user->isInaccessible()
((user && user->isInaccessible())
? Ui::EmptyUserpic::InaccessibleName()
: name());
: name()));
}
return _userpicEmpty.get();
}
void PeerData::invalidateEmptyUserpic() {
_userpicEmpty = nullptr;
}
ClickHandlerPtr PeerData::createOpenLink() {
return std::make_shared<PeerClickHandler>(this);
}
@ -265,50 +269,45 @@ void PeerData::setUserpicPhoto(const MTPPhoto &data) {
}
}
Image *PeerData::currentUserpic(
std::shared_ptr<Data::CloudImageView> &view) const {
if (!_userpic.isCurrentView(view)) {
view = _userpic.createView();
_userpic.load(&session(), userpicOrigin());
QImage *PeerData::userpicCloudImage(Ui::PeerUserpicView &view) const {
if (!_userpic.isCurrentView(view.cloud)) {
if (!_userpic.empty()) {
view.cloud = _userpic.createView();
_userpic.load(&session(), userpicOrigin());
} else {
view.cloud = nullptr;
if (view.empty.null()) {
view.paletteVersion = 0;
}
}
}
const auto image = view ? view->image() : nullptr;
if (image) {
if (const auto image = view.cloud.get(); image && !image->isNull()) {
_userpicEmpty = nullptr;
return image;
} else if (isNotificationsUser()) {
static auto result = Image(
Window::LogoNoMargin().scaledToWidth(
kUserpicSize,
Qt::SmoothTransformation));
static auto result = Window::LogoNoMargin().scaledToWidth(
kUserpicSize,
Qt::SmoothTransformation);
return &result;
}
return image;
return nullptr;
}
void PeerData::paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int x,
int y,
int size) const {
if (const auto userpic = currentUserpic(view)) {
const auto rounding = isForum()
? Images::Option::RoundLarge
: Images::Option::RoundCircle;
p.drawPixmap(
x,
y,
userpic->pix(size, size, { .options = rounding }));
} else if (isForum()) {
ensureEmptyUserpic()->paintRounded(
p,
x,
y,
x + size + x,
size,
st::roundRadiusLarge);
} else{
ensureEmptyUserpic()->paint(p, x, y, x + size + x, size);
}
const auto cloud = userpicCloudImage(view);
const auto ratio = style::DevicePixelRatio();
Ui::ValidateUserpicCache(
view,
cloud,
cloud ? nullptr : ensureEmptyUserpic().get(),
size * ratio,
isForum());
p.drawImage(QRect(x, y, size, size), view.cached);
}
void PeerData::loadUserpic() {
@ -319,112 +318,76 @@ bool PeerData::hasUserpic() const {
return !_userpic.empty();
}
std::shared_ptr<Data::CloudImageView> PeerData::activeUserpicView() {
return _userpic.empty() ? nullptr : _userpic.activeView();
Ui::PeerUserpicView PeerData::activeUserpicView() {
return { .cloud = _userpic.empty() ? nullptr : _userpic.activeView() };
}
std::shared_ptr<Data::CloudImageView> PeerData::createUserpicView() {
Ui::PeerUserpicView PeerData::createUserpicView() {
if (_userpic.empty()) {
return nullptr;
return {};
}
auto result = _userpic.createView();
_userpic.load(&session(), userpicPhotoOrigin());
return result;
return { .cloud = result };
}
bool PeerData::useEmptyUserpic(
std::shared_ptr<Data::CloudImageView> &view) const {
return !currentUserpic(view);
bool PeerData::useEmptyUserpic(Ui::PeerUserpicView &view) const {
return !userpicCloudImage(view);
}
InMemoryKey PeerData::userpicUniqueKey(
std::shared_ptr<Data::CloudImageView> &view) const {
InMemoryKey PeerData::userpicUniqueKey(Ui::PeerUserpicView &view) const {
return useEmptyUserpic(view)
? ensureEmptyUserpic()->uniqueKey()
: inMemoryKey(_userpic.location());
}
void PeerData::saveUserpic(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const {
generateUserpicImage(view, size * cIntRetinaFactor()).save(path, "PNG");
}
void PeerData::saveUserpicRounded(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const {
generateUserpicImage(
view,
size * cIntRetinaFactor(),
ImageRoundRadius::Small).save(path, "PNG");
}
QPixmap PeerData::genUserpic(
std::shared_ptr<Data::CloudImageView> &view,
int size) const {
if (const auto userpic = currentUserpic(view)) {
const auto rounding = isForum()
? Images::Option::RoundLarge
: Images::Option::RoundCircle;
return userpic->pix(size, size, { .options = rounding });
}
const auto ratio = style::DevicePixelRatio();
auto result = QImage(
QSize(size, size) * ratio,
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(ratio);
result.fill(Qt::transparent);
{
Painter p(&result);
paintUserpic(p, view, 0, 0, size);
}
return Ui::PixmapFromImage(std::move(result));
}
QImage PeerData::generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view,
int size) const {
return generateUserpicImage(
view,
size,
isForum() ? ImageRoundRadius::Large : ImageRoundRadius::Ellipse);
}
QImage PeerData::generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int size,
ImageRoundRadius radius) const {
if (const auto userpic = currentUserpic(view)) {
const auto options = (radius == ImageRoundRadius::Ellipse)
? Images::Option::RoundCircle
: (radius == ImageRoundRadius::Large)
? Images::Option::RoundLarge
: (radius == ImageRoundRadius::Small)
? Images::Option::RoundSmall
: Images::Option();
return userpic->pixNoCache(
std::optional<int> radius) const {
if (const auto userpic = userpicCloudImage(view)) {
auto image = userpic->scaled(
{ size, size },
{ .options = options }).toImage();
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
const auto round = [&](int radius) {
return Images::Round(
std::move(image),
Images::CornersMask(radius));
};
if (radius == 0) {
return image;
} else if (radius) {
return round(*radius);
} else if (isForum()) {
return round(size * Ui::ForumUserpicRadiusMultiplier());
} else {
return Images::Circle(std::move(image));
}
}
auto result = QImage(
QSize(size, size),
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
{
Painter p(&result);
if (radius == ImageRoundRadius::Ellipse) {
ensureEmptyUserpic()->paint(p, 0, 0, size, size);
} else if (radius == ImageRoundRadius::None) {
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
} else if (radius == ImageRoundRadius::Large) {
const auto radius = st::roundRadiusLarge;
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, radius);
} else {
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size);
}
Painter p(&result);
if (radius == 0) {
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
} else if (radius) {
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, *radius);
} else if (isForum()) {
ensureEmptyUserpic()->paintRounded(
p,
0,
0,
size,
size,
size * Ui::ForumUserpicRadiusMultiplier());
} else {
ensureEmptyUserpic()->paintCircle(p, 0, 0, size, size);
}
p.end();
return result;
}

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_flags.h"
#include "data/notify/data_peer_notify_settings.h"
#include "data/data_cloud_file.h"
#include "ui/userpic_view.h"
struct BotInfo;
class PeerData;
@ -36,13 +37,12 @@ class Forum;
class ForumTopic;
class Session;
class GroupCall;
class CloudImageView;
struct ReactionId;
int PeerColorIndex(PeerId peerId);
[[nodiscard]] int PeerColorIndex(PeerId peerId);
// Must be used only for PeerColor-s.
PeerId FakePeerIdForJustName(const QString &name);
[[nodiscard]] PeerId FakePeerIdForJustName(const QString &name);
class RestrictionCheckResult {
public:
@ -264,13 +264,13 @@ public:
void setUserpicPhoto(const MTPPhoto &data);
void paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int x,
int y,
int size) const;
void paintUserpicLeft(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int x,
int y,
int w,
@ -279,30 +279,14 @@ public:
}
void loadUserpic();
[[nodiscard]] bool hasUserpic() const;
[[nodiscard]] std::shared_ptr<Data::CloudImageView> activeUserpicView();
[[nodiscard]] std::shared_ptr<Data::CloudImageView> createUserpicView();
[[nodiscard]] bool useEmptyUserpic(
std::shared_ptr<Data::CloudImageView> &view) const;
[[nodiscard]] InMemoryKey userpicUniqueKey(
std::shared_ptr<Data::CloudImageView> &view) const;
void saveUserpic(
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]] Ui::PeerUserpicView activeUserpicView();
[[nodiscard]] Ui::PeerUserpicView createUserpicView();
[[nodiscard]] bool useEmptyUserpic(Ui::PeerUserpicView &view) const;
[[nodiscard]] InMemoryKey userpicUniqueKey(Ui::PeerUserpicView &view) const;
[[nodiscard]] QImage generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view,
int size) const;
[[nodiscard]] QImage generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int size,
ImageRoundRadius radius) const;
std::optional<int> radius = {}) const;
[[nodiscard]] ImageLocation userpicLocation() const {
return _userpic.location();
}
@ -332,8 +316,7 @@ public:
return _openLink;
}
[[nodiscard]] Image *currentUserpic(
std::shared_ptr<Data::CloudImageView> &view) const;
[[nodiscard]] QImage *userpicCloudImage(Ui::PeerUserpicView &view) const;
[[nodiscard]] bool canPinMessages() const;
[[nodiscard]] bool canEditMessagesIndefinitely() const;
@ -423,6 +406,7 @@ protected:
const QString &newUsername);
void updateUserpic(PhotoId photoId, MTP::DcId dcId, bool hasVideo);
void clearUserpic();
void invalidateEmptyUserpic();
private:
void fillNames();

View file

@ -515,17 +515,10 @@ bool ChannelHasActiveCall(not_null<ChannelData*> channel) {
rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size) {
return PeerUserpicImageValue(peer, size, ImageRoundRadius::Ellipse);
}
rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size,
ImageRoundRadius radius) {
return [=](auto consumer) {
auto result = rpl::lifetime();
struct State {
std::shared_ptr<CloudImageView> view;
Ui::PeerUserpicView view;
rpl::lifetime waiting;
InMemoryKey key = {};
bool empty = true;
@ -534,7 +527,7 @@ rpl::producer<QImage> PeerUserpicImageValue(
const auto state = result.make_state<State>();
state->push = [=] {
const auto key = peer->userpicUniqueKey(state->view);
const auto loading = state->view && !state->view->image();
const auto loading = Ui::PeerUserpicLoading(state->view);
if (loading && !state->waiting) {
peer->session().downloaderTaskFinished(
@ -548,8 +541,7 @@ rpl::producer<QImage> PeerUserpicImageValue(
}
state->key = key;
state->empty = false;
consumer.put_next(
peer->generateUserpicImage(state->view, size, radius));
consumer.put_next(peer->generateUserpicImage(state->view, size));
};
peer->session().changes().peerFlagsValue(
peer,

View file

@ -134,10 +134,6 @@ inline auto PeerFullFlagValue(
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size);
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size,
ImageRoundRadius radius);
[[nodiscard]] const AllowedReactions &PeerAllowedReactions(
not_null<PeerData*> peer);

View file

@ -228,22 +228,27 @@ void UserData::setAccessHash(uint64 accessHash) {
if (accessHash == kInaccessibleAccessHashOld) {
_accessHash = 0;
_flags.add(Flag::Deleted);
invalidateEmptyUserpic();
} else {
_accessHash = accessHash;
}
}
void UserData::setFlags(UserDataFlags which) {
if ((which & UserDataFlag::Deleted)
!= (flags() & UserDataFlag::Deleted)) {
invalidateEmptyUserpic();
}
_flags.set((flags() & UserDataFlag::Self)
| (which & ~UserDataFlag::Self));
}
void UserData::addFlags(UserDataFlags which) {
_flags.add(which & ~UserDataFlag::Self);
setFlags(flags() | which);
}
void UserData::removeFlags(UserDataFlags which) {
_flags.remove(which & ~UserDataFlag::Self);
setFlags(flags() & ~which);
}
bool UserData::isVerified() const {

View file

@ -25,10 +25,10 @@ class Session;
class Forum;
class Folder;
class ForumTopic;
class CloudImageView;
} // namespace Data
namespace Ui {
struct PeerUserpicView;
} // namespace Ui
namespace Dialogs::Ui {
@ -233,7 +233,7 @@ public:
virtual void loadUserpic() = 0;
virtual void paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Ui::PaintContext &context) const = 0;
[[nodiscard]] TimeId chatListTimeId() const {

View file

@ -1134,7 +1134,7 @@ void InnerWidget::paintSearchInFilter(
void InnerWidget::paintSearchInPeer(
Painter &p,
not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpic,
Ui::PeerUserpicView &userpic,
int top,
const Ui::Text::String &text) const {
const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
@ -1168,7 +1168,7 @@ void InnerWidget::paintSearchInTopic(
Painter &p,
const Ui::PaintContext &context,
not_null<Data::ForumTopic*> topic,
std::shared_ptr<Data::CloudImageView> &userpic,
Ui::PeerUserpicView &userpic,
int top,
const Ui::Text::String &text) const {
const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
@ -2846,7 +2846,7 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
_searchFromUserUserpic = _searchFromPeer->createUserpicView();
} else {
_cancelSearchFromUser->hide();
_searchFromUserUserpic = nullptr;
_searchFromUserUserpic = {};
}
if (_searchInChat || _searchFromPeer) {
refreshSearchInChatLabel();
@ -2855,7 +2855,7 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
if (const auto peer = _searchInChat.peer()) {
_searchInChatUserpic = peer->createUserpicView();
} else {
_searchInChatUserpic = nullptr;
_searchInChatUserpic = {};
}
moveCancelSearchButtons();

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/dragging_scroll_manager.h"
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "base/flags.h"
#include "base/object_ptr.h"
@ -39,7 +40,6 @@ class SessionController;
} // namespace Window
namespace Data {
class CloudImageView;
class Thread;
class Folder;
class Forum;
@ -340,7 +340,7 @@ private:
void paintSearchInPeer(
Painter &p,
not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpic,
Ui::PeerUserpicView &userpic,
int top,
const Ui::Text::String &text) const;
void paintSearchInSaved(
@ -355,7 +355,7 @@ private:
Painter &p,
const Ui::PaintContext &context,
not_null<Data::ForumTopic*> topic,
std::shared_ptr<Data::CloudImageView> &userpic,
Ui::PeerUserpicView &userpic,
int top,
const Ui::Text::String &text) const;
template <typename PaintUserpic>
@ -470,8 +470,8 @@ private:
Key _searchInChat;
History *_searchInMigrated = nullptr;
PeerData *_searchFromPeer = nullptr;
mutable std::shared_ptr<Data::CloudImageView> _searchInChatUserpic;
mutable std::shared_ptr<Data::CloudImageView> _searchFromUserUserpic;
mutable Ui::PeerUserpicView _searchInChatUserpic;
mutable Ui::PeerUserpicView _searchFromUserUserpic;
Ui::Text::String _searchInChatText;
Ui::Text::String _searchFromUserText;
RowDescriptor _menuRow;

View file

@ -38,11 +38,11 @@ constexpr auto kNoneLayer = 0;
void PaintCornerBadgeTTLFrame(
QPainter &q,
not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
float64 progress,
int photoSize) {
const auto ttl = peer->messagesTTL();
if (!ttl || !view) {
if (!ttl) {
return;
}
using Radius = ImageRoundRadius;
@ -54,7 +54,7 @@ void PaintCornerBadgeTTLFrame(
const auto ratio = style::DevicePixelRatio();
const auto fullSize = photoSize;
const auto blurredFull = Images::BlurLargeImage(
peer->generateUserpicImage(view, fullSize * ratio, Radius::None),
peer->generateUserpicImage(view, fullSize * ratio, 0),
kBlurRadius);
const auto partRect = CornerBadgeTTLRect(fullSize);
const auto &partSize = partRect.width();
@ -338,7 +338,7 @@ void Row::PaintCornerBadgeFrame(
not_null<CornerBadgeUserpic*> data,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Ui::PaintContext &context) {
data->frame.fill(Qt::transparent);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text.h"
#include "ui/effects/animations.h"
#include "ui/unread_badge.h"
#include "ui/userpic_view.h"
#include "dialogs/dialogs_key.h"
#include "dialogs/ui/dialogs_message_view.h"
@ -20,10 +21,6 @@ namespace style {
struct DialogRow;
} // namespace style
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui {
class RippleAnimation;
} // namespace Ui
@ -69,13 +66,12 @@ public:
int outerWidth,
const QColor *colorOverride = nullptr) const;
[[nodiscard]] auto userpicView() const
-> std::shared_ptr<Data::CloudImageView> & {
[[nodiscard]] Ui::PeerUserpicView &userpicView() const {
return _userpic;
}
private:
mutable std::shared_ptr<Data::CloudImageView> _userpic;
mutable Ui::PeerUserpicView _userpic;
mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
};
@ -182,7 +178,7 @@ private:
not_null<CornerBadgeUserpic*> data,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Ui::PaintContext &context);
Key _id;

View file

@ -285,9 +285,9 @@ void PaintRow(
not_null<Entry*> entry,
VideoUserpic *videoUserpic,
PeerData *from,
Ui::PeerBadge &fromBadge,
PeerBadge &fromBadge,
Fn<void()> customEmojiRepaint,
const Ui::Text::String &fromName,
const Text::String &fromName,
const HiddenSenderInfo *hiddenSenderInfo,
HistoryItem *item,
const Data::Draft *draft,
@ -306,8 +306,8 @@ void PaintRow(
: context.selected
? st::dialogsBgOver
: anim::brush(
st::dialogsBg,
st::dialogsBgOver,
st::dialogsBg,
st::dialogsBgOver,
context.childListShown);
p.fillRect(geometry, bg);
if (!(flags & Flag::TopicJumpRipple)) {
@ -342,7 +342,7 @@ void PaintRow(
(flags & Flag::AllowUserOnline) ? history : nullptr,
context);
} else if (hiddenSenderInfo) {
hiddenSenderInfo->emptyUserpic.paint(
hiddenSenderInfo->emptyUserpic.paintCircle(
p,
context.st->padding.left(),
context.st->padding.top(),

View file

@ -38,8 +38,8 @@ using namespace ::Ui;
class VideoUserpic;
struct TopicJumpCorners {
Ui::CornersPixmaps normal;
Ui::CornersPixmaps inverted;
CornersPixmaps normal;
CornersPixmaps inverted;
QPixmap small;
int invertedRadius = 0;
int smallKey = 0; // = `-radius` if top right else `radius`.

View file

@ -30,7 +30,7 @@ int VideoUserpic::frameIndex() const {
void VideoUserpic::paintLeft(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int x,
int y,
int w,
@ -133,7 +133,7 @@ void PaintUserpic(
Painter &p,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
int x,
int y,
int outerWidth,

View file

@ -12,10 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class Painter;
namespace Data {
class CloudImageView;
class PhotoMedia;
} // namespace Data
namespace Ui {
struct PeerUserpicView;
} // namespace Ui
namespace Dialogs::Ui {
using namespace ::Ui;
@ -29,7 +32,7 @@ public:
void paintLeft(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
PeerUserpicView &view,
int x,
int y,
int w,
@ -54,8 +57,8 @@ private:
void PaintUserpic(
Painter &p,
not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view,
VideoUserpic *videoUserpic,
PeerUserpicView &view,
int x,
int y,
int outerWidth,

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "editor/editor_crop.h"
#include "ui/userpic_view.h"
#include "styles/style_editor.h"
#include "styles/style_basic.h"
#include "styles/style_dialogs.h"
@ -149,7 +150,7 @@ void Crop::setCropPaint(QRectF &&rect) {
_painterPath.addEllipse(_cropPaint);
} else if (_data.cropType == EditorData::CropType::RoundedRect) {
const auto radius = std::min(_cropPaint.width(), _cropPaint.height())
* st::roundRadiusLarge / float64(st::defaultDialogRow.photoSize);
* Ui::ForumUserpicRadiusMultiplier();
_painterPath.addRoundedRect(_cropPaint, radius, radius);
} else {
_painterPath.addRect(_cropPaint);

View file

@ -20,10 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace AdminLog {
namespace {
@ -64,7 +60,7 @@ private:
QRect _checkRect;
const not_null<UserData*> _user;
std::shared_ptr<Data::CloudImageView> _userpic;
Ui::PeerUserpicView _userpic;
Ui::Text::String _name;
QString _statusText;
bool _statusOnline = false;

View file

@ -19,10 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct ChatRestrictionsInfo;
namespace Data {
class CloudImageView;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@ -38,6 +34,7 @@ enum class PointState : char;
namespace Ui {
class PopupMenu;
class ChatStyle;
struct PeerUserpicView;
} // namespace Ui
namespace Window {
@ -278,9 +275,8 @@ private:
std::map<not_null<const HistoryItem*>, not_null<Element*>> _itemsByData;
base::flat_map<not_null<const HistoryItem*>, TimeId> _itemDates;
base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map<
not_null<PeerData*>,
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
int _itemsTop = 0;
int _itemsWidth = 0;
int _itemsHeight = 0;

View file

@ -2262,7 +2262,7 @@ void History::loadUserpic() {
void History::paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const {
peer->paintUserpic(
p,

View file

@ -398,7 +398,7 @@ public:
void loadUserpic() override;
void paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override;
void refreshChatListNameSortKey();

View file

@ -1135,7 +1135,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
// paint the userpic if it intersects the painted rect
if (userpicTop + st::msgPhotoSize > clip.top()) {
if (const auto from = view->data()->displayFrom()) {
const auto item = view->data();
if (const auto from = item->displayFrom()) {
Dialogs::Ui::PaintUserpic(
p,
from,
@ -1146,28 +1147,25 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
width(),
st::msgPhotoSize,
context.paused);
} else if (const auto info = view->data()->hiddenSenderInfo()) {
} else if (const auto info = item->hiddenSenderInfo()) {
if (info->customUserpic.empty()) {
info->emptyUserpic.paint(
info->emptyUserpic.paintCircle(
p,
st::historyPhotoLeft,
userpicTop,
width(),
st::msgPhotoSize);
} else {
const auto painted = info->paintCustomUserpic(
auto &userpic = _hiddenSenderUserpics[item->id];
const auto valid = info->paintCustomUserpic(
p,
userpic,
st::historyPhotoLeft,
userpicTop,
width(),
st::msgPhotoSize);
if (!painted) {
const auto itemId = view->data()->fullId();
auto &v = _sponsoredUserpics[itemId.msg];
if (!info->customUserpic.isCurrentView(v)) {
v = info->customUserpic.createView();
info->customUserpic.load(&session(), itemId);
}
if (!valid) {
info->customUserpic.load(&session(), item->fullId());
}
}
} else {

View file

@ -20,7 +20,6 @@ struct ClickHandlerContext;
namespace Data {
struct Group;
class CloudImageView;
} // namespace Data
namespace HistoryView {
@ -51,6 +50,7 @@ class PopupMenu;
enum class ReportReason;
struct ChatPaintContext;
class PathShiftGradient;
struct PeerUserpicView;
} // namespace Ui
namespace Dialogs::Ui {
@ -458,12 +458,9 @@ private:
bool _isChatWide = false;
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
base::flat_map<
not_null<PeerData*>,
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
base::flat_map<
MsgId,
std::shared_ptr<Data::CloudImageView>> _sponsoredUserpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
base::flat_map<MsgId, Ui::PeerUserpicView> _hiddenSenderUserpics;
base::flat_map<
not_null<PeerData*>,
std::unique_ptr<VideoUserpic>> _videoUserpics;

View file

@ -144,22 +144,31 @@ ClickHandlerPtr HiddenSenderInfo::ForwardClickHandler() {
bool HiddenSenderInfo::paintCustomUserpic(
Painter &p,
Ui::PeerUserpicView &view,
int x,
int y,
int outerWidth,
int size) const {
const auto view = customUserpic.activeView();
if (const auto image = view ? view->image() : nullptr) {
const auto circled = Images::Option::RoundCircle;
p.drawPixmap(
x,
y,
image->pix(size, size, { .options = circled }));
return true;
} else {
emptyUserpic.paint(p, x, y, outerWidth, size);
return false;
Expects(!customUserpic.empty());
auto valid = true;
if (!customUserpic.isCurrentView(view.cloud)) {
view.cloud = customUserpic.createView();
valid = false;
}
const auto image = *view.cloud;
if (image.isNull()) {
emptyUserpic.paintCircle(p, x, y, outerWidth, size);
return valid;
}
Ui::ValidateUserpicCache(
view,
image.isNull() ? nullptr : &image,
image.isNull() ? &emptyUserpic : nullptr,
size * style::DevicePixelRatio(),
false);
p.drawImage(QRect(x, y, size, size), view.cached);
return valid;
}
void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {

View file

@ -19,6 +19,7 @@ class VoiceSeekClickHandler;
namespace Ui {
struct ChatPaintContext;
class ChatStyle;
struct PeerUserpicView;
} // namespace Ui
namespace Data {
@ -94,6 +95,7 @@ public:
[[nodiscard]] const Ui::Text::String &nameText() const;
[[nodiscard]] bool paintCustomUserpic(
Painter &p,
Ui::PeerUserpicView &view,
int x,
int y,
int outerWidth,

View file

@ -139,12 +139,12 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallBarContentByCall(
state->someUserpicsNotLoaded = false;
for (auto &userpic : state->userpics) {
userpic.peer->loadUserpic();
const auto pic = userpic.peer->genUserpic(
auto image = userpic.peer->generateUserpicImage(
userpic.view,
userpicSize);
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
state->current.users.push_back({
.userpic = pic.toImage(),
.userpic = std::move(image),
.userpicKey = userpic.uniqueKey,
.id = userpic.peer->id.value,
.speaking = userpic.speaking,

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
namespace Ui {
struct GroupCallBarContent;
@ -15,7 +16,6 @@ struct GroupCallBarContent;
namespace Data {
class GroupCall;
class CloudImageView;
} // namespace Data
namespace style {
@ -27,7 +27,7 @@ namespace HistoryView {
struct UserpicInRow {
not_null<PeerData*> peer;
bool speaking = false;
mutable std::shared_ptr<Data::CloudImageView> view;
mutable Ui::PeerUserpicView view;
mutable InMemoryKey uniqueKey;
};

View file

@ -2132,7 +2132,8 @@ void ListWidget::paintEvent(QPaintEvent *e) {
// paint the userpic if it intersects the painted rect
if (userpicTop + st::msgPhotoSize > clip.top()) {
if (const auto from = view->data()->displayFrom()) {
const auto item = view->data();
if (const auto from = item->displayFrom()) {
from->paintUserpicLeft(
p,
_userpics[from],
@ -2140,28 +2141,25 @@ void ListWidget::paintEvent(QPaintEvent *e) {
userpicTop,
view->width(),
st::msgPhotoSize);
} else if (const auto info = view->data()->hiddenSenderInfo()) {
} else if (const auto info = item->hiddenSenderInfo()) {
if (info->customUserpic.empty()) {
info->emptyUserpic.paint(
info->emptyUserpic.paintCircle(
p,
st::historyPhotoLeft,
userpicTop,
view->width(),
st::msgPhotoSize);
} else {
const auto painted = info->paintCustomUserpic(
auto &userpic = _hiddenSenderUserpics[item->id];
const auto valid = info->paintCustomUserpic(
p,
userpic,
st::historyPhotoLeft,
userpicTop,
view->width(),
st::msgPhotoSize);
if (!painted) {
const auto itemId = view->data()->fullId();
auto &v = _sponsoredUserpics[itemId.msg];
if (!info->customUserpic.isCurrentView(v)) {
v = info->customUserpic.createView();
info->customUserpic.load(session, itemId);
}
if (!valid) {
info->customUserpic.load(session, item->fullId());
}
}
} else {

View file

@ -29,6 +29,7 @@ class PopupMenu;
class ChatTheme;
struct ChatPaintContext;
enum class TouchScrollState;
struct PeerUserpicView;
} // namespace Ui
namespace Window {
@ -37,7 +38,6 @@ class SessionController;
namespace Data {
struct Group;
class CloudImageView;
struct Reaction;
struct AllowedReactions;
} // namespace Data
@ -637,12 +637,9 @@ private:
ItemRevealAnimation> _itemRevealAnimations;
int _itemsRevealHeight = 0;
base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map<
not_null<PeerData*>,
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
base::flat_map<
MsgId,
std::shared_ptr<Data::CloudImageView>> _sponsoredUserpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
base::flat_map<MsgId, Ui::PeerUserpicView> _hiddenSenderUserpics;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;

View file

@ -1105,10 +1105,10 @@ void Message::paintCommentsButton(
auto &entry = list[i];
const auto peer = entry.peer;
auto &view = entry.view;
const auto wasView = view.get();
const auto wasView = view.cloud.get();
if (views->recentRepliers[i] != peer->id
|| peer->userpicUniqueKey(view) != entry.uniqueKey
|| view.get() != wasView) {
|| view.cloud.get() != wasView) {
return true;
}
}

View file

@ -86,12 +86,12 @@ rpl::producer<Ui::RequestsBarContent> RequestsBarContentByPeer(
state->someUserpicsNotLoaded = false;
for (auto &userpic : state->userpics) {
userpic.peer->loadUserpic();
const auto pic = userpic.peer->genUserpic(
auto image = userpic.peer->generateUserpicImage(
userpic.view,
userpicSize);
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
state->current.users.push_back({
.userpic = pic.toImage(),
.userpic = std::move(image),
.userpicKey = userpic.uniqueKey,
.id = userpic.peer->id.value,
});

View file

@ -96,8 +96,8 @@ Contact::Contact(
Contact::~Contact() {
history()->owner().unregisterContactView(_userId, _parent);
if (_userpic) {
_userpic = nullptr;
if (!_userpic.null()) {
_userpic = {};
_parent->checkHeavyPart();
}
}
@ -177,13 +177,13 @@ void Contact::draw(Painter &p, const PaintContext &context) const {
if (_userId) {
QRect rthumb(style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, paintw));
if (_contact) {
const auto was = (_userpic != nullptr);
const auto was = !_userpic.null();
_contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st.thumbSize);
if (!was && _userpic) {
if (!was && !_userpic.null()) {
history()->owner().registerHeavyViewPart(_parent);
}
} else {
_photoEmpty->paint(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
_photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
}
if (context.selected()) {
PainterHighQualityEnabler hq(p);
@ -197,7 +197,7 @@ void Contact::draw(Painter &p, const PaintContext &context) const {
p.setPen(stm->msgFileThumbLinkFg);
p.drawTextLeft(nameleft, linktop, paintw, _link, _linkw);
} else {
_photoEmpty->paint(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
_photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
}
const auto namewidth = paintw - nameleft - nameright;
@ -232,11 +232,11 @@ TextState Contact::textState(QPoint point, StateRequest request) const {
}
void Contact::unloadHeavyPart() {
_userpic = nullptr;
_userpic = {};
}
bool Contact::hasHeavyPart() const {
return (_userpic != nullptr);
return !_userpic.null();
}
} // namespace HistoryView

View file

@ -8,10 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "history/view/media/history_view_media.h"
namespace Data {
class CloudImageView;
} // namespace Data
#include "ui/userpic_view.h"
namespace Ui {
class EmptyUserpic;
@ -72,7 +69,7 @@ private:
QString _fname, _lname, _phone;
Ui::Text::String _name;
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
mutable std::shared_ptr<Data::CloudImageView> _userpic;
mutable Ui::PeerUserpicView _userpic;
ClickHandlerPtr _linkl;
int _linkw = 0;

View file

@ -245,17 +245,16 @@ void Location::draw(Painter &p, const PaintContext &context) const {
void Location::validateImageCache(
QSize outer,
Ui::BubbleRounding rounding) const {
Expects(_media != nullptr);
const auto ratio = style::DevicePixelRatio();
if (_imageCache.size() == (outer * ratio)
&& _imageCacheRounding == rounding) {
return;
}
const auto thumbnail = _media->image();
if (!thumbnail) {
&& _imageCacheRounding == rounding
|| _media->isNull()) {
return;
}
_imageCache = Images::Round(
thumbnail->original().scaled(
_media->scaled(
outer * ratio,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation),

View file

@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
class CloudImage;
class CloudImageView;
} // namespace Data
namespace HistoryView {
@ -81,7 +80,7 @@ private:
[[nodiscard]] int fullHeight() const;
const not_null<Data::CloudImage*> _data;
mutable std::shared_ptr<Data::CloudImageView> _media;
mutable std::shared_ptr<QImage> _media;
Ui::Text::String _title, _description;
ClickHandlerPtr _link;

View file

@ -46,6 +46,7 @@ struct Photo::Streamed {
::Media::Streaming::Instance instance;
::Media::Streaming::FrameRequest frozenRequest;
QImage frozenFrame;
std::array<QImage, 4> roundingCorners;
QImage roundingMask;
};
@ -352,22 +353,66 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
}
}
void Photo::validateUserpicImageCache(QSize size, bool forum) const {
const auto forumValue = forum ? 1 : 0;
const auto large = _dataMedia->image(PhotoSize::Large);
const auto ratio = style::DevicePixelRatio();
const auto blurredValue = large ? 0 : 1;
if (_imageCache.size() == (size * ratio)
&& _imageCacheForum == forumValue
&& _imageCacheBlurred == blurredValue) {
return;
}
auto original = [&] {
if (large) {
return large->original();
} else if (const auto thumbnail = _dataMedia->image(
PhotoSize::Thumbnail)) {
return thumbnail->original();
} else if (const auto small = _dataMedia->image(
PhotoSize::Small)) {
return small->original();
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
return blurred->original();
} else {
return Image::Empty()->original();
}
}();
auto args = Images::PrepareArgs();
if (blurredValue) {
args = args.blurred();
}
original = Images::Prepare(std::move(original), size, args);
if (forumValue) {
original = Images::Round(
std::move(original),
Images::CornersMask(std::min(size.width(), size.height())
* Ui::ForumUserpicRadiusMultiplier()
* style::DevicePixelRatio()));
} else {
original = Images::Circle(std::move(original));
}
_imageCache = std::move(original);
_imageCacheForum = forumValue;
_imageCacheBlurred = blurredValue;
}
void Photo::validateImageCache(
QSize outer,
std::optional<Ui::BubbleRounding> rounding) const {
const auto large = _dataMedia->image(PhotoSize::Large);
const auto ratio = style::DevicePixelRatio();
const auto shouldBeBlurred = !large;
const auto blurredValue = large ? 0 : 1;
if (_imageCache.size() == (outer * ratio)
&& _imageCacheRounding == rounding
&& _imageCacheBlurred == shouldBeBlurred) {
&& _imageCacheBlurred == blurredValue) {
return;
}
_imageCache = Images::Round(
prepareImageCache(outer),
MediaRoundingMask(rounding));
_imageCacheRounding = rounding;
_imageCacheBlurred = shouldBeBlurred;
_imageCacheBlurred = blurredValue;
}
QImage Photo::prepareImageCache(QSize outer) const {
@ -405,17 +450,23 @@ void Photo::paintUserpicFrame(
const auto rect = QRect(photoPosition, size);
const auto st = context.st;
const auto sti = context.imageStyle();
const auto forum = _parent->data()->history()->isForum();
if (_streamed
&& _streamed->instance.player().ready()
&& !_streamed->instance.player().videoSize().isEmpty()) {
const auto ratio = style::DevicePixelRatio();
auto request = ::Media::Streaming::FrameRequest();
request.outer = size * cIntRetinaFactor();
request.resize = size * cIntRetinaFactor();
const auto forum = _parent->data()->history()->isForum();
request.outer = request.resize = size * ratio;
if (forum) {
const auto radius = int(std::min(size.width(), size.height())
* Ui::ForumUserpicRadiusMultiplier()
* ratio);
if (_streamed->roundingCorners[0].width() != radius) {
_streamed->roundingCorners = Images::CornersMask(radius);
}
request.rounding = Images::CornersMaskRef(
Images::CornersMask(ImageRoundRadius::Large));
_streamed->roundingCorners);
} else {
if (_streamed->roundingMask.size() != request.outer) {
_streamed->roundingMask = Images::EllipseMask(size);
@ -438,28 +489,8 @@ void Photo::paintUserpicFrame(
}
return;
}
const auto pix = [&] {
const auto forum = _parent->data()->history()->isForum();
const auto args = Images::PrepareArgs{
.options = (forum
? Images::Option::RoundLarge
: Images::Option::RoundCircle),
};
if (const auto large = _dataMedia->image(PhotoSize::Large)) {
return large->pix(size, args);
} else if (const auto thumbnail = _dataMedia->image(
PhotoSize::Thumbnail)) {
return thumbnail->pix(size, args.blurred());
} else if (const auto small = _dataMedia->image(
PhotoSize::Small)) {
return small->pix(size, args.blurred());
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
return blurred->pix(size, args.blurred());
} else {
return QPixmap();
}
}();
p.drawPixmap(rect, pix);
validateUserpicImageCache(size, forum);
p.drawImage(rect, _imageCache);
if (_data->videoCanBePlayed() && !_streamed) {
const auto innerSize = st::msgFileLayout.thumbSize;

View file

@ -129,6 +129,7 @@ private:
void validateImageCache(
QSize outer,
std::optional<Ui::BubbleRounding> rounding) const;
void validateUserpicImageCache(QSize size, bool forum) const;
[[nodiscard]] QImage prepareImageCache(QSize outer) const;
bool videoAutoplayEnabled() const;
@ -150,8 +151,9 @@ private:
mutable std::unique_ptr<Streamed> _streamed;
mutable QImage _imageCache;
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
int _serviceWidth = 0;
mutable bool _imageCacheBlurred = false;
int _serviceWidth : 30 = 0;
mutable int _imageCacheForum : 1 = 0;
mutable int _imageCacheBlurred : 1 = 0;
};

View file

@ -181,6 +181,11 @@ struct Poll::CloseInformation {
Ui::Animations::Basic radial;
};
struct Poll::RecentVoter {
not_null<UserData*> user;
mutable Ui::PeerUserpicView userpic;
};
template <typename Callback>
Poll::SendingAnimation::SendingAnimation(
const QByteArray &option,
@ -886,9 +891,9 @@ void Poll::paintRecentVoters(
auto created = false;
for (auto &recent : _recentVoters) {
const auto was = (recent.userpic != nullptr);
const auto was = !recent.userpic.null();
recent.user->paintUserpic(p, recent.userpic, x, y, size);
if (!was && recent.userpic) {
if (!was && !recent.userpic.null()) {
created = true;
}
const auto paintContent = [&](QPainter &p) {
@ -1499,13 +1504,13 @@ void Poll::clickHandlerPressedChanged(
void Poll::unloadHeavyPart() {
for (auto &recent : _recentVoters) {
recent.userpic = nullptr;
recent.userpic = {};
}
}
bool Poll::hasHeavyPart() const {
for (auto &recent : _recentVoters) {
if (recent.userpic) {
if (!recent.userpic.null()) {
return true;
}
}

View file

@ -12,10 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_poll.h"
#include "base/weak_ptr.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui {
class RippleAnimation;
class FireworksAnimation;
@ -75,11 +71,7 @@ private:
struct SendingAnimation;
struct Answer;
struct CloseInformation;
struct RecentVoter {
not_null<UserData*> user;
mutable std::shared_ptr<Data::CloudImageView> userpic;
};
struct RecentVoter;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;

View file

@ -524,9 +524,9 @@ void InlineList::resolveUserpicsImage(const Button &button) const {
for (auto &entry : userpics->list) {
const auto peer = entry.peer;
auto &view = entry.view;
const auto wasView = view.get();
const auto wasView = view.cloud.get();
if (peer->userpicUniqueKey(view) != entry.uniqueKey
|| view.get() != wasView) {
|| view.cloud.get() != wasView) {
return true;
}
}

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_message_reaction_id.h"
namespace Data {
class CloudImageView;
class Reactions;
} // namespace Data

View file

@ -879,9 +879,17 @@ void Video::prepareThumbnail(QSize size) const {
}
}
}
auto resultThumbnailImage = _documentMedia
? nullptr
: getResultThumb(fileOrigin());
const auto resultThumbnail = Image(resultThumbnailImage
? base::duplicate(*resultThumbnailImage)
: QImage());
const auto thumb = _documentMedia
? _documentMedia->thumbnail()
: getResultThumb(fileOrigin());
: resultThumbnailImage
? &resultThumbnail
: nullptr;
if (!thumb) {
return;
}
@ -1245,7 +1253,7 @@ void Contact::prepareThumbnail(int width, int height) const {
w = width;
}
}
_thumb = thumb->pixNoCache(
_thumb = Image(base::duplicate(*thumb)).pixNoCache(
QSize(w, h) * style::DevicePixelRatio(),
{
.options = Images::Option::TransparentBackground,
@ -1403,7 +1411,7 @@ void Article::prepareThumbnail(int width, int height) const {
w = width;
}
}
_thumb = thumb->pixNoCache(
_thumb = Image(base::duplicate(*thumb)).pixNoCache(
QSize(w, h) * style::DevicePixelRatio(),
{
.options = Images::Option::TransparentBackground,

View file

@ -142,7 +142,7 @@ bool ItemBase::hasResultThumb() const {
|| !_result->_locationThumbnail.empty());
}
Image *ItemBase::getResultThumb(Data::FileOrigin origin) const {
QImage *ItemBase::getResultThumb(Data::FileOrigin origin) const {
if (_result && !_thumbnail) {
if (!_result->_thumbnail.empty()) {
_thumbnail = _result->_thumbnail.createView();
@ -152,7 +152,9 @@ Image *ItemBase::getResultThumb(Data::FileOrigin origin) const {
_result->_locationThumbnail.load(_result->_session, origin);
}
}
return _thumbnail->image();
return (_thumbnail && !_thumbnail->isNull())
? _thumbnail.get()
: nullptr;
}
QPixmap ItemBase::getResultContactAvatar(int width, int height) const {

View file

@ -16,10 +16,6 @@ namespace Ui {
class PathShiftGradient;
} // namespace Ui
namespace Data {
class CloudImageView;
} // namespace Data
namespace InlineBots {
class Result;
@ -126,7 +122,7 @@ protected:
DocumentData *getResultDocument() const;
PhotoData *getResultPhoto() const;
bool hasResultThumb() const;
Image *getResultThumb(Data::FileOrigin origin) const;
QImage *getResultThumb(Data::FileOrigin origin) const;
QPixmap getResultContactAvatar(int width, int height) const;
int getResultDuration() const;
QString getResultUrl() const;
@ -148,7 +144,7 @@ protected:
private:
not_null<Context*> _context;
mutable std::shared_ptr<Data::CloudImageView> _thumbnail;
mutable std::shared_ptr<QImage> _thumbnail;
};

View file

@ -110,8 +110,9 @@ Session::Session(
_user,
Data::PeerUpdate::Flag::Photo
) | rpl::start_with_next([=] {
[[maybe_unused]] const auto image = _user->currentUserpic(
_selfUserpicView);
auto view = Ui::PeerUserpicView{ .cloud = _selfUserpicView };
[[maybe_unused]] const auto image = _user->userpicCloudImage(view);
_selfUserpicView = view.cloud;
}, lifetime());
crl::on_main(this, [=] {

View file

@ -32,7 +32,6 @@ class Templates;
namespace Data {
class Session;
class Changes;
class CloudImageView;
} // namespace Data
namespace Storage {
@ -216,7 +215,7 @@ private:
const std::unique_ptr<Support::Helper> _supportHelper;
std::shared_ptr<Data::CloudImageView> _selfUserpicView;
std::shared_ptr<QImage> _selfUserpicView;
rpl::variable<bool> _premiumPossible = false;
rpl::event_stream<bool> _termsLockChanges;

View file

@ -2827,14 +2827,16 @@ void OverlayWidget::initStreamingThumbnail() {
_touchbarDisplay.fire(TouchBarItemType::Video);
auto userpicImage = std::optional<Image>();
const auto computePhotoThumbnail = [&] {
const auto thumbnail = _photoMedia->image(Data::PhotoSize::Thumbnail);
if (thumbnail) {
return thumbnail;
} else if (_peer && _peer->userpicPhotoId() == _photo->id) {
if (const auto view = _peer->activeUserpicView()) {
if (const auto image = view->image()) {
return image;
if (const auto view = _peer->activeUserpicView(); view.cloud) {
if (!view.cloud->isNull()) {
userpicImage.emplace(base::duplicate(*view.cloud));
return &*userpicImage;
}
}
}
@ -3455,8 +3457,11 @@ void OverlayWidget::validatePhotoCurrentImage() {
&& !_message
&& _peer
&& _peer->hasUserpic()) {
if (const auto view = _peer->activeUserpicView()) {
validatePhotoImage(view->image(), true);
if (const auto view = _peer->activeUserpicView(); view.cloud) {
if (!view.cloud->isNull()) {
auto image = Image(base::duplicate(*view.cloud));
validatePhotoImage(&image, true);
}
}
}
if (_staticContent.isNull()) {

View file

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "base/weak_ptr.h"
#include "window/notifications_utilities.h"
#include "styles/style_window.h"
#include <QtCore/QBuffer>
@ -906,7 +907,7 @@ public:
void showNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -1013,7 +1014,7 @@ Manager::Private::Private(not_null<Manager*> manager)
void Manager::Private::showNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -1041,12 +1042,8 @@ void Manager::Private::showNotification(
}
if (!options.hideNameAndPhoto) {
const auto userpic = peer->isSelf()
? Ui::EmptyUserpic::GenerateSavedMessages(st::notifyMacPhotoSize)
: peer->isRepliesChat()
? Ui::EmptyUserpic::GenerateRepliesMessages(st::notifyMacPhotoSize)
: peer->genUserpic(userpicView, st::notifyMacPhotoSize);
notification->setImage(userpic.toImage());
notification->setImage(
Window::Notifications::GenerateUserpic(peer, userpicView));
}
auto i = _notifications.find(key);
@ -1183,7 +1180,7 @@ Manager::~Manager() = default;
void Manager::doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -22,7 +22,7 @@ protected:
void doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -22,7 +22,7 @@ protected:
void doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h"
#include "main/main_session.h"
#include "mainwindow.h"
#include "window/notification_utilities.h"
#include "styles/style_window.h"
#include <thread>
@ -189,7 +190,7 @@ public:
void showNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -265,7 +266,7 @@ Manager::Private::Private(Manager *manager)
void Manager::Private::showNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -302,12 +303,8 @@ void Manager::Private::showNotification(
[notification setInformativeText:Q2NSString(msg)];
if (!options.hideNameAndPhoto
&& [notification respondsToSelector:@selector(setContentImage:)]) {
auto userpic = peer->isSelf()
? Ui::EmptyUserpic::GenerateSavedMessages(st::notifyMacPhotoSize)
: peer->isRepliesChat()
? Ui::EmptyUserpic::GenerateRepliesMessages(st::notifyMacPhotoSize)
: peer->genUserpic(userpicView, st::notifyMacPhotoSize);
NSImage *img = Q2NSImage(userpic.toImage());
NSImage *img = Q2NSImage(
Window::Notifications::GenerateUserpic(peer, userpicView));
[notification setContentImage:img];
}
@ -475,7 +472,7 @@ Manager::~Manager() = default;
void Manager::doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -84,7 +84,7 @@ QImage ArchiveUserpic(not_null<Data::Folder*> folder) {
auto result = PrepareImage();
Painter paint(&result);
auto view = std::shared_ptr<Data::CloudImageView>();
auto view = Ui::PeerUserpicView();
folder->paintUserpic(paint, 0, 0, result.width());
return result;
}
@ -161,7 +161,7 @@ TimeId CalculateOnlineTill(not_null<PeerData*> peer) {
@implementation PinnedDialogsPanel {
struct Pin {
PeerData *peer = nullptr;
std::shared_ptr<Data::CloudImageView> userpicView = nullptr;
Ui::PeerUserpicView userpicView;
int index = -1;
QImage userpic;
QImage unreadBadge;

View file

@ -405,15 +405,13 @@ void Create(Window::Notifications::System *system) {
#ifndef __MINGW32__
class Manager::Private {
public:
using Type = Window::Notifications::CachedUserpics::Type;
explicit Private(Manager *instance, Type type);
explicit Private(Manager *instance);
bool init();
bool showNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -438,7 +436,7 @@ private:
bool showNotificationInTryCatch(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -460,9 +458,8 @@ private:
};
Manager::Private::Private(Manager *instance, Type type)
: _cachedUserpics(type)
, _guarded(std::make_shared<Manager*>(instance)) {
Manager::Private::Private(Manager *instance)
: _guarded(std::make_shared<Manager*>(instance)) {
ToastActivations(
) | rpl::start_with_next([=](const ToastActivation &activation) {
handleActivation(activation);
@ -657,7 +654,7 @@ void Manager::Private::handleActivation(const ToastActivation &activation) {
bool Manager::Private::showNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -692,7 +689,7 @@ std::wstring Manager::Private::ensureSendButtonIcon() {
bool Manager::Private::showNotificationInTryCatch(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -870,7 +867,7 @@ void Manager::Private::tryHide(const ToastNotification &notification) {
Manager::Manager(Window::Notifications::System *system)
: NativeManager(system)
, _private(std::make_unique<Private>(this, Private::Type::Rounded)) {
, _private(std::make_unique<Private>(this)) {
}
bool Manager::init() {
@ -890,7 +887,7 @@ Manager::~Manager() = default;
void Manager::doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -30,7 +30,7 @@ protected:
void doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -8,10 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "profile/profile_block_widget.h"
namespace Data {
class CloudImageView;
} // namespace Data
#include "ui/userpic_view.h"
namespace Ui {
class RippleAnimation;
@ -33,7 +30,7 @@ public:
~Item();
const not_null<PeerData*> peer;
std::shared_ptr<Data::CloudImageView> userpic;
Ui::PeerUserpicView userpic;
Ui::Text::String name;
QString statusText;
bool statusHasOnlineColor = false;

View file

@ -644,7 +644,7 @@ void SetupAccountsWrap(
}
Ui::RpWidget userpic;
std::shared_ptr<Data::CloudImageView> view;
Ui::PeerUserpicView view;
base::unique_qptr<Ui::PopupMenu> menu;
};
const auto state = raw->lifetime().make_state<State>(raw);

View file

@ -360,7 +360,7 @@ RoundImageCheckbox::RoundImageCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback,
PaintRoundImage &&paintRoundImage,
Fn<ImageRoundRadius()> roundingRadius)
Fn<std::optional<int>(int size)> roundingRadius)
: _st(st)
, _updateCallback(updateCallback)
, _paintRoundImage(std::move(paintRoundImage))
@ -390,8 +390,8 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
if (selectionLevel > 0) {
const auto radius = _roundingRadius
? _roundingRadius()
: ImageRoundRadius::Ellipse;
? _roundingRadius(_st.imageRadius)
: std::optional<int>();
PainterHighQualityEnabler hq(p);
p.setOpacity(std::clamp(selectionLevel, 0., 1.));
p.setBrush(Qt::NoBrush);
@ -405,11 +405,10 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
_st.imageRadius * 2,
_st.imageRadius * 2,
outerWidth);
if (radius == ImageRoundRadius::Ellipse) {
if (!radius) {
p.drawEllipse(rect);
} else {
const auto pxRadius = st::roundRadiusLarge;
p.drawRoundedRect(rect, pxRadius, pxRadius);
p.drawRoundedRect(rect, *radius, *radius);
}
p.setOpacity(1.);
}

View file

@ -52,7 +52,7 @@ public:
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback,
PaintRoundImage &&paintRoundImage,
Fn<ImageRoundRadius()> roundingRadius = nullptr);
Fn<std::optional<int>(int size)> roundingRadius = nullptr);
void paint(Painter &p, int x, int y, int outerWidth) const;
float64 checkedAnimationRatio() const;
@ -76,7 +76,7 @@ private:
const style::RoundImageCheckbox &_st;
Fn<void()> _updateCallback;
PaintRoundImage _paintRoundImage;
Fn<ImageRoundRadius()> _roundingRadius;
Fn<std::optional<int>(int size)> _roundingRadius;
QPixmap _wideCache;
Ui::Animations::Simple _selection;

View file

@ -193,7 +193,7 @@ void PaintInaccessibleAccountInner(
}
}
[[nodiscard]] QPixmap Generate(int size, Fn<void(QPainter&)> callback) {
[[nodiscard]] QImage Generate(int size, Fn<void(QPainter&)> callback) {
auto result = QImage(
QSize(size, size) * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
@ -203,7 +203,7 @@ void PaintInaccessibleAccountInner(
Painter p(&result);
callback(p);
}
return Ui::PixmapFromImage(std::move(result));
return result;
}
} // namespace
@ -286,7 +286,7 @@ void EmptyUserpic::paint(
}
}
void EmptyUserpic::paint(
void EmptyUserpic::paintCircle(
QPainter &p,
int x,
int y,
@ -304,9 +304,6 @@ void EmptyUserpic::paintRounded(
int outerWidth,
int size,
int radius) const {
if (!radius) {
radius = st::roundRadiusSmall;
}
paint(p, x, y, outerWidth, size, [&] {
p.drawRoundedRect(x, y, size, size, radius, radius);
});
@ -338,21 +335,6 @@ void EmptyUserpic::PaintSavedMessages(
PaintSavedMessages(p, x, y, outerWidth, size, QBrush(bg), fg);
}
void EmptyUserpic::PaintSavedMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size) {
auto bg = QLinearGradient(x, y, x, y + size);
bg.setStops({
{ 0., st::historyPeerSavedMessagesBg->c },
{ 1., st::historyPeerSavedMessagesBg2->c }
});
const auto &fg = st::historyPeerUserpicFg;
PaintSavedMessagesRounded(p, x, y, outerWidth, size, QBrush(bg), fg);
}
void EmptyUserpic::PaintSavedMessages(
QPainter &p,
int x,
@ -371,42 +353,12 @@ void EmptyUserpic::PaintSavedMessages(
PaintSavedMessagesInner(p, x, y, size, fg);
}
void EmptyUserpic::PaintSavedMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg) {
x = style::RightToLeft() ? (outerWidth - x - size) : x;
PainterHighQualityEnabler hq(p);
p.setBrush(std::move(bg));
p.setPen(Qt::NoPen);
p.drawRoundedRect(
x,
y,
size,
size,
st::roundRadiusSmall,
st::roundRadiusSmall);
PaintSavedMessagesInner(p, x, y, size, fg);
}
QPixmap EmptyUserpic::GenerateSavedMessages(int size) {
QImage EmptyUserpic::GenerateSavedMessages(int size) {
return Generate(size, [&](QPainter &p) {
PaintSavedMessages(p, 0, 0, size, size);
});
}
QPixmap EmptyUserpic::GenerateSavedMessagesRounded(int size) {
return Generate(size, [&](QPainter &p) {
PaintSavedMessagesRounded(p, 0, 0, size, size);
});
}
void EmptyUserpic::PaintRepliesMessages(
QPainter &p,
int x,
@ -422,21 +374,6 @@ void EmptyUserpic::PaintRepliesMessages(
PaintRepliesMessages(p, x, y, outerWidth, size, QBrush(bg), fg);
}
void EmptyUserpic::PaintRepliesMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size) {
auto bg = QLinearGradient(x, y, x, y + size);
bg.setStops({
{ 0., st::historyPeerSavedMessagesBg->c },
{ 1., st::historyPeerSavedMessagesBg2->c }
});
const auto &fg = st::historyPeerUserpicFg;
PaintRepliesMessagesRounded(p, x, y, outerWidth, size, QBrush(bg), fg);
}
void EmptyUserpic::PaintRepliesMessages(
QPainter &p,
int x,
@ -455,42 +392,12 @@ void EmptyUserpic::PaintRepliesMessages(
PaintRepliesMessagesInner(p, x, y, size, fg);
}
void EmptyUserpic::PaintRepliesMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg) {
x = style::RightToLeft() ? (outerWidth - x - size) : x;
PainterHighQualityEnabler hq(p);
p.setBrush(bg);
p.setPen(Qt::NoPen);
p.drawRoundedRect(
x,
y,
size,
size,
st::roundRadiusSmall,
st::roundRadiusSmall);
PaintRepliesMessagesInner(p, x, y, size, fg);
}
QPixmap EmptyUserpic::GenerateRepliesMessages(int size) {
QImage EmptyUserpic::GenerateRepliesMessages(int size) {
return Generate(size, [&](QPainter &p) {
PaintRepliesMessages(p, 0, 0, size, size);
});
}
QPixmap EmptyUserpic::GenerateRepliesMessagesRounded(int size) {
return Generate(size, [&](QPainter &p) {
PaintRepliesMessagesRounded(p, 0, 0, size, size);
});
}
std::pair<uint64, uint64> EmptyUserpic::uniqueKey() const {
const auto first = (uint64(0xFFFFFFFFU) << 32)
| anim::getPremultiplied(_colors.color1->c);
@ -510,7 +417,7 @@ QPixmap EmptyUserpic::generate(int size) {
result.fill(Qt::transparent);
{
auto p = QPainter(&result);
paint(p, 0, 0, size, size);
paintCircle(p, 0, 0, size, size);
}
return Ui::PixmapFromImage(std::move(result));
}

View file

@ -7,9 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/weak_ptr.h"
namespace Ui {
class EmptyUserpic {
class EmptyUserpic final : public base::has_weak_ptr {
public:
struct BgColors {
const style::color color1;
@ -24,7 +26,7 @@ public:
EmptyUserpic(const BgColors &colors, const QString &name);
void paint(
void paintCircle(
QPainter &p,
int x,
int y,
@ -52,12 +54,6 @@ public:
int y,
int outerWidth,
int size);
static void PaintSavedMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size);
static void PaintSavedMessages(
QPainter &p,
int x,
@ -66,16 +62,7 @@ public:
int size,
QBrush bg,
const style::color &fg);
static void PaintSavedMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg);
[[nodiscard]] static QPixmap GenerateSavedMessages(int size);
[[nodiscard]] static QPixmap GenerateSavedMessagesRounded(int size);
[[nodiscard]] static QImage GenerateSavedMessages(int size);
static void PaintRepliesMessages(
QPainter &p,
@ -83,12 +70,6 @@ public:
int y,
int outerWidth,
int size);
static void PaintRepliesMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size);
static void PaintRepliesMessages(
QPainter &p,
int x,
@ -97,16 +78,7 @@ public:
int size,
QBrush bg,
const style::color &fg);
static void PaintRepliesMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg);
[[nodiscard]] static QPixmap GenerateRepliesMessages(int size);
[[nodiscard]] static QPixmap GenerateRepliesMessagesRounded(int size);
[[nodiscard]] static QImage GenerateRepliesMessages(int size);
~EmptyUserpic();

View file

@ -354,7 +354,7 @@ void UserpicButton::setupPeerViewers() {
) | rpl::filter([=] {
return _waiting;
}) | rpl::start_with_next([=] {
if (!_userpicView || _userpicView->image()) {
if (!Ui::PeerUserpicLoading(_userpicView)) {
_waiting = false;
startNewPhotoShowing();
}
@ -473,12 +473,17 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) {
: false;
auto request = Media::Streaming::FrameRequest();
auto size = QSize{ _st.photoSize, _st.photoSize };
request.outer = size * cIntRetinaFactor();
request.resize = size * cIntRetinaFactor();
const auto ratio = style::DevicePixelRatio();
request.outer = request.resize = size * ratio;
const auto forum = _peer && _peer->isForum();
if (forum) {
request.rounding = Images::CornersMaskRef(
Images::CornersMask(ImageRoundRadius::Large));
const auto radius = int(_st.photoSize
* Ui::ForumUserpicRadiusMultiplier()
* ratio);
if (_roundingCorners[0].width() != radius) {
_roundingCorners = Images::CornersMask(radius);
}
request.rounding = Images::CornersMaskRef(_roundingCorners);
} else {
if (_ellipseMask.size() != request.outer) {
_ellipseMask = Images::EllipseMask(size);
@ -520,7 +525,7 @@ void UserpicButton::processPeerPhoto() {
Expects(_peer != nullptr);
_userpicView = _peer->createUserpicView();
_waiting = _userpicView && !_userpicView->image();
_waiting = Ui::PeerUserpicLoading(_userpicView);
if (_waiting) {
_peer->loadUserpic();
}
@ -798,7 +803,7 @@ void UserpicButton::fillShape(QPainter &p, const style::color &color) const {
p.setBrush(color);
const auto size = _st.photoSize;
if (_peer && _peer->isForum()) {
const auto radius = st::roundRadiusLarge;
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(0, 0, size, size, radius, radius);
} else {
p.drawEllipse(0, 0, size, size);
@ -811,8 +816,8 @@ void UserpicButton::prepareUserpicPixmap() {
}
auto size = _st.photoSize;
_userpicHasImage = _peer
? (_peer->currentUserpic(_userpicView) || _role != Role::ChangePhoto)
: false;
&& (_peer->userpicCloudImage(_userpicView)
|| _role != Role::ChangePhoto);
_userpic = CreateSquarePixmap(size, [&](Painter &p) {
if (_userpicHasImage) {
_peer->paintUserpic(p, _userpicView, 0, 0, _st.photoSize);

View file

@ -11,15 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/tooltip.h"
#include "ui/effects/animations.h"
#include "ui/effects/cross_line.h"
#include "ui/userpic_view.h"
#include "styles/style_window.h"
#include "styles/style_widgets.h"
class PeerData;
namespace Data {
class CloudImageView;
} // namespace Data
namespace Window {
class Controller;
class SessionController;
@ -159,7 +156,7 @@ private:
::Window::SessionController *_controller = nullptr;
::Window::Controller *_window = nullptr;
PeerData *_peer = nullptr;
std::shared_ptr<Data::CloudImageView> _userpicView;
PeerUserpicView _userpicView;
QString _cropTitle;
Role _role = Role::ChangePhoto;
bool _notShownYet = true;
@ -169,28 +166,29 @@ private:
bool _userpicCustom = false;
bool _requestToUpload = false;
InMemoryKey _userpicUniqueKey;
Ui::Animations::Simple _a_appearance;
Animations::Simple _a_appearance;
QImage _result;
QImage _ellipseMask;
std::array<QImage, 4> _roundingCorners;
std::unique_ptr<Media::Streaming::Instance> _streamed;
PhotoData *_streamedPhoto = nullptr;
base::unique_qptr<Ui::PopupMenu> _menu;
base::unique_qptr<PopupMenu> _menu;
bool _showSavedMessagesOnSelf = false;
bool _canOpenPhoto = false;
bool _cursorInChangeOverlay = false;
bool _changeOverlayEnabled = false;
Ui::Animations::Simple _changeOverlayShown;
Animations::Simple _changeOverlayShown;
rpl::event_stream<QImage> _chosenImages;
rpl::event_stream<> _uploadPhotoRequests;
};
class SilentToggle
: public Ui::RippleButton
, public Ui::AbstractTooltipShower {
class SilentToggle final
: public RippleButton
, public AbstractTooltipShower {
public:
SilentToggle(QWidget *parent, not_null<ChannelData*> channel);
@ -218,7 +216,7 @@ private:
not_null<ChannelData*> _channel;
bool _checked = false;
Ui::Animations::Simple _crossLineAnimation;
Animations::Simple _crossLineAnimation;
};

View file

@ -0,0 +1,80 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/userpic_view.h"
#include "ui/empty_userpic.h"
#include "ui/image/image_prepare.h"
namespace Ui {
float64 ForumUserpicRadiusMultiplier() {
return 0.3;
}
bool PeerUserpicLoading(const PeerUserpicView &view) {
return view.cloud && view.cloud->isNull();
}
void ValidateUserpicCache(
PeerUserpicView &view,
const QImage *cloud,
const EmptyUserpic *empty,
int size,
bool forum) {
Expects(cloud != nullptr || empty != nullptr);
const auto full = QSize(size, size);
const auto version = style::PaletteVersion();
const auto forumValue = forum ? 1 : 0;
const auto regenerate = (view.cached.size() != QSize(size, size))
|| (view.forum != forumValue)
|| (cloud && !view.empty.null())
|| (empty && empty != view.empty.get())
|| (empty && view.paletteVersion != version);
if (!regenerate) {
return;
}
view.empty = empty;
view.forum = forumValue;
view.paletteVersion = version;
if (cloud) {
view.cached = cloud->scaled(
full,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
if (forum) {
view.cached = Images::Round(
std::move(view.cached),
Images::CornersMask(
size * Ui::ForumUserpicRadiusMultiplier()));
} else {
view.cached = Images::Circle(std::move(view.cached));
}
} else {
if (view.cached.size() != full) {
view.cached = QImage(full, QImage::Format_ARGB32_Premultiplied);
}
view.cached.fill(Qt::transparent);
auto p = QPainter(&view.cached);
if (forum) {
empty->paintRounded(
p,
0,
0,
size,
size,
size * Ui::ForumUserpicRadiusMultiplier());
} else {
empty->paintCircle(p, 0, 0, size, size);
}
}
}
} // namespace Ui

View file

@ -0,0 +1,41 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/weak_ptr.h"
#include <QtGui/QImage>
namespace Ui {
class EmptyUserpic;
[[nodiscard]] float64 ForumUserpicRadiusMultiplier();
struct PeerUserpicView {
[[nodiscard]] bool null() const {
return cached.isNull() && !cloud && empty.null();
}
QImage cached;
std::shared_ptr<QImage> cloud;
base::weak_ptr<const EmptyUserpic> empty;
int paletteVersion : 31 = 0;
int forum : 1 = 0;
};
[[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view);
void ValidateUserpicCache(
PeerUserpicView &view,
const QImage *cloud,
const EmptyUserpic *empty,
int size,
bool forum);
} // namespace Ui

View file

@ -15,13 +15,16 @@ class History;
namespace Data {
class Session;
class CloudImageView;
class ForumTopic;
class Thread;
struct ItemNotification;
enum class ItemNotificationType;
} // namespace Data
namespace Ui {
struct PeerUserpicView;
} // namespace Ui
namespace Main {
class Session;
} // namespace Main
@ -392,7 +395,7 @@ protected:
virtual void doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,
@ -413,7 +416,7 @@ protected:
void doShowNativeNotification(
not_null<PeerData*> peer,
MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView,
Ui::PeerUserpicView &userpicView,
MsgId msgId,
const QString &title,
const QString &subtitle,

View file

@ -660,7 +660,7 @@ Notification::Notification(
auto position = computePosition(st::notifyMinHeight);
updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight);
_userpicLoaded = !_userpicView || (_userpicView->image() != nullptr);
_userpicLoaded = !Ui::PeerUserpicLoading(_userpicView);
updateNotifyDisplay();
_hideTimer.setSingleShot(true);
@ -1004,7 +1004,7 @@ void Notification::updatePeerPhoto() {
return;
}
_userpicView = _peer->createUserpicView();
if (_userpicView && !_userpicView->image()) {
if (Ui::PeerUserpicLoading(_userpicView)) {
return;
}
_userpicLoaded = true;
@ -1024,7 +1024,7 @@ void Notification::updatePeerPhoto() {
st::notifyPhotoPos.y(),
width(),
st::notifyPhotoSize);
_userpicView = nullptr;
_userpicView = {};
update();
}

View file

@ -11,16 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/text/text.h"
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "base/timer.h"
#include "base/binary_guard.h"
#include "base/object_ptr.h"
#include <QtCore/QTimer>
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui {
class IconButton;
class RoundButton;
@ -290,7 +287,7 @@ private:
History *_history = nullptr;
Data::ForumTopic *_topic = nullptr;
MsgId _topicRootId = 0;
std::shared_ptr<Data::CloudImageView> _userpicView;
Ui::PeerUserpicView _userpicView;
QString _author;
Data::ReactionId _reaction;
HistoryItem *_item = nullptr;

View file

@ -15,8 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h"
#include "styles/style_window.h"
namespace Window {
namespace Notifications {
namespace Window::Notifications {
namespace {
// Delete notify photo file after 1 minute of not using.
@ -24,9 +23,16 @@ constexpr int kNotifyDeletePhotoAfterMs = 60000;
} // namespace
CachedUserpics::CachedUserpics(Type type)
: _type(type)
, _clearTimer([=] { clear(); }) {
QImage GenerateUserpic(not_null<PeerData*> peer, Ui::PeerUserpicView &view) {
return peer->isSelf()
? Ui::EmptyUserpic::GenerateSavedMessages(st::notifyMacPhotoSize)
: peer->isRepliesChat()
? Ui::EmptyUserpic::GenerateRepliesMessages(st::notifyMacPhotoSize)
: peer->generateUserpicImage(view, st::notifyMacPhotoSize);
}
CachedUserpics::CachedUserpics()
: _clearTimer([=] { clear(); }) {
QDir().mkpath(cWorkingDir() + u"tdata/temp"_q);
}
@ -37,14 +43,14 @@ CachedUserpics::~CachedUserpics() {
}
// This works about 1200ms on Windows for a folder with one image O_o
// base::Platform::DeleteDirectory(cWorkingDir() + u"tdata/temp"_q);
//base::Platform::DeleteDirectory(cWorkingDir() + u"tdata/temp"_q);
}
}
QString CachedUserpics::get(
const InMemoryKey &key,
not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view) {
Ui::PeerUserpicView &view) {
auto ms = crl::now();
auto i = _images.find(key);
if (i != _images.cend()) {
@ -64,21 +70,7 @@ QString CachedUserpics::get(
cWorkingDir(),
QString::number(base::RandomValue<uint64>(), 16));
if (key.first || key.second) {
if (peer->isSelf()) {
const auto method = (_type == Type::Rounded)
? Ui::EmptyUserpic::GenerateSavedMessagesRounded
: Ui::EmptyUserpic::GenerateSavedMessages;
method(st::notifyMacPhotoSize).save(v.path, "PNG");
} else if (peer->isRepliesChat()) {
const auto method = (_type == Type::Rounded)
? Ui::EmptyUserpic::GenerateRepliesMessagesRounded
: Ui::EmptyUserpic::GenerateRepliesMessages;
method(st::notifyMacPhotoSize).save(v.path, "PNG");
} else if (_type == Type::Rounded) {
peer->saveUserpicRounded(view, v.path, st::notifyMacPhotoSize);
} else {
peer->saveUserpic(view, v.path, st::notifyMacPhotoSize);
}
GenerateUserpic(peer, view).save(v.path, "PNG");
} else {
LogoNoMargin().save(v.path, "PNG");
}
@ -128,5 +120,4 @@ void CachedUserpics::clear() {
}
}
} // namespace Notifications
} // namespace Window
} // namespace Window::Notifications

View file

@ -10,34 +10,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/notifications_manager.h"
#include "base/timer.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui {
struct PeerUserpicView;
} // namespace Ui
namespace Window {
namespace Notifications {
namespace Window::Notifications {
[[nodiscard]] QImage GenerateUserpic(
not_null<PeerData*> peer,
Ui::PeerUserpicView &view);
class CachedUserpics : public QObject {
public:
enum class Type {
Rounded,
Circled,
};
CachedUserpics(Type type);
CachedUserpics();
~CachedUserpics();
[[nodiscard]] QString get(
const InMemoryKey &key,
not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view);
Ui::PeerUserpicView &view);
private:
void clear();
void clearInMs(int ms);
crl::time clear(crl::time ms);
Type _type = Type::Rounded;
struct Image {
crl::time until = 0;
QString path;
@ -49,5 +46,4 @@ private:
};
} // namesapce Notifications
} // namespace Window
} // namespace Window::Notifications

View file

@ -982,7 +982,7 @@ void Generator::paintUserpic(int x, int y, Row::Type type, int index, QString le
image.fill(Qt::transparent);
{
Painter p(&image);
userpic.paintRounded(p, 0, 0, size, size, size / 2);
userpic.paintCircle(p, 0, 0, size, size);
}
_p->drawImage(rtl() ? (_rect.width() - x - size) : x, y, image);
}

View file

@ -278,6 +278,8 @@ PRIVATE
ui/empty_userpic.h
ui/grouped_layout.cpp
ui/grouped_layout.h
ui/userpic_view.cpp
ui/userpic_view.h
ui/widgets/fields/special_fields.cpp
ui/widgets/fields/special_fields.h
ui/widgets/fields/time_part_input_with_placeholder.cpp

@ -1 +1 @@
Subproject commit e8294bbc9c7f85e6909c3ed06fe03cfd39869613
Subproject commit fd3531b70750ffe7ec2213a899409c08c388ce4c

@ -1 +1 @@
Subproject commit 22ceaae4ed958d9711965fbe4c1385a7d8c60212
Subproject commit 59a7b94ef4bb4d4cf6597d53bdacccbf5720e694