From d775760f988c0b1cda08398d4804733ff66506e8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 3 Jun 2025 17:17:36 +0400 Subject: [PATCH] Support nice monoforum userpics. --- .../SourceFiles/boxes/add_contact_box.cpp | 2 +- .../boxes/moderate_messages_box.cpp | 2 +- Telegram/SourceFiles/data/data_peer.cpp | 22 ++++- Telegram/SourceFiles/data/data_peer.h | 19 ++-- .../history/history_item_components.cpp | 2 +- .../view/history_view_chat_preview.cpp | 2 +- .../view/history_view_top_bar_widget.cpp | 2 +- .../info/profile/info_profile_cover.cpp | 2 +- .../main/main_session_settings.cpp | 7 +- .../settings/settings_websites.cpp | 26 ++--- .../ui/controls/subsection_tabs_slider.cpp | 1 + .../ui/controls/userpic_button.cpp | 72 +++++++------- .../SourceFiles/ui/controls/userpic_button.h | 11 ++- .../SourceFiles/ui/dynamic_thumbnails.cpp | 23 ++--- Telegram/SourceFiles/ui/empty_userpic.cpp | 94 +++++++++++++++++++ Telegram/SourceFiles/ui/empty_userpic.h | 11 +++ Telegram/SourceFiles/ui/userpic_view.cpp | 17 ++-- Telegram/SourceFiles/ui/userpic_view.h | 13 ++- .../window/notifications_manager_default.cpp | 2 +- 19 files changed, 221 insertions(+), 109 deletions(-) diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 020550774f..792eff79b4 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -559,7 +559,7 @@ void GroupInfoBox::prepare() { &_navigation->parentController()->window(), Ui::UserpicButton::Role::ChoosePhoto, st::defaultUserpicButton, - (_type == Type::Forum)); + (_type == Type::Forum) ? Ui::PeerUserpicShape::Forum : Ui::PeerUserpicShape::Auto); _photo->showCustomOnChosen(); _title.create( this, diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index 29f7a22c0e..071b9dd684 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -604,7 +604,7 @@ void DeleteChatBox(not_null box, not_null peer) { container, userpicPeer, st::mainMenuUserpic, - peer->userpicForceForumShape()); + peer->userpicShape()); userpic->showSavedMessagesOnSelf(true); Ui::IconWithTitle( container, diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 7359ef5af4..86fb382f0e 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -427,20 +427,30 @@ QImage *PeerData::userpicCloudImage(Ui::PeerUserpicView &view) const { void PeerData::paintUserpic( Painter &p, Ui::PeerUserpicView &view, - const PaintUserpicContext &context) const { + PaintUserpicContext context) const { if (const auto broadcast = monoforumBroadcast()) { + if (context.shape == Ui::PeerUserpicShape::Auto) { + context.shape = Ui::PeerUserpicShape::Monoforum; + } broadcast->paintUserpic(p, view, context); return; } const auto size = context.size; const auto cloud = userpicCloudImage(view); const auto ratio = style::DevicePixelRatio(); + if (context.shape == Ui::PeerUserpicShape::Auto) { + context.shape = isForum() + ? Ui::PeerUserpicShape::Forum + : isMonoforum() + ? Ui::PeerUserpicShape::Monoforum + : Ui::PeerUserpicShape::Circle; + } Ui::ValidateUserpicCache( view, cloud, cloud ? nullptr : ensureEmptyUserpic().get(), size * ratio, - context.forumLayout); + context.shape); p.drawImage(QRect(context.position, QSize(size, size)), view.cached); } @@ -1176,8 +1186,12 @@ not_null PeerData::userpicPaintingPeer() const { return const_cast(this)->userpicPaintingPeer(); } -bool PeerData::userpicForceForumShape() const { - return monoforumBroadcast() != nullptr; +Ui::PeerUserpicShape PeerData::userpicShape() const { + return isForum() + ? Ui::PeerUserpicShape::Forum + : isMonoforum() + ? Ui::PeerUserpicShape::Monoforum + : Ui::PeerUserpicShape::Circle; } ChannelData *PeerData::monoforumBroadcast() const { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 104e8ba10b..207b7361e2 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -186,6 +186,12 @@ struct PeerBarDetails { int paysPerMessage = 0; }; +struct PaintUserpicContext { + QPoint position; + int size = 0; + Ui::PeerUserpicShape shape = Ui::PeerUserpicShape::Auto; +}; + class PeerData { protected: PeerData(not_null owner, PeerId id); @@ -310,7 +316,7 @@ public: [[nodiscard]] not_null migrateToOrMe() const; [[nodiscard]] not_null userpicPaintingPeer(); [[nodiscard]] not_null userpicPaintingPeer() const; - [[nodiscard]] bool userpicForceForumShape() const; + [[nodiscard]] Ui::PeerUserpicShape userpicShape() const; // isMonoforum() ? monoforumLink() : nullptr [[nodiscard]] ChannelData *monoforumBroadcast() const; @@ -348,15 +354,10 @@ public: bool hasVideo); void setUserpicPhoto(const MTPPhoto &data); - struct PaintUserpicContext { - QPoint position; - int size = 0; - bool forumLayout = false; - }; void paintUserpic( Painter &p, Ui::PeerUserpicView &view, - const PaintUserpicContext &context) const; + PaintUserpicContext context) const; void paintUserpic( Painter &p, Ui::PeerUserpicView &view, @@ -367,7 +368,9 @@ public: paintUserpic(p, view, { .position = { x, y }, .size = size, - .forumLayout = !forceCircle && (isForum() || isMonoforum()), + .shape = (forceCircle + ? Ui::PeerUserpicShape::Circle + : Ui::PeerUserpicShape::Auto), }); } void paintUserpicLeft( diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 28eea2027d..edb6a099da 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -196,7 +196,7 @@ bool HiddenSenderInfo::paintCustomUserpic( image.isNull() ? nullptr : &image, image.isNull() ? &emptyUserpic : nullptr, size * style::DevicePixelRatio(), - false); + Ui::PeerUserpicShape::Circle); p.drawImage(QRect(x, y, size, size), view.cached); return valid; } diff --git a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp index 05ad41d477..5e9d3eecdd 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp @@ -368,7 +368,7 @@ void Item::setupTop() { _top.get(), _thread->peer()->userpicPaintingPeer(), st::previewUserpic, - _thread->peer()->userpicForceForumShape()); + _thread->peer()->userpicShape()); if (userpic) { userpic->showSavedMessagesOnSelf(true); userpic->setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 9f7a8ac8be..e45537d964 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -936,7 +936,7 @@ void TopBarWidget::refreshInfoButton() { Ui::UserpicButton::Role::Custom, Ui::UserpicButton::Source::PeerPhoto, st::topBarInfoButton, - infoPeer->userpicForceForumShape()); + infoPeer->userpicShape()); info->showSavedMessagesOnSelf(true); _info.destroy(); _info = std::move(info); diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index 84786a9ad0..05a11f3008 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -632,7 +632,7 @@ Cover::Cover( Ui::UserpicButton::Role::OpenPhoto, Ui::UserpicButton::Source::PeerPhoto, _st.photo, - _peer->userpicForceForumShape())) + _peer->userpicShape())) , _changePersonal((role == Role::Info || topic || !_peer->isUser() diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp index 58b7578c1b..88f60b5af8 100644 --- a/Telegram/SourceFiles/main/main_session_settings.cpp +++ b/Telegram/SourceFiles/main/main_session_settings.cpp @@ -39,11 +39,10 @@ QByteArray SessionSettings::serialize() const { + Serialize::bytearraySize(autoDownload) + sizeof(qint32) * 11 + (_mutePeriods.size() * sizeof(quint64)) - + sizeof(qint32) * 2 - + _hiddenPinnedMessages.size() * (sizeof(quint64) * 4) - + sizeof(qint32) + + sizeof(qint32) * 3 + _groupEmojiSectionHidden.size() * sizeof(quint64) - + sizeof(qint32) * 2; + + sizeof(qint32) * 3 + + _hiddenPinnedMessages.size() * (sizeof(quint64) * 4); auto result = QByteArray(); result.reserve(size); diff --git a/Telegram/SourceFiles/settings/settings_websites.cpp b/Telegram/SourceFiles/settings/settings_websites.cpp index 836f20ab4d..7f495998f0 100644 --- a/Telegram/SourceFiles/settings/settings_websites.cpp +++ b/Telegram/SourceFiles/settings/settings_websites.cpp @@ -119,7 +119,7 @@ void InfoBox( data.bot, st::websiteBigUserpic)), st::sessionBigCoverPadding)->entity(); - userpic->forceForumShape(true); + userpic->overrideShape(Ui::PeerUserpicShape::Forum); userpic->setAttribute(Qt::WA_TransparentForMouseEvents); const auto nameWrap = box->addRow( @@ -224,25 +224,11 @@ PaintRoundImageCallback Row::generatePaintUserpicCallback(bool forceRound) { const auto peer = _data.bot; auto userpic = _userpic = peer->createUserpicView(); return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { - const auto ratio = style::DevicePixelRatio(); - if (const auto cloud = peer->userpicCloudImage(userpic)) { - Ui::ValidateUserpicCache( - userpic, - cloud, - nullptr, - size * ratio, - true); - p.drawImage(QRect(x, y, size, size), userpic.cached); - } else { - if (_emptyUserpic.isNull()) { - _emptyUserpic = PeerData::GenerateUserpicImage( - peer, - _userpic, - size * ratio, - size * ratio * Ui::ForumUserpicRadiusMultiplier()); - } - p.drawImage(QRect(x, y, size, size), _emptyUserpic); - } + peer->paintUserpic(p, _userpic, { + .position = QPoint(x, y), + .size = size, + .shape = Ui::PeerUserpicShape::Forum, + }); }; } diff --git a/Telegram/SourceFiles/ui/controls/subsection_tabs_slider.cpp b/Telegram/SourceFiles/ui/controls/subsection_tabs_slider.cpp index fbf6df2d0a..eae444f628 100644 --- a/Telegram/SourceFiles/ui/controls/subsection_tabs_slider.cpp +++ b/Telegram/SourceFiles/ui/controls/subsection_tabs_slider.cpp @@ -107,6 +107,7 @@ void VerticalButton::paintEvent(QPaintEvent *e) { .availableWidth = _st.nameWidth, .align = style::al_top, .paused = _delegate->buttonPaused(), + .elisionLines = kMaxNameLines, }); const auto &state = _data.badges; diff --git a/Telegram/SourceFiles/ui/controls/userpic_button.cpp b/Telegram/SourceFiles/ui/controls/userpic_button.cpp index fa7e2c70f7..ed382472df 100644 --- a/Telegram/SourceFiles/ui/controls/userpic_button.cpp +++ b/Telegram/SourceFiles/ui/controls/userpic_button.cpp @@ -160,12 +160,12 @@ UserpicButton::UserpicButton( not_null window, Role role, const style::UserpicButton &st, - bool forceForumShape) + PeerUserpicShape shape) : RippleButton(parent, st.changeButton.ripple) , _st(st) , _controller(window->sessionController()) , _window(window) -, _forceForumShape(forceForumShape) +, _shape(shape) , _role(role) { Expects(_role == Role::ChangePhoto || _role == Role::ChoosePhoto); @@ -181,13 +181,13 @@ UserpicButton::UserpicButton( Role role, Source source, const style::UserpicButton &st, - bool forceForumShape) + PeerUserpicShape shape) : RippleButton(parent, st.changeButton.ripple) , _st(st) , _controller(controller) , _window(&controller->window()) , _peer(peer) -, _forceForumShape(forceForumShape) +, _shape(shape) , _role(role) , _source(source) { if (_source == Source::Custom) { @@ -203,11 +203,11 @@ UserpicButton::UserpicButton( QWidget *parent, not_null peer, const style::UserpicButton &st, - bool forceForumShape) + PeerUserpicShape shape) : RippleButton(parent, st.changeButton.ripple) , _st(st) , _peer(peer) -, _forceForumShape(forceForumShape) +, _shape(shape) , _role(Role::Custom) , _source(Source::PeerPhoto) { Expects(_role != Role::OpenPhoto); @@ -407,7 +407,7 @@ void UserpicButton::choosePhotoLocally() { CameraBox, _window, _peer, - _forceForumShape, + (_shape == PeerUserpicShape::Forum), callback(ChosenType::Set))); }, &st::menuIconPhotoSet); } @@ -648,7 +648,8 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) { auto size = QSize{ _st.photoSize, _st.photoSize }; const auto ratio = style::DevicePixelRatio(); request.outer = request.resize = size * ratio; - if (useForumShape()) { + if (_shape == PeerUserpicShape::Monoforum) { + } else if (useForumShape()) { const auto radius = int(_st.photoSize * Ui::ForumUserpicRadiusMultiplier()); if (_roundingCorners[0].width() != radius * ratio) { @@ -661,7 +662,24 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) { } request.mask = _ellipseMask; } - p.drawImage(QRect(photoPosition, size), _streamed->frame(request)); + auto frame = _streamed->frame(request); + + if (_shape == PeerUserpicShape::Monoforum) { + if (_monoforumMask.isNull()) { + _monoforumMask = MonoforumShapeMask(request.resize); + } + constexpr auto format = QImage::Format_ARGB32_Premultiplied; + if (frame.format() != format) { + frame = std::move(frame).convertToFormat(format); + } + auto q = QPainter(&frame); + q.setCompositionMode(QPainter::CompositionMode_DestinationIn); + q.drawImage( + QRect(QPoint(), frame.size() / frame.devicePixelRatio()), + _monoforumMask); + q.end(); + } + p.drawImage(QRect(photoPosition, size), frame); if (!paused) { _streamed->markFrameShown(); } @@ -892,9 +910,8 @@ void UserpicButton::processNewPeerPhoto() { } bool UserpicButton::useForumShape() const { - return _forceForumShape - || (_peer && _peer->isForum()) - || (_peer && _peer->isMonoforum()); + return (_shape == PeerUserpicShape::Forum) + || (_peer && _peer->isForum() && _shape == PeerUserpicShape::Auto); } void UserpicButton::grabOldUserpic() { @@ -946,8 +963,8 @@ void UserpicButton::switchChangePhotoOverlay( } } -void UserpicButton::forceForumShape(bool force) { - _forceForumShape = force; +void UserpicButton::overrideShape(PeerUserpicShape shape) { + _shape = shape; prepare(); } @@ -1083,28 +1100,11 @@ void UserpicButton::prepareUserpicPixmap() { _userpic = CreateSquarePixmap(size, [&](Painter &p) { if (_userpicHasImage) { if (_showPeerUserpic) { - if (useForumShape()) { - const auto ratio = style::DevicePixelRatio(); - if (const auto cloud = _peer->userpicCloudImage(_userpicView)) { - Ui::ValidateUserpicCache( - _userpicView, - cloud, - nullptr, - size * ratio, - true); - p.drawImage(QRect(0, 0, size, size), _userpicView.cached); - } else { - const auto empty = PeerData::GenerateUserpicImage( - _peer, - _userpicView, - size * ratio, - (size * ratio) - * Ui::ForumUserpicRadiusMultiplier()); - p.drawImage(QRect(0, 0, size, size), empty); - } - } else { - _peer->paintUserpic(p, _userpicView, 0, 0, size); - } + _peer->paintUserpic(p, _userpicView, { + .position = QPoint(), + .size = size, + .shape = _shape, + }); } else if (_nonPersonalView) { using Size = Data::PhotoSize; if (const auto full = _nonPersonalView->image(Size::Large)) { diff --git a/Telegram/SourceFiles/ui/controls/userpic_button.h b/Telegram/SourceFiles/ui/controls/userpic_button.h index 959f980333..f126fac07a 100644 --- a/Telegram/SourceFiles/ui/controls/userpic_button.h +++ b/Telegram/SourceFiles/ui/controls/userpic_button.h @@ -62,7 +62,7 @@ public: not_null<::Window::Controller*> window, Role role, const style::UserpicButton &st, - bool forceForumShape = false); + PeerUserpicShape shape = PeerUserpicShape::Auto); UserpicButton( QWidget *parent, not_null<::Window::SessionController*> controller, @@ -70,12 +70,12 @@ public: Role role, Source source, const style::UserpicButton &st, - bool forceForumShape = false); + PeerUserpicShape shape = PeerUserpicShape::Auto); UserpicButton( QWidget *parent, not_null peer, // Role::Custom, Source::PeerPhoto const style::UserpicButton &st, - bool forceForumShape = false); + PeerUserpicShape shape = PeerUserpicShape::Auto); ~UserpicButton(); enum class ChosenType { @@ -96,7 +96,7 @@ public: bool enabled, Fn chosen); void showSavedMessagesOnSelf(bool enabled); - void forceForumShape(bool force); + void overrideShape(PeerUserpicShape shape); // Role::ChoosePhoto or Role::ChangePhoto [[nodiscard]] rpl::producer chosenImages() const { @@ -163,8 +163,9 @@ private: ::Window::SessionController *_controller = nullptr; ::Window::Controller *_window = nullptr; PeerData *_peer = nullptr; - bool _forceForumShape = false; + PeerUserpicShape _shape = PeerUserpicShape::Auto; PeerUserpicView _userpicView; + QImage _monoforumMask; std::shared_ptr _nonPersonalView; Role _role = Role::ChangePhoto; bool _notShownYet = true; diff --git a/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp b/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp index 42dec878d3..32f164776f 100644 --- a/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp +++ b/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp @@ -250,22 +250,13 @@ QImage PeerUserpic::image(int size) { auto p = Painter(&_frame); auto &view = _subscribed->view; - if (!_forceRound) { - _peer->paintUserpic(p, view, 0, 0, size); - } else if (const auto cloud = _peer->userpicCloudImage(view)) { - const auto full = size * style::DevicePixelRatio(); - Ui::ValidateUserpicCache(view, cloud, nullptr, full, false); - p.drawImage(QRect(0, 0, size, size), view.cached); - } else { - const auto full = size * style::DevicePixelRatio(); - const auto r = full / 2.; - const auto empty = PeerData::GenerateUserpicImage( - _peer, - view, - full, - r); - p.drawImage(QRect(0, 0, size, size), empty); - } + _peer->paintUserpic(p, view, { + .position = QPoint(), + .size = size, + .shape = (_forceRound + ? Ui::PeerUserpicShape::Circle + : Ui::PeerUserpicShape::Auto), + }); } return _frame; } diff --git a/Telegram/SourceFiles/ui/empty_userpic.cpp b/Telegram/SourceFiles/ui/empty_userpic.cpp index ea38baa05d..e731062b67 100644 --- a/Telegram/SourceFiles/ui/empty_userpic.cpp +++ b/Telegram/SourceFiles/ui/empty_userpic.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_widgets.h" // style::IconButton #include "styles/style_info.h" // st::topBarCall +#include #include namespace Ui { @@ -366,6 +367,17 @@ void EmptyUserpic::paintSquare( }); } +void EmptyUserpic::paintMonoforum( + QPainter &p, + int x, + int y, + int outerWidth, + int size) const { + paint(p, x, y, outerWidth, size, [&] { + PaintMonoforumShape(p, QRect(x, y, size, size)); + }); +} + void EmptyUserpic::PaintSavedMessages( QPainter &p, int x, @@ -649,4 +661,86 @@ void EmptyUserpic::fillString(const QString &name) { EmptyUserpic::~EmptyUserpic() = default; +void PaintMonoforumShape(QPainter &p, QRect rect) { + p.drawEllipse(rect); + + auto path = QPainterPath(); + path.moveTo( + rect.x() + rect.width() * 0.5, + rect.y() + rect.height() * 0.5); + path.arcTo( + QRectF( + rect.x() - rect.width() * 0.5, + rect.y(), + rect.width(), + rect.height()), + 0, + -90); + path.arcTo( + QRectF( + rect.x() - rect.width() * 0.25, + rect.y() - rect.height() * 2, + rect.width() * 0.5, + rect.height() * 3), + -90, + 45); + path.lineTo( + rect.x() + rect.width() * 0.5, + rect.y() + rect.height() * 0.5); + p.drawPath(path); +} + +QImage MonoforumShapeMask(QSize size) { + auto result = QImage(size, QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + + QPainter p(&result); + PainterHighQualityEnabler hq(p); + p.setBrush(Qt::white); + p.setPen(Qt::NoPen); + + PaintMonoforumShape(p, QRect(QPoint(), size)); + + p.end(); + + return result; +} + +const QImage &MonoforumShapeMaskCached(QSize size) { + const auto key = (uint64(uint32(size.width())) << 32) + | uint64(uint32(size.height())); + + static auto Masks = base::flat_map(); + static auto Mutex = QMutex(); + auto lock = QMutexLocker(&Mutex); + const auto i = Masks.find(key); + if (i != end(Masks)) { + return i->second; + } + lock.unlock(); + + auto mask = MonoforumShapeMask(size); + + lock.relock(); + return Masks.emplace(key, std::move(mask)).first->second; +} + +QImage ApplyMonoforumShape(QImage image) { + const auto size = image.size(); + auto mask = MonoforumShapeMaskCached(size); + + constexpr auto format = QImage::Format_ARGB32_Premultiplied; + if (image.format() != format) { + image = std::move(image).convertToFormat(format); + } + auto p = QPainter(&image); + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + p.drawImage( + QRect(QPoint(), image.size() / image.devicePixelRatio()), + mask); + p.end(); + + return image; +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/empty_userpic.h b/Telegram/SourceFiles/ui/empty_userpic.h index fba1fd88ad..6e2e903cec 100644 --- a/Telegram/SourceFiles/ui/empty_userpic.h +++ b/Telegram/SourceFiles/ui/empty_userpic.h @@ -46,6 +46,12 @@ public: int y, int outerWidth, int size) const; + void paintMonoforum( + QPainter &p, + int x, + int y, + int outerWidth, + int size) const; [[nodiscard]] QPixmap generate(int size); [[nodiscard]] std::pair uniqueKey() const; @@ -147,4 +153,9 @@ private: }; +void PaintMonoforumShape(QPainter &p, QRect rect); +[[nodiscard]] QImage MonoforumShapeMask(QSize size); +[[nodiscard]] const QImage &MonoforumShapeMaskCached(QSize size); +[[nodiscard]] QImage ApplyMonoforumShape(QImage image); + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/userpic_view.cpp b/Telegram/SourceFiles/ui/userpic_view.cpp index f14ac76e80..ff5f336d42 100644 --- a/Telegram/SourceFiles/ui/userpic_view.cpp +++ b/Telegram/SourceFiles/ui/userpic_view.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/userpic_view.h" #include "ui/empty_userpic.h" +#include "ui/painter.h" #include "ui/image/image_prepare.h" namespace Ui { @@ -25,14 +26,14 @@ void ValidateUserpicCache( const QImage *cloud, const EmptyUserpic *empty, int size, - bool forum) { + PeerUserpicShape shape) { Expects(cloud != nullptr || empty != nullptr); const auto full = QSize(size, size); const auto version = style::PaletteVersion(); - const auto forumValue = forum ? 1 : 0; + const auto shapeValue = static_cast(shape) & 3; const auto regenerate = (view.cached.size() != QSize(size, size)) - || (view.forum != forumValue) + || (view.shape != shapeValue) || (cloud && !view.empty.null()) || (empty && empty != view.empty.get()) || (empty && view.paletteVersion != version); @@ -40,7 +41,7 @@ void ValidateUserpicCache( return; } view.empty = empty; - view.forum = forumValue; + view.shape = shapeValue; view.paletteVersion = version; if (cloud) { @@ -48,7 +49,9 @@ void ValidateUserpicCache( full, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - if (forum) { + if (shape == PeerUserpicShape::Monoforum) { + view.cached = Ui::ApplyMonoforumShape(std::move(view.cached)); + } else if (shape == PeerUserpicShape::Forum) { view.cached = Images::Round( std::move(view.cached), Images::CornersMask(size @@ -64,7 +67,9 @@ void ValidateUserpicCache( view.cached.fill(Qt::transparent); auto p = QPainter(&view.cached); - if (forum) { + if (shape == PeerUserpicShape::Monoforum) { + empty->paintMonoforum(p, 0, 0, size, size); + } else if (shape == PeerUserpicShape::Forum) { empty->paintRounded( p, 0, diff --git a/Telegram/SourceFiles/ui/userpic_view.h b/Telegram/SourceFiles/ui/userpic_view.h index 6e50127d74..0bd9cbcedc 100644 --- a/Telegram/SourceFiles/ui/userpic_view.h +++ b/Telegram/SourceFiles/ui/userpic_view.h @@ -17,6 +17,13 @@ class EmptyUserpic; [[nodiscard]] float64 ForumUserpicRadiusMultiplier(); +enum class PeerUserpicShape : uint8 { + Auto, + Circle, + Forum, + Monoforum, +}; + struct PeerUserpicView { [[nodiscard]] bool null() const { return cached.isNull() && !cloud && empty.null(); @@ -25,8 +32,8 @@ struct PeerUserpicView { QImage cached; std::shared_ptr cloud; base::weak_ptr empty; - uint32 paletteVersion : 31 = 0; - uint32 forum : 1 = 0; + uint32 paletteVersion : 30 = 0; + uint32 shape : 2 = 0; }; [[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view); @@ -36,6 +43,6 @@ void ValidateUserpicCache( const QImage *cloud, const EmptyUserpic *empty, int size, - bool forum); + PeerUserpicShape shape); } // namespace Ui diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index dcfd89dec5..e5a0a78ac1 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -656,7 +656,7 @@ Notification::Notification( , _topicRootId(topicRootId) , _sublist(history->peer->monoforumSublistFor(monoforumPeerId)) , _monoforumPeerId(monoforumPeerId) -, _userpicView(_peer->createUserpicView()) +, _userpicView(_peer->userpicPaintingPeer()->createUserpicView()) , _author(author) , _reaction(reaction) , _item(item)