unread_badge.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "ui/unread_badge.h"
  8. #include "data/data_emoji_statuses.h"
  9. #include "data/data_peer.h"
  10. #include "data/data_user.h"
  11. #include "data/data_session.h"
  12. #include "data/stickers/data_custom_emoji.h"
  13. #include "main/main_session.h"
  14. #include "lang/lang_keys.h"
  15. #include "ui/painter.h"
  16. #include "ui/power_saving.h"
  17. #include "ui/unread_badge_paint.h"
  18. #include "styles/style_dialogs.h"
  19. namespace Ui {
  20. namespace {
  21. constexpr auto kPlayStatusLimit = 2;
  22. } // namespace
  23. struct PeerBadge::EmojiStatus {
  24. EmojiStatusId id;
  25. std::unique_ptr<Ui::Text::CustomEmoji> emoji;
  26. int skip = 0;
  27. };
  28. struct PeerBadge::BotVerifiedData {
  29. QImage cache;
  30. std::unique_ptr<Text::CustomEmoji> icon;
  31. };
  32. void UnreadBadge::setText(const QString &text, bool active) {
  33. _text = text;
  34. _active = active;
  35. const auto st = Dialogs::Ui::UnreadBadgeStyle();
  36. resize(
  37. std::max(st.font->width(text) + 2 * st.padding, st.size),
  38. st.size);
  39. update();
  40. }
  41. int UnreadBadge::textBaseline() const {
  42. const auto st = Dialogs::Ui::UnreadBadgeStyle();
  43. return ((st.size - st.font->height) / 2) + st.font->ascent;
  44. }
  45. void UnreadBadge::paintEvent(QPaintEvent *e) {
  46. if (_text.isEmpty()) {
  47. return;
  48. }
  49. auto p = QPainter(this);
  50. UnreadBadgeStyle unreadSt;
  51. unreadSt.muted = !_active;
  52. auto unreadRight = width();
  53. auto unreadTop = 0;
  54. PaintUnreadBadge(
  55. p,
  56. _text,
  57. unreadRight,
  58. unreadTop,
  59. unreadSt);
  60. }
  61. QSize ScamBadgeSize(bool fake) {
  62. const auto phrase = fake
  63. ? tr::lng_fake_badge(tr::now)
  64. : tr::lng_scam_badge(tr::now);
  65. const auto phraseWidth = st::dialogsScamFont->width(phrase);
  66. const auto width = st::dialogsScamPadding.left()
  67. + phraseWidth
  68. + st::dialogsScamPadding.right();
  69. const auto height = st::dialogsScamPadding.top()
  70. + st::dialogsScamFont->height
  71. + st::dialogsScamPadding.bottom();
  72. return { width, height };
  73. }
  74. void DrawScamFakeBadge(
  75. Painter &p,
  76. QRect rect,
  77. int outerWidth,
  78. const style::color &color,
  79. const QString &phrase,
  80. int phraseWidth) {
  81. PainterHighQualityEnabler hq(p);
  82. auto pen = color->p;
  83. pen.setWidth(st::lineWidth);
  84. p.setPen(pen);
  85. p.setBrush(Qt::NoBrush);
  86. p.drawRoundedRect(rect, st::dialogsScamRadius, st::dialogsScamRadius);
  87. p.setFont(st::dialogsScamFont);
  88. p.drawTextLeft(
  89. rect.x() + st::dialogsScamPadding.left(),
  90. rect.y() + st::dialogsScamPadding.top(),
  91. outerWidth,
  92. phrase,
  93. phraseWidth);
  94. }
  95. void DrawScamBadge(
  96. bool fake,
  97. Painter &p,
  98. QRect rect,
  99. int outerWidth,
  100. const style::color &color) {
  101. const auto phrase = fake
  102. ? tr::lng_fake_badge(tr::now)
  103. : tr::lng_scam_badge(tr::now);
  104. DrawScamFakeBadge(
  105. p,
  106. rect,
  107. outerWidth,
  108. color,
  109. phrase,
  110. st::dialogsScamFont->width(phrase));
  111. }
  112. PeerBadge::PeerBadge() = default;
  113. PeerBadge::~PeerBadge() = default;
  114. int PeerBadge::drawGetWidth(Painter &p, Descriptor &&descriptor) {
  115. Expects(descriptor.customEmojiRepaint != nullptr);
  116. const auto peer = descriptor.peer;
  117. if (descriptor.scam && (peer->isScam() || peer->isFake())) {
  118. return drawScamOrFake(p, descriptor);
  119. }
  120. const auto verifyCheck = descriptor.verified && peer->isVerified();
  121. const auto premiumMark = descriptor.premium
  122. && peer->session().premiumBadgesShown();
  123. const auto emojiStatus = premiumMark
  124. && peer->emojiStatusId()
  125. && (peer->isPremium() || peer->isChannel());
  126. const auto premiumStar = premiumMark
  127. && !emojiStatus
  128. && peer->isPremium();
  129. const auto paintVerify = verifyCheck
  130. && (descriptor.prioritizeVerification
  131. || descriptor.bothVerifyAndStatus
  132. || !emojiStatus);
  133. const auto paintEmoji = emojiStatus
  134. && (!paintVerify || descriptor.bothVerifyAndStatus);
  135. const auto paintStar = premiumStar && !paintVerify;
  136. auto result = 0;
  137. if (paintEmoji) {
  138. auto &rectForName = descriptor.rectForName;
  139. const auto verifyWidth = descriptor.verified->width();
  140. if (paintVerify) {
  141. rectForName.setWidth(rectForName.width() - verifyWidth);
  142. }
  143. result += drawPremiumEmojiStatus(p, descriptor);
  144. if (!paintVerify) {
  145. return result;
  146. }
  147. rectForName.setWidth(rectForName.width() + verifyWidth);
  148. descriptor.nameWidth += result;
  149. }
  150. if (paintVerify) {
  151. result += drawVerifyCheck(p, descriptor);
  152. return result;
  153. } else if (paintStar) {
  154. return drawPremiumStar(p, descriptor);
  155. }
  156. return 0;
  157. }
  158. int PeerBadge::drawScamOrFake(Painter &p, const Descriptor &descriptor) {
  159. const auto phrase = descriptor.peer->isScam()
  160. ? tr::lng_scam_badge(tr::now)
  161. : tr::lng_fake_badge(tr::now);
  162. const auto phraseWidth = st::dialogsScamFont->width(phrase);
  163. const auto width = st::dialogsScamPadding.left()
  164. + phraseWidth
  165. + st::dialogsScamPadding.right();
  166. const auto height = st::dialogsScamPadding.top()
  167. + st::dialogsScamFont->height
  168. + st::dialogsScamPadding.bottom();
  169. const auto rectForName = descriptor.rectForName;
  170. const auto rect = QRect(
  171. (rectForName.x()
  172. + qMin(
  173. descriptor.nameWidth + st::dialogsScamSkip,
  174. rectForName.width() - width)),
  175. rectForName.y() + (rectForName.height() - height) / 2,
  176. width,
  177. height);
  178. DrawScamFakeBadge(
  179. p,
  180. rect,
  181. descriptor.outerWidth,
  182. *descriptor.scam,
  183. phrase,
  184. phraseWidth);
  185. return st::dialogsScamSkip + width;
  186. }
  187. int PeerBadge::drawVerifyCheck(Painter &p, const Descriptor &descriptor) {
  188. const auto iconw = descriptor.verified->width();
  189. const auto rectForName = descriptor.rectForName;
  190. const auto nameWidth = descriptor.nameWidth;
  191. descriptor.verified->paint(
  192. p,
  193. rectForName.x() + qMin(nameWidth, rectForName.width() - iconw),
  194. rectForName.y(),
  195. descriptor.outerWidth);
  196. return iconw;
  197. }
  198. int PeerBadge::drawPremiumEmojiStatus(
  199. Painter &p,
  200. const Descriptor &descriptor) {
  201. const auto peer = descriptor.peer;
  202. const auto id = peer->emojiStatusId();
  203. const auto rectForName = descriptor.rectForName;
  204. const auto iconw = descriptor.premium->width();
  205. const auto iconx = rectForName.x()
  206. + qMin(descriptor.nameWidth, rectForName.width() - iconw);
  207. const auto icony = rectForName.y();
  208. if (!_emojiStatus) {
  209. _emojiStatus = std::make_unique<EmojiStatus>();
  210. const auto size = st::emojiSize;
  211. const auto emoji = Ui::Text::AdjustCustomEmojiSize(size);
  212. _emojiStatus->skip = (size - emoji) / 2;
  213. }
  214. if (_emojiStatus->id != id) {
  215. using namespace Ui::Text;
  216. auto &manager = peer->session().data().customEmojiManager();
  217. _emojiStatus->id = id;
  218. _emojiStatus->emoji = std::make_unique<LimitedLoopsEmoji>(
  219. manager.create(
  220. Data::EmojiStatusCustomId(id),
  221. descriptor.customEmojiRepaint),
  222. kPlayStatusLimit);
  223. }
  224. _emojiStatus->emoji->paint(p, {
  225. .textColor = (*descriptor.premiumFg)->c,
  226. .now = descriptor.now,
  227. .position = QPoint(
  228. iconx - 2 * _emojiStatus->skip,
  229. icony + _emojiStatus->skip),
  230. .paused = descriptor.paused || On(PowerSaving::kEmojiStatus),
  231. });
  232. return iconw - 4 * _emojiStatus->skip;
  233. }
  234. int PeerBadge::drawPremiumStar(Painter &p, const Descriptor &descriptor) {
  235. const auto rectForName = descriptor.rectForName;
  236. const auto iconw = descriptor.premium->width();
  237. const auto iconx = rectForName.x()
  238. + qMin(descriptor.nameWidth, rectForName.width() - iconw);
  239. const auto icony = rectForName.y();
  240. _emojiStatus = nullptr;
  241. descriptor.premium->paint(p, iconx, icony, descriptor.outerWidth);
  242. return iconw;
  243. }
  244. void PeerBadge::unload() {
  245. _emojiStatus = nullptr;
  246. }
  247. bool PeerBadge::ready(const BotVerifyDetails *details) const {
  248. if (!details || !*details) {
  249. _botVerifiedData = nullptr;
  250. return true;
  251. } else if (!_botVerifiedData) {
  252. return false;
  253. }
  254. if (!details->iconId) {
  255. _botVerifiedData->icon = nullptr;
  256. } else if (!_botVerifiedData->icon
  257. || (_botVerifiedData->icon->entityData()
  258. != Data::SerializeCustomEmojiId(details->iconId))) {
  259. return false;
  260. }
  261. return true;
  262. }
  263. void PeerBadge::set(
  264. not_null<const BotVerifyDetails*> details,
  265. Ui::Text::CustomEmojiFactory factory,
  266. Fn<void()> repaint) {
  267. if (!_botVerifiedData) {
  268. _botVerifiedData = std::make_unique<BotVerifiedData>();
  269. }
  270. if (details->iconId) {
  271. _botVerifiedData->icon = std::make_unique<Ui::Text::FirstFrameEmoji>(
  272. factory(
  273. Data::SerializeCustomEmojiId(details->iconId),
  274. { .repaint = repaint }));
  275. }
  276. }
  277. int PeerBadge::drawVerified(
  278. QPainter &p,
  279. QPoint position,
  280. const style::VerifiedBadge &st) {
  281. const auto data = _botVerifiedData.get();
  282. if (!data) {
  283. return 0;
  284. }
  285. if (const auto icon = data->icon.get()) {
  286. icon->paint(p, {
  287. .textColor = st.color->c,
  288. .now = crl::now(),
  289. .position = position,
  290. });
  291. return icon->width();
  292. }
  293. return 0;
  294. }
  295. } // namespace Ui