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,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(

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -440,7 +440,7 @@ infoPeerBadge: InfoPeerBadge {
premium: infoPremiumStar;
premiumFg: profileVerifiedCheckBg;
position: infoVerifiedCheckPosition;
sizeTag: 0; // Large
sizeTag: 0; // Normal
}
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
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<Ui::Text::LimitedLoopsEmoji>(
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<void()> callback) {
_premiumClickCallback = std::move(callback);
if (_view && _content.badge == BadgeType::Premium) {
if (_view && HasPremiumClick(_content)) {
if (!_premiumClickCallback) {
_view->setAttribute(Qt::WA_TransparentForMouseEvents);
} else {

View file

@ -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<EmojiStatus>();
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<LimitedLoopsEmoji>(
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<EmojiStatus>();
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<LimitedLoopsEmoji>(
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;
}

View file

@ -52,20 +52,20 @@ public:
struct Descriptor {
not_null<PeerData*> 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<void()> 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> _emojiStatus;
mutable std::unique_ptr<BotVerifiedData> _botVerifiedData;