From 549de7fa54d501ee983ed5d4d21111e6739161e5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 6 Jan 2025 11:59:40 +0400 Subject: [PATCH] Show both verify and status emoji in opened chat. --- Telegram/SourceFiles/boxes/peer_list_box.cpp | 40 ++-- .../dialogs/dialogs_inner_widget.cpp | 59 +++-- .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 64 +++--- .../view/history_view_top_bar_widget.cpp | 31 ++- Telegram/SourceFiles/info/info.style | 2 +- .../info/profile/info_profile_badge.cpp | 50 +++-- Telegram/SourceFiles/ui/unread_badge.cpp | 208 +++++++++++------- Telegram/SourceFiles/ui/unread_badge.h | 17 +- 8 files changed, 265 insertions(+), 206 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index c72c392a0..22e086933 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -788,31 +788,29 @@ int PeerListRow::paintNameIconGetWidth( || _isVerifyCodesChat) { return 0; } - return _badge.drawGetWidth( - p, - QRect( + return _badge.drawGetWidth(p, { + .peer = peer(), + .rectForName = QRect( nameLeft, nameTop, availableWidth, st::semiboldFont->height), - nameWidth, - outerWidth, - { - .peer = peer(), - .verified = &(selected - ? st::dialogsVerifiedIconOver - : st::dialogsVerifiedIcon), - .premium = &(selected - ? st::dialogsPremiumIcon.over - : st::dialogsPremiumIcon.icon), - .scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg), - .premiumFg = &(selected - ? st::dialogsVerifiedIconBgOver - : st::dialogsVerifiedIconBg), - .customEmojiRepaint = repaint, - .now = now, - .paused = false, - }); + .nameWidth = nameWidth, + .outerWidth = outerWidth, + .verified = &(selected + ? st::dialogsVerifiedIconOver + : st::dialogsVerifiedIcon), + .premium = &(selected + ? st::dialogsPremiumIcon.over + : st::dialogsPremiumIcon.icon), + .scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg), + .premiumFg = &(selected + ? st::dialogsVerifiedIconBgOver + : st::dialogsVerifiedIconBg), + .customEmojiRepaint = repaint, + .now = now, + .paused = false, + }); } void PeerListRow::paintStatusText( diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index daa218816..99b87f3d4 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1447,36 +1447,35 @@ void InnerWidget::paintPeerSearchResult( + chatTypeIcon->width() + st::dialogsChatTypeSkip); } - const auto badgeWidth = result->badge.drawGetWidth( - p, - rectForName, - result->name.maxWidth(), - context.width, - { - .peer = peer, - .verified = (context.active - ? &st::dialogsVerifiedIconActive - : context.selected - ? &st::dialogsVerifiedIconOver - : &st::dialogsVerifiedIcon), - .premium = &ThreeStateIcon( - st::dialogsPremiumIcon, - context.active, - context.selected), - .scam = (context.active - ? &st::dialogsScamFgActive - : context.selected - ? &st::dialogsScamFgOver - : &st::dialogsScamFg), - .premiumFg = (context.active - ? &st::dialogsVerifiedIconBgActive - : context.selected - ? &st::dialogsVerifiedIconBgOver - : &st::dialogsVerifiedIconBg), - .customEmojiRepaint = [=] { updateSearchResult(peer); }, - .now = context.now, - .paused = context.paused, - }); + const auto badgeWidth = result->badge.drawGetWidth(p, { + .peer = peer, + .rectForName = rectForName, + .nameWidth = result->name.maxWidth(), + .outerWidth = context.width, + .verified = (context.active + ? &st::dialogsVerifiedIconActive + : context.selected + ? &st::dialogsVerifiedIconOver + : &st::dialogsVerifiedIcon), + .premium = &ThreeStateIcon( + st::dialogsPremiumIcon, + context.active, + context.selected), + .scam = (context.active + ? &st::dialogsScamFgActive + : context.selected + ? &st::dialogsScamFgOver + : &st::dialogsScamFg), + .premiumFg = (context.active + ? &st::dialogsVerifiedIconBgActive + : context.selected + ? &st::dialogsVerifiedIconBgOver + : &st::dialogsVerifiedIconBg), + .customEmojiRepaint = [=] { updateSearchResult(peer); }, + .now = context.now, + .prioritizeVerification = true, + .paused = context.paused, + }); rectForName.setWidth(rectForName.width() - badgeWidth); QRect tr(context.st->textLeft, context.st->textTop, namewidth, st::dialogsTextFont->height); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index e5b80ef19..f37b6c537 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -729,39 +729,41 @@ void PaintRow( : context.selected ? st::dialogsNameFgOver : st::dialogsNameFg); - p.drawTextLeft(rectForName.left(), rectForName.top(), context.width, text); + p.drawTextLeft( + rectForName.left(), + rectForName.top(), + context.width, + text); } else if (from) { if ((history || sublist) && !context.search) { - const auto badgeWidth = rowBadge.drawGetWidth( - p, - rectForName, - rowName.maxWidth(), - context.width, - { - .peer = from, - .verified = (context.active - ? &st::dialogsVerifiedIconActive - : context.selected - ? &st::dialogsVerifiedIconOver - : &st::dialogsVerifiedIcon), - .premium = &ThreeStateIcon( - st::dialogsPremiumIcon, - context.active, - context.selected), - .scam = (context.active - ? &st::dialogsScamFgActive - : context.selected - ? &st::dialogsScamFgOver - : &st::dialogsScamFg), - .premiumFg = (context.active - ? &st::dialogsVerifiedIconBgActive - : context.selected - ? &st::dialogsVerifiedIconBgOver - : &st::dialogsVerifiedIconBg), - .customEmojiRepaint = customEmojiRepaint, - .now = context.now, - .paused = context.paused, - }); + const auto badgeWidth = rowBadge.drawGetWidth(p, { + .peer = from, + .rectForName = rectForName, + .nameWidth = rowName.maxWidth(), + .outerWidth = context.width, + .verified = (context.active + ? &st::dialogsVerifiedIconActive + : context.selected + ? &st::dialogsVerifiedIconOver + : &st::dialogsVerifiedIcon), + .premium = &ThreeStateIcon( + st::dialogsPremiumIcon, + context.active, + context.selected), + .scam = (context.active + ? &st::dialogsScamFgActive + : context.selected + ? &st::dialogsScamFgOver + : &st::dialogsScamFg), + .premiumFg = (context.active + ? &st::dialogsVerifiedIconBgActive + : context.selected + ? &st::dialogsVerifiedIconBgOver + : &st::dialogsVerifiedIconBg), + .customEmojiRepaint = customEmojiRepaint, + .now = context.now, + .paused = context.paused, + }); rectForName.setWidth(rectForName.width() - badgeWidth); } p.setPen(context.active 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 cbb94bbce..c0a6c9e7b 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -578,26 +578,25 @@ void TopBarWidget::paintTopBar(Painter &p) { nameleft += skip + st::dialogsChatTypeSkip; namewidth -= skip + st::dialogsChatTypeSkip; } - const auto badgeWidth = _titleBadge.drawGetWidth( - p, - QRect( + const auto badgeWidth = _titleBadge.drawGetWidth(p, { + .peer = namePeer, + .rectForName = QRect( nameleft, nametop, namewidth, st::msgNameStyle.font->height), - _title.maxWidth(), - width(), - { - .peer = namePeer, - .verified = &st::dialogsVerifiedIcon, - .premium = &st::dialogsPremiumIcon.icon, - .scam = &st::attentionButtonFg, - .premiumFg = &st::dialogsVerifiedIconBg, - .customEmojiRepaint = [=] { update(); }, - .now = now, - .paused = _controller->isGifPausedAtLeastFor( - Window::GifPauseReason::Any), - }); + .nameWidth = _title.maxWidth(), + .outerWidth = width(), + .verified = &st::dialogsVerifiedIcon, + .premium = &st::dialogsPremiumIcon.icon, + .scam = &st::attentionButtonFg, + .premiumFg = &st::dialogsVerifiedIconBg, + .customEmojiRepaint = [=] { update(); }, + .now = now, + .bothVerifyAndStatus = true, + .paused = _controller->isGifPausedAtLeastFor( + Window::GifPauseReason::Any), + }); namewidth -= badgeWidth; p.setPen(st::dialogsNameFg); diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index c066bccdf..96a0089fd 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -440,7 +440,7 @@ infoPeerBadge: InfoPeerBadge { premium: infoPremiumStar; premiumFg: profileVerifiedCheckBg; position: infoVerifiedCheckPosition; - sizeTag: 0; // Large + sizeTag: 0; // Normal } infoIconFg: windowBoldFg; diff --git a/Telegram/SourceFiles/info/profile/info_profile_badge.cpp b/Telegram/SourceFiles/info/profile/info_profile_badge.cpp index 07f63b2fe..8b0bb185f 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_badge.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_badge.cpp @@ -39,6 +39,11 @@ namespace { }); } +[[nodiscard]] bool HasPremiumClick(const Badge::Content &content) { + return content.badge == BadgeType::Premium + || (content.badge == BadgeType::Verified && content.emojiStatusId); +} + } // namespace Badge::Badge( @@ -114,6 +119,15 @@ void Badge::setContent(Content content) { case BadgeType::Verified: case BadgeType::Premium: { const auto id = _content.emojiStatusId; + const auto emoji = id + ? (Data::FrameSizeFromTag(sizeTag()) + / style::DevicePixelRatio()) + : 0; + const auto icon = (_content.badge == BadgeType::Verified) + ? &_st.verified + : id + ? nullptr + : &_st.premium; if (id) { _emojiStatus = _session->data().customEmojiManager().create( id, @@ -121,14 +135,16 @@ void Badge::setContent(Content content) { sizeTag()); if (_customStatusLoopsLimit > 0) { _emojiStatus = std::make_unique( - std::move(_emojiStatus), - _customStatusLoopsLimit); + std::move(_emojiStatus), + _customStatusLoopsLimit); } - const auto emoji = Data::FrameSizeFromTag(sizeTag()) - / style::DevicePixelRatio(); - _view->resize(emoji, emoji); - _view->paintRequest( - ) | rpl::start_with_next([=, check = _view.data()]{ + } + const auto width = emoji + (icon ? icon->width() : 0); + const auto height = std::max(emoji, icon ? icon->height() : 0); + _view->resize(width, height); + _view->paintRequest( + ) | rpl::start_with_next([=, check = _view.data()]{ + if (_emojiStatus) { auto args = Ui::Text::CustomEmoji::Context{ .textColor = _st.premiumFg->c, .now = crl::now(), @@ -140,18 +156,12 @@ void Badge::setContent(Content content) { Painter p(check); _emojiStatus->paint(p, args); } - }, _view->lifetime()); - } else { - const auto icon = (_content.badge == BadgeType::Verified) - ? &_st.verified - : &_st.premium; - _view->resize(icon->size()); - _view->paintRequest( - ) | rpl::start_with_next([=, check = _view.data()]{ + } + if (icon) { Painter p(check); - icon->paint(p, 0, 0, check->width()); - }, _view->lifetime()); - } + icon->paint(p, emoji, 0, check->width()); + } + }, _view->lifetime()); } break; case BadgeType::Scam: case BadgeType::Fake: { @@ -174,7 +184,7 @@ void Badge::setContent(Content content) { } break; } - if (_content.badge != BadgeType::Premium || !_premiumClickCallback) { + if (!HasPremiumClick(_content) || !_premiumClickCallback) { _view->setAttribute(Qt::WA_TransparentForMouseEvents); } else { _view->setClickedCallback(_premiumClickCallback); @@ -185,7 +195,7 @@ void Badge::setContent(Content content) { void Badge::setPremiumClickCallback(Fn callback) { _premiumClickCallback = std::move(callback); - if (_view && _content.badge == BadgeType::Premium) { + if (_view && HasPremiumClick(_content)) { if (!_premiumClickCallback) { _view->setAttribute(Qt::WA_TransparentForMouseEvents); } else { diff --git a/Telegram/SourceFiles/ui/unread_badge.cpp b/Telegram/SourceFiles/ui/unread_badge.cpp index 4448d57db..16ea25dd3 100644 --- a/Telegram/SourceFiles/ui/unread_badge.cpp +++ b/Telegram/SourceFiles/ui/unread_badge.cpp @@ -128,98 +128,144 @@ PeerBadge::PeerBadge() = default; PeerBadge::~PeerBadge() = default; -int PeerBadge::drawGetWidth( - Painter &p, - QRect rectForName, - int nameWidth, - int outerWidth, - const Descriptor &descriptor) { +int PeerBadge::drawGetWidth(Painter &p, Descriptor &&descriptor) { Expects(descriptor.customEmojiRepaint != nullptr); const auto peer = descriptor.peer; if (descriptor.scam && (peer->isScam() || peer->isFake())) { - const auto phrase = peer->isScam() - ? tr::lng_scam_badge(tr::now) - : tr::lng_fake_badge(tr::now); - const auto phraseWidth = st::dialogsScamFont->width(phrase); - const auto width = st::dialogsScamPadding.left() - + phraseWidth - + st::dialogsScamPadding.right(); - const auto height = st::dialogsScamPadding.top() - + st::dialogsScamFont->height - + st::dialogsScamPadding.bottom(); - const auto rect = QRect( - (rectForName.x() - + qMin( - nameWidth + st::dialogsScamSkip, - rectForName.width() - width)), - rectForName.y() + (rectForName.height() - height) / 2, - width, - height); - DrawScamFakeBadge( - p, - rect, - outerWidth, - *descriptor.scam, - phrase, - phraseWidth); - return st::dialogsScamSkip + width; - } else if (descriptor.premium + return drawScamOrFake(p, descriptor); + } + const auto verifyCheck = descriptor.verified && peer->isVerified(); + const auto premiumMark = descriptor.premium + && peer->session().premiumBadgesShown(); + const auto emojiStatus = premiumMark && peer->emojiStatusId() - && (peer->isPremium() || peer->isChannel()) - && peer->session().premiumBadgesShown()) { - const auto id = peer->emojiStatusId(); - const auto iconw = descriptor.premium->width(); - const auto iconx = rectForName.x() - + qMin(nameWidth, rectForName.width() - iconw); - const auto icony = rectForName.y(); - if (!_emojiStatus) { - _emojiStatus = std::make_unique(); - const auto size = st::emojiSize; - const auto emoji = Ui::Text::AdjustCustomEmojiSize(size); - _emojiStatus->skip = (size - emoji) / 2; + && (peer->isPremium() || peer->isChannel()); + const auto premiumStar = premiumMark + && !emojiStatus + && peer->isPremium(); + + const auto paintVerify = verifyCheck + && (descriptor.prioritizeVerification + || descriptor.bothVerifyAndStatus + || !emojiStatus); + const auto paintEmoji = emojiStatus + && (!paintVerify || descriptor.bothVerifyAndStatus); + const auto paintStar = premiumStar && !paintVerify; + + auto result = 0; + if (paintEmoji) { + auto &rectForName = descriptor.rectForName; + const auto verifyWidth = descriptor.verified->width(); + if (paintVerify) { + rectForName.setWidth(rectForName.width() - verifyWidth); } - if (_emojiStatus->id != id) { - using namespace Ui::Text; - auto &manager = peer->session().data().customEmojiManager(); - _emojiStatus->id = id; - _emojiStatus->emoji = std::make_unique( - manager.create( - id, - descriptor.customEmojiRepaint), - kPlayStatusLimit); + result += drawPremiumEmojiStatus(p, descriptor); + if (!paintVerify) { + return result; } - _emojiStatus->emoji->paint(p, { - .textColor = (*descriptor.premiumFg)->c, - .now = descriptor.now, - .position = QPoint( - iconx - 2 * _emojiStatus->skip, - icony + _emojiStatus->skip), - .paused = descriptor.paused || On(PowerSaving::kEmojiStatus), - }); - return iconw - 4 * _emojiStatus->skip; - } else if (descriptor.verified && peer->isVerified()) { - const auto iconw = descriptor.verified->width(); - descriptor.verified->paint( - p, - rectForName.x() + qMin(nameWidth, rectForName.width() - iconw), - rectForName.y(), - outerWidth); - return iconw; - } else if (descriptor.premium - && peer->isPremium() - && peer->session().premiumBadgesShown()) { - const auto iconw = descriptor.premium->width(); - const auto iconx = rectForName.x() - + qMin(nameWidth, rectForName.width() - iconw); - const auto icony = rectForName.y(); - _emojiStatus = nullptr; - descriptor.premium->paint(p, iconx, icony, outerWidth); - return iconw; + rectForName.setWidth(rectForName.width() + verifyWidth); + descriptor.nameWidth += result; + } + if (paintVerify) { + result += drawVerifyCheck(p, descriptor); + return result; + } else if (paintStar) { + return drawPremiumStar(p, descriptor); } return 0; } +int PeerBadge::drawScamOrFake(Painter &p, const Descriptor &descriptor) { + const auto phrase = descriptor.peer->isScam() + ? tr::lng_scam_badge(tr::now) + : tr::lng_fake_badge(tr::now); + const auto phraseWidth = st::dialogsScamFont->width(phrase); + const auto width = st::dialogsScamPadding.left() + + phraseWidth + + st::dialogsScamPadding.right(); + const auto height = st::dialogsScamPadding.top() + + st::dialogsScamFont->height + + st::dialogsScamPadding.bottom(); + const auto rectForName = descriptor.rectForName; + const auto rect = QRect( + (rectForName.x() + + qMin( + descriptor.nameWidth + st::dialogsScamSkip, + rectForName.width() - width)), + rectForName.y() + (rectForName.height() - height) / 2, + width, + height); + DrawScamFakeBadge( + p, + rect, + descriptor.outerWidth, + *descriptor.scam, + phrase, + phraseWidth); + return st::dialogsScamSkip + width; +} + +int PeerBadge::drawVerifyCheck(Painter &p, const Descriptor &descriptor) { + const auto iconw = descriptor.verified->width(); + const auto rectForName = descriptor.rectForName; + const auto nameWidth = descriptor.nameWidth; + descriptor.verified->paint( + p, + rectForName.x() + qMin(nameWidth, rectForName.width() - iconw), + rectForName.y(), + descriptor.outerWidth); + return iconw; +} + +int PeerBadge::drawPremiumEmojiStatus( + Painter &p, + const Descriptor &descriptor) { + const auto peer = descriptor.peer; + const auto id = peer->emojiStatusId(); + const auto rectForName = descriptor.rectForName; + const auto iconw = descriptor.premium->width(); + const auto iconx = rectForName.x() + + qMin(descriptor.nameWidth, rectForName.width() - iconw); + const auto icony = rectForName.y(); + if (!_emojiStatus) { + _emojiStatus = std::make_unique(); + const auto size = st::emojiSize; + const auto emoji = Ui::Text::AdjustCustomEmojiSize(size); + _emojiStatus->skip = (size - emoji) / 2; + } + if (_emojiStatus->id != id) { + using namespace Ui::Text; + auto &manager = peer->session().data().customEmojiManager(); + _emojiStatus->id = id; + _emojiStatus->emoji = std::make_unique( + manager.create( + id, + descriptor.customEmojiRepaint), + kPlayStatusLimit); + } + _emojiStatus->emoji->paint(p, { + .textColor = (*descriptor.premiumFg)->c, + .now = descriptor.now, + .position = QPoint( + iconx - 2 * _emojiStatus->skip, + icony + _emojiStatus->skip), + .paused = descriptor.paused || On(PowerSaving::kEmojiStatus), + }); + return iconw - 4 * _emojiStatus->skip; +} + +int PeerBadge::drawPremiumStar(Painter &p, const Descriptor &descriptor) { + const auto rectForName = descriptor.rectForName; + const auto iconw = descriptor.premium->width(); + const auto iconx = rectForName.x() + + qMin(descriptor.nameWidth, rectForName.width() - iconw); + const auto icony = rectForName.y(); + _emojiStatus = nullptr; + descriptor.premium->paint(p, iconx, icony, descriptor.outerWidth); + return iconw; +} + void PeerBadge::unload() { _emojiStatus = nullptr; } diff --git a/Telegram/SourceFiles/ui/unread_badge.h b/Telegram/SourceFiles/ui/unread_badge.h index 84fb5f9e9..5fd7990c4 100644 --- a/Telegram/SourceFiles/ui/unread_badge.h +++ b/Telegram/SourceFiles/ui/unread_badge.h @@ -52,20 +52,20 @@ public: struct Descriptor { not_null peer; + QRect rectForName; + int nameWidth = 0; + int outerWidth = 0; const style::icon *verified = nullptr; const style::icon *premium = nullptr; const style::color *scam = nullptr; const style::color *premiumFg = nullptr; Fn customEmojiRepaint; crl::time now = 0; + bool prioritizeVerification = false; + bool bothVerifyAndStatus = false; bool paused = false; }; - int drawGetWidth( - Painter &p, - QRect rectForName, - int nameWidth, - int outerWidth, - const Descriptor &descriptor); + int drawGetWidth(Painter &p, Descriptor &&descriptor); void unload(); [[nodiscard]] bool ready(const BotVerifyDetails *details) const; @@ -84,6 +84,11 @@ private: struct EmojiStatus; struct BotVerifiedData; + int drawScamOrFake(Painter &p, const Descriptor &descriptor); + int drawVerifyCheck(Painter &p, const Descriptor &descriptor); + int drawPremiumEmojiStatus(Painter &p, const Descriptor &descriptor); + int drawPremiumStar(Painter &p, const Descriptor &descriptor); + std::unique_ptr _emojiStatus; mutable std::unique_ptr _botVerifiedData;