Show both verify and status emoji in opened chat.

This commit is contained in:
John Preston 2025-01-06 11:59:40 +04:00
parent ecf9faa21d
commit 549de7fa54
8 changed files with 265 additions and 206 deletions

View file

@ -788,17 +788,15 @@ int PeerListRow::paintNameIconGetWidth(
|| _isVerifyCodesChat) { || _isVerifyCodesChat) {
return 0; return 0;
} }
return _badge.drawGetWidth( return _badge.drawGetWidth(p, {
p, .peer = peer(),
QRect( .rectForName = QRect(
nameLeft, nameLeft,
nameTop, nameTop,
availableWidth, availableWidth,
st::semiboldFont->height), st::semiboldFont->height),
nameWidth, .nameWidth = nameWidth,
outerWidth, .outerWidth = outerWidth,
{
.peer = peer(),
.verified = &(selected .verified = &(selected
? st::dialogsVerifiedIconOver ? st::dialogsVerifiedIconOver
: st::dialogsVerifiedIcon), : st::dialogsVerifiedIcon),

View file

@ -1447,13 +1447,11 @@ void InnerWidget::paintPeerSearchResult(
+ chatTypeIcon->width() + chatTypeIcon->width()
+ st::dialogsChatTypeSkip); + st::dialogsChatTypeSkip);
} }
const auto badgeWidth = result->badge.drawGetWidth( const auto badgeWidth = result->badge.drawGetWidth(p, {
p,
rectForName,
result->name.maxWidth(),
context.width,
{
.peer = peer, .peer = peer,
.rectForName = rectForName,
.nameWidth = result->name.maxWidth(),
.outerWidth = context.width,
.verified = (context.active .verified = (context.active
? &st::dialogsVerifiedIconActive ? &st::dialogsVerifiedIconActive
: context.selected : context.selected
@ -1475,6 +1473,7 @@ void InnerWidget::paintPeerSearchResult(
: &st::dialogsVerifiedIconBg), : &st::dialogsVerifiedIconBg),
.customEmojiRepaint = [=] { updateSearchResult(peer); }, .customEmojiRepaint = [=] { updateSearchResult(peer); },
.now = context.now, .now = context.now,
.prioritizeVerification = true,
.paused = context.paused, .paused = context.paused,
}); });
rectForName.setWidth(rectForName.width() - badgeWidth); rectForName.setWidth(rectForName.width() - badgeWidth);

View file

@ -729,16 +729,18 @@ void PaintRow(
: context.selected : context.selected
? st::dialogsNameFgOver ? st::dialogsNameFgOver
: st::dialogsNameFg); : st::dialogsNameFg);
p.drawTextLeft(rectForName.left(), rectForName.top(), context.width, text); p.drawTextLeft(
rectForName.left(),
rectForName.top(),
context.width,
text);
} else if (from) { } else if (from) {
if ((history || sublist) && !context.search) { if ((history || sublist) && !context.search) {
const auto badgeWidth = rowBadge.drawGetWidth( const auto badgeWidth = rowBadge.drawGetWidth(p, {
p,
rectForName,
rowName.maxWidth(),
context.width,
{
.peer = from, .peer = from,
.rectForName = rectForName,
.nameWidth = rowName.maxWidth(),
.outerWidth = context.width,
.verified = (context.active .verified = (context.active
? &st::dialogsVerifiedIconActive ? &st::dialogsVerifiedIconActive
: context.selected : context.selected

View file

@ -578,23 +578,22 @@ void TopBarWidget::paintTopBar(Painter &p) {
nameleft += skip + st::dialogsChatTypeSkip; nameleft += skip + st::dialogsChatTypeSkip;
namewidth -= skip + st::dialogsChatTypeSkip; namewidth -= skip + st::dialogsChatTypeSkip;
} }
const auto badgeWidth = _titleBadge.drawGetWidth( const auto badgeWidth = _titleBadge.drawGetWidth(p, {
p, .peer = namePeer,
QRect( .rectForName = QRect(
nameleft, nameleft,
nametop, nametop,
namewidth, namewidth,
st::msgNameStyle.font->height), st::msgNameStyle.font->height),
_title.maxWidth(), .nameWidth = _title.maxWidth(),
width(), .outerWidth = width(),
{
.peer = namePeer,
.verified = &st::dialogsVerifiedIcon, .verified = &st::dialogsVerifiedIcon,
.premium = &st::dialogsPremiumIcon.icon, .premium = &st::dialogsPremiumIcon.icon,
.scam = &st::attentionButtonFg, .scam = &st::attentionButtonFg,
.premiumFg = &st::dialogsVerifiedIconBg, .premiumFg = &st::dialogsVerifiedIconBg,
.customEmojiRepaint = [=] { update(); }, .customEmojiRepaint = [=] { update(); },
.now = now, .now = now,
.bothVerifyAndStatus = true,
.paused = _controller->isGifPausedAtLeastFor( .paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Any), Window::GifPauseReason::Any),
}); });

View file

@ -440,7 +440,7 @@ infoPeerBadge: InfoPeerBadge {
premium: infoPremiumStar; premium: infoPremiumStar;
premiumFg: profileVerifiedCheckBg; premiumFg: profileVerifiedCheckBg;
position: infoVerifiedCheckPosition; position: infoVerifiedCheckPosition;
sizeTag: 0; // Large sizeTag: 0; // Normal
} }
infoIconFg: windowBoldFg; infoIconFg: windowBoldFg;

View file

@ -39,6 +39,11 @@ namespace {
}); });
} }
[[nodiscard]] bool HasPremiumClick(const Badge::Content &content) {
return content.badge == BadgeType::Premium
|| (content.badge == BadgeType::Verified && content.emojiStatusId);
}
} // namespace } // namespace
Badge::Badge( Badge::Badge(
@ -114,6 +119,15 @@ void Badge::setContent(Content content) {
case BadgeType::Verified: case BadgeType::Verified:
case BadgeType::Premium: { case BadgeType::Premium: {
const auto id = _content.emojiStatusId; 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) { if (id) {
_emojiStatus = _session->data().customEmojiManager().create( _emojiStatus = _session->data().customEmojiManager().create(
id, id,
@ -124,11 +138,13 @@ void Badge::setContent(Content content) {
std::move(_emojiStatus), std::move(_emojiStatus),
_customStatusLoopsLimit); _customStatusLoopsLimit);
} }
const auto emoji = Data::FrameSizeFromTag(sizeTag()) }
/ style::DevicePixelRatio(); const auto width = emoji + (icon ? icon->width() : 0);
_view->resize(emoji, emoji); const auto height = std::max(emoji, icon ? icon->height() : 0);
_view->resize(width, height);
_view->paintRequest( _view->paintRequest(
) | rpl::start_with_next([=, check = _view.data()]{ ) | rpl::start_with_next([=, check = _view.data()]{
if (_emojiStatus) {
auto args = Ui::Text::CustomEmoji::Context{ auto args = Ui::Text::CustomEmoji::Context{
.textColor = _st.premiumFg->c, .textColor = _st.premiumFg->c,
.now = crl::now(), .now = crl::now(),
@ -140,18 +156,12 @@ void Badge::setContent(Content content) {
Painter p(check); Painter p(check);
_emojiStatus->paint(p, args); _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()]{
Painter p(check);
icon->paint(p, 0, 0, check->width());
}, _view->lifetime());
} }
if (icon) {
Painter p(check);
icon->paint(p, emoji, 0, check->width());
}
}, _view->lifetime());
} break; } break;
case BadgeType::Scam: case BadgeType::Scam:
case BadgeType::Fake: { case BadgeType::Fake: {
@ -174,7 +184,7 @@ void Badge::setContent(Content content) {
} break; } break;
} }
if (_content.badge != BadgeType::Premium || !_premiumClickCallback) { if (!HasPremiumClick(_content) || !_premiumClickCallback) {
_view->setAttribute(Qt::WA_TransparentForMouseEvents); _view->setAttribute(Qt::WA_TransparentForMouseEvents);
} else { } else {
_view->setClickedCallback(_premiumClickCallback); _view->setClickedCallback(_premiumClickCallback);
@ -185,7 +195,7 @@ void Badge::setContent(Content content) {
void Badge::setPremiumClickCallback(Fn<void()> callback) { void Badge::setPremiumClickCallback(Fn<void()> callback) {
_premiumClickCallback = std::move(callback); _premiumClickCallback = std::move(callback);
if (_view && _content.badge == BadgeType::Premium) { if (_view && HasPremiumClick(_content)) {
if (!_premiumClickCallback) { if (!_premiumClickCallback) {
_view->setAttribute(Qt::WA_TransparentForMouseEvents); _view->setAttribute(Qt::WA_TransparentForMouseEvents);
} else { } else {

View file

@ -128,17 +128,56 @@ PeerBadge::PeerBadge() = default;
PeerBadge::~PeerBadge() = default; PeerBadge::~PeerBadge() = default;
int PeerBadge::drawGetWidth( int PeerBadge::drawGetWidth(Painter &p, Descriptor &&descriptor) {
Painter &p,
QRect rectForName,
int nameWidth,
int outerWidth,
const Descriptor &descriptor) {
Expects(descriptor.customEmojiRepaint != nullptr); Expects(descriptor.customEmojiRepaint != nullptr);
const auto peer = descriptor.peer; const auto peer = descriptor.peer;
if (descriptor.scam && (peer->isScam() || peer->isFake())) { if (descriptor.scam && (peer->isScam() || peer->isFake())) {
const auto phrase = peer->isScam() 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());
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);
}
result += drawPremiumEmojiStatus(p, descriptor);
if (!paintVerify) {
return result;
}
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_scam_badge(tr::now)
: tr::lng_fake_badge(tr::now); : tr::lng_fake_badge(tr::now);
const auto phraseWidth = st::dialogsScamFont->width(phrase); const auto phraseWidth = st::dialogsScamFont->width(phrase);
@ -148,10 +187,11 @@ int PeerBadge::drawGetWidth(
const auto height = st::dialogsScamPadding.top() const auto height = st::dialogsScamPadding.top()
+ st::dialogsScamFont->height + st::dialogsScamFont->height
+ st::dialogsScamPadding.bottom(); + st::dialogsScamPadding.bottom();
const auto rectForName = descriptor.rectForName;
const auto rect = QRect( const auto rect = QRect(
(rectForName.x() (rectForName.x()
+ qMin( + qMin(
nameWidth + st::dialogsScamSkip, descriptor.nameWidth + st::dialogsScamSkip,
rectForName.width() - width)), rectForName.width() - width)),
rectForName.y() + (rectForName.height() - height) / 2, rectForName.y() + (rectForName.height() - height) / 2,
width, width,
@ -159,19 +199,34 @@ int PeerBadge::drawGetWidth(
DrawScamFakeBadge( DrawScamFakeBadge(
p, p,
rect, rect,
outerWidth, descriptor.outerWidth,
*descriptor.scam, *descriptor.scam,
phrase, phrase,
phraseWidth); phraseWidth);
return st::dialogsScamSkip + width; return st::dialogsScamSkip + width;
} else if (descriptor.premium }
&& peer->emojiStatusId()
&& (peer->isPremium() || peer->isChannel()) int PeerBadge::drawVerifyCheck(Painter &p, const Descriptor &descriptor) {
&& peer->session().premiumBadgesShown()) { 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 id = peer->emojiStatusId();
const auto rectForName = descriptor.rectForName;
const auto iconw = descriptor.premium->width(); const auto iconw = descriptor.premium->width();
const auto iconx = rectForName.x() const auto iconx = rectForName.x()
+ qMin(nameWidth, rectForName.width() - iconw); + qMin(descriptor.nameWidth, rectForName.width() - iconw);
const auto icony = rectForName.y(); const auto icony = rectForName.y();
if (!_emojiStatus) { if (!_emojiStatus) {
_emojiStatus = std::make_unique<EmojiStatus>(); _emojiStatus = std::make_unique<EmojiStatus>();
@ -198,27 +253,18 @@ int PeerBadge::drawGetWidth(
.paused = descriptor.paused || On(PowerSaving::kEmojiStatus), .paused = descriptor.paused || On(PowerSaving::kEmojiStatus),
}); });
return iconw - 4 * _emojiStatus->skip; return iconw - 4 * _emojiStatus->skip;
} else if (descriptor.verified && peer->isVerified()) { }
const auto iconw = descriptor.verified->width();
descriptor.verified->paint( int PeerBadge::drawPremiumStar(Painter &p, const Descriptor &descriptor) {
p, const auto rectForName = descriptor.rectForName;
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 iconw = descriptor.premium->width();
const auto iconx = rectForName.x() const auto iconx = rectForName.x()
+ qMin(nameWidth, rectForName.width() - iconw); + qMin(descriptor.nameWidth, rectForName.width() - iconw);
const auto icony = rectForName.y(); const auto icony = rectForName.y();
_emojiStatus = nullptr; _emojiStatus = nullptr;
descriptor.premium->paint(p, iconx, icony, outerWidth); descriptor.premium->paint(p, iconx, icony, descriptor.outerWidth);
return iconw; return iconw;
} }
return 0;
}
void PeerBadge::unload() { void PeerBadge::unload() {
_emojiStatus = nullptr; _emojiStatus = nullptr;

View file

@ -52,20 +52,20 @@ public:
struct Descriptor { struct Descriptor {
not_null<PeerData*> peer; not_null<PeerData*> peer;
QRect rectForName;
int nameWidth = 0;
int outerWidth = 0;
const style::icon *verified = nullptr; const style::icon *verified = nullptr;
const style::icon *premium = nullptr; const style::icon *premium = nullptr;
const style::color *scam = nullptr; const style::color *scam = nullptr;
const style::color *premiumFg = nullptr; const style::color *premiumFg = nullptr;
Fn<void()> customEmojiRepaint; Fn<void()> customEmojiRepaint;
crl::time now = 0; crl::time now = 0;
bool prioritizeVerification = false;
bool bothVerifyAndStatus = false;
bool paused = false; bool paused = false;
}; };
int drawGetWidth( int drawGetWidth(Painter &p, Descriptor &&descriptor);
Painter &p,
QRect rectForName,
int nameWidth,
int outerWidth,
const Descriptor &descriptor);
void unload(); void unload();
[[nodiscard]] bool ready(const BotVerifyDetails *details) const; [[nodiscard]] bool ready(const BotVerifyDetails *details) const;
@ -84,6 +84,11 @@ private:
struct EmojiStatus; struct EmojiStatus;
struct BotVerifiedData; 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> _emojiStatus; std::unique_ptr<EmojiStatus> _emojiStatus;
mutable std::unique_ptr<BotVerifiedData> _botVerifiedData; mutable std::unique_ptr<BotVerifiedData> _botVerifiedData;