AyuGramDesktop/Telegram/SourceFiles/ui/unread_badge.cpp
2025-03-08 23:02:34 +03:00

384 lines
10 KiB
C++

/*
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/unread_badge.h"
#include "data/data_emoji_statuses.h"
#include "data/data_peer.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "data/stickers/data_custom_emoji.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
#include "ui/unread_badge_paint.h"
#include "styles/style_dialogs.h"
// AyuGram includes
#include "ayu/utils/telegram_helpers.h"
namespace Ui {
namespace {
constexpr auto kPlayStatusLimit = 12;
} // namespace
struct PeerBadge::EmojiStatus {
EmojiStatusId id;
std::unique_ptr<Ui::Text::CustomEmoji> emoji;
int skip = 0;
};
struct PeerBadge::BotVerifiedData {
QImage cache;
std::unique_ptr<Text::CustomEmoji> icon;
};
void UnreadBadge::setText(const QString &text, bool active) {
_text = text;
_active = active;
const auto st = Dialogs::Ui::UnreadBadgeStyle();
resize(
std::max(st.font->width(text) + 2 * st.padding, st.size),
st.size);
update();
}
int UnreadBadge::textBaseline() const {
const auto st = Dialogs::Ui::UnreadBadgeStyle();
return ((st.size - st.font->height) / 2) + st.font->ascent;
}
void UnreadBadge::paintEvent(QPaintEvent *e) {
if (_text.isEmpty()) {
return;
}
auto p = QPainter(this);
UnreadBadgeStyle unreadSt;
unreadSt.muted = !_active;
auto unreadRight = width();
auto unreadTop = 0;
PaintUnreadBadge(
p,
_text,
unreadRight,
unreadTop,
unreadSt);
}
QSize ScamBadgeSize(bool fake) {
const auto phrase = fake
? tr::lng_fake_badge(tr::now)
: tr::lng_scam_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();
return { width, height };
}
void DrawScamFakeBadge(
Painter &p,
QRect rect,
int outerWidth,
const style::color &color,
const QString &phrase,
int phraseWidth) {
PainterHighQualityEnabler hq(p);
auto pen = color->p;
pen.setWidth(st::lineWidth);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawRoundedRect(rect, st::dialogsScamRadius, st::dialogsScamRadius);
p.setFont(st::dialogsScamFont);
p.drawTextLeft(
rect.x() + st::dialogsScamPadding.left(),
rect.y() + st::dialogsScamPadding.top(),
outerWidth,
phrase,
phraseWidth);
}
void DrawScamBadge(
bool fake,
Painter &p,
QRect rect,
int outerWidth,
const style::color &color) {
const auto phrase = fake
? tr::lng_fake_badge(tr::now)
: tr::lng_scam_badge(tr::now);
DrawScamFakeBadge(
p,
rect,
outerWidth,
color,
phrase,
st::dialogsScamFont->width(phrase));
}
PeerBadge::PeerBadge() = default;
PeerBadge::~PeerBadge() = default;
int PeerBadge::drawGetWidth(Painter &p, Descriptor &&descriptor) {
Expects(descriptor.customEmojiRepaint != nullptr);
const auto peer = descriptor.peer;
if (descriptor.scam && (peer->isScam() || peer->isFake())) {
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;
const auto paintExteraDev =
isExteraPeer(getBareID(peer)) && (!paintEmoji || descriptor.bothVerifyAndStatus);
const auto paintExteraSupporter = !paintExteraDev &&
isSupporterPeer(getBareID(peer)) && (!paintEmoji || descriptor.bothVerifyAndStatus);
const auto exteraWidth = paintExteraDev
? descriptor.exteraOfficial->width()
: paintExteraSupporter
? descriptor.exteraSupporter->width()
: 0;
auto result = 0;
if (paintEmoji) {
auto &rectForName = descriptor.rectForName;
const auto verifyWidth = descriptor.verified->width();
if (paintVerify) {
rectForName.setWidth(rectForName.width() - verifyWidth);
}
if (paintExteraDev || paintExteraSupporter) {
rectForName.setWidth(rectForName.width() - exteraWidth);
}
result += drawPremiumEmojiStatus(p, descriptor);
if (!paintVerify && !paintExteraDev) {
return result;
}
if (paintVerify) {
rectForName.setWidth(rectForName.width() + verifyWidth);
}
if (paintExteraDev || paintExteraSupporter) {
rectForName.setWidth(rectForName.width() + exteraWidth);
}
descriptor.nameWidth += result;
}
if (paintExteraDev || paintExteraSupporter) {
if (paintStar) {
auto &rectForName = descriptor.rectForName;
rectForName.setWidth(rectForName.width() - exteraWidth);
result += drawPremiumStar(p, descriptor);
rectForName.setWidth(rectForName.width() + exteraWidth);
descriptor.nameWidth += result;
}
result += paintExteraDev ? drawExteraOfficial(p, descriptor) : drawExteraSupporter(p, descriptor);
return 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(
Data::EmojiStatusCustomId(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;
}
int PeerBadge::drawExteraOfficial(Painter &p, const Descriptor &descriptor) {
const auto iconw = descriptor.exteraOfficial->width();
const auto rectForName = descriptor.rectForName;
const auto nameWidth = descriptor.nameWidth;
descriptor.exteraOfficial->paint(
p,
rectForName.x() + qMin(nameWidth, rectForName.width() - iconw),
rectForName.y(),
descriptor.outerWidth);
return iconw;
}
int PeerBadge::drawExteraSupporter(Painter &p, const Descriptor &descriptor) {
const auto iconw = descriptor.exteraSupporter->width();
const auto rectForName = descriptor.rectForName;
const auto nameWidth = descriptor.nameWidth;
descriptor.exteraSupporter->paint(
p,
rectForName.x() + qMin(nameWidth, rectForName.width() - iconw),
rectForName.y(),
descriptor.outerWidth);
return iconw;
}
void PeerBadge::unload() {
_emojiStatus = nullptr;
}
bool PeerBadge::ready(const BotVerifyDetails *details) const {
if (!details || !*details) {
_botVerifiedData = nullptr;
return true;
} else if (!_botVerifiedData) {
return false;
}
if (!details->iconId) {
_botVerifiedData->icon = nullptr;
} else if (!_botVerifiedData->icon
|| (_botVerifiedData->icon->entityData()
!= Data::SerializeCustomEmojiId(details->iconId))) {
return false;
}
return true;
}
void PeerBadge::set(
not_null<const BotVerifyDetails*> details,
Ui::Text::CustomEmojiFactory factory,
Fn<void()> repaint) {
if (!_botVerifiedData) {
_botVerifiedData = std::make_unique<BotVerifiedData>();
}
if (details->iconId) {
_botVerifiedData->icon = std::make_unique<Ui::Text::FirstFrameEmoji>(
factory(
Data::SerializeCustomEmojiId(details->iconId),
{ .repaint = repaint }));
}
}
int PeerBadge::drawVerified(
QPainter &p,
QPoint position,
const style::VerifiedBadge &st) {
const auto data = _botVerifiedData.get();
if (!data) {
return 0;
}
if (const auto icon = data->icon.get()) {
icon->paint(p, {
.textColor = st.color->c,
.now = crl::now(),
.position = position,
});
return icon->width();
}
return 0;
}
} // namespace Ui