unread_badge_paint.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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_paint.h"
  8. #include "ui/ui_utility.h"
  9. #include "styles/style_dialogs.h"
  10. namespace Ui {
  11. namespace {
  12. struct UnreadBadgeSizeData {
  13. QImage circle;
  14. QPixmap left[6], right[6];
  15. };
  16. class UnreadBadgeStyleData {
  17. public:
  18. UnreadBadgeStyleData();
  19. UnreadBadgeSizeData sizes[static_cast<int>(UnreadBadgeSize::kCount)];
  20. style::color bg[6] = {
  21. st::dialogsUnreadBg,
  22. st::dialogsUnreadBgOver,
  23. st::dialogsUnreadBgActive,
  24. st::dialogsUnreadBgMuted,
  25. st::dialogsUnreadBgMutedOver,
  26. st::dialogsUnreadBgMutedActive
  27. };
  28. style::color reactionBg[6] = {
  29. st::dialogsDraftFg,
  30. st::dialogsDraftFgOver,
  31. st::dialogsDraftFgActive,
  32. st::dialogsUnreadBgMuted,
  33. st::dialogsUnreadBgMutedOver,
  34. st::dialogsUnreadBgMutedActive
  35. };
  36. rpl::lifetime lifetime;
  37. };
  38. UnreadBadgeStyleData::UnreadBadgeStyleData() {
  39. style::PaletteChanged(
  40. ) | rpl::start_with_next([=] {
  41. for (auto &data : sizes) {
  42. for (auto &left : data.left) {
  43. left = QPixmap();
  44. }
  45. for (auto &right : data.right) {
  46. right = QPixmap();
  47. }
  48. }
  49. }, lifetime);
  50. }
  51. UnreadBadgeStyleData &UnreadBadgeStyles() {
  52. static auto result = UnreadBadgeStyleData();
  53. return result;
  54. }
  55. void CreateCircleMask(UnreadBadgeSizeData *data, int size) {
  56. if (!data->circle.isNull()) {
  57. return;
  58. }
  59. data->circle = style::createCircleMask(size);
  60. }
  61. [[nodiscard]] QImage ColorizeCircleHalf(
  62. UnreadBadgeSizeData *data,
  63. int size,
  64. int half,
  65. int xoffset,
  66. style::color color) {
  67. auto result = style::colorizeImage(data->circle, color, QRect(xoffset, 0, half, size));
  68. result.setDevicePixelRatio(style::DevicePixelRatio());
  69. return result;
  70. }
  71. [[nodiscard]] QString ComputeUnreadBadgeText(
  72. const QString &unreadCount,
  73. int allowDigits) {
  74. return (allowDigits > 0) && (unreadCount.size() > allowDigits + 1)
  75. ? u".."_q + unreadCount.mid(unreadCount.size() - allowDigits)
  76. : unreadCount;
  77. }
  78. void PaintUnreadBadge(QPainter &p, const QRect &rect, const UnreadBadgeStyle &st) {
  79. Assert(rect.height() == st.size);
  80. int index = (st.muted ? 0x03 : 0x00) + (st.active ? 0x02 : (st.selected ? 0x01 : 0x00));
  81. int size = st.size, sizehalf = size / 2;
  82. auto &styles = UnreadBadgeStyles();
  83. auto badgeData = styles.sizes;
  84. if (st.sizeId > UnreadBadgeSize()) {
  85. Assert(st.sizeId < UnreadBadgeSize::kCount);
  86. badgeData = &styles.sizes[static_cast<int>(st.sizeId)];
  87. }
  88. const auto bg = (st.sizeId == UnreadBadgeSize::ReactionInDialogs)
  89. ? styles.reactionBg[index]
  90. : styles.bg[index];
  91. if (badgeData->left[index].isNull()) {
  92. const auto ratio = style::DevicePixelRatio();
  93. int imgsize = size * ratio, imgsizehalf = sizehalf * ratio;
  94. CreateCircleMask(badgeData, size);
  95. badgeData->left[index] = PixmapFromImage(
  96. ColorizeCircleHalf(badgeData, imgsize, imgsizehalf, 0, bg));
  97. badgeData->right[index] = PixmapFromImage(ColorizeCircleHalf(
  98. badgeData,
  99. imgsize,
  100. imgsizehalf,
  101. imgsize - imgsizehalf,
  102. bg));
  103. }
  104. int bar = rect.width() - 2 * sizehalf;
  105. p.drawPixmap(rect.x(), rect.y(), badgeData->left[index]);
  106. if (bar) {
  107. p.fillRect(rect.x() + sizehalf, rect.y(), bar, rect.height(), bg);
  108. }
  109. p.drawPixmap(rect.x() + sizehalf + bar, rect.y(), badgeData->right[index]);
  110. }
  111. } // namespace
  112. UnreadBadgeStyle::UnreadBadgeStyle()
  113. : size(st::dialogsUnreadHeight)
  114. , padding(st::dialogsUnreadPadding)
  115. , font(st::dialogsUnreadFont) {
  116. }
  117. QSize CountUnreadBadgeSize(
  118. const QString &unreadCount,
  119. const UnreadBadgeStyle &st,
  120. int allowDigits) {
  121. const auto text = ComputeUnreadBadgeText(unreadCount, allowDigits);
  122. const auto unreadRectHeight = st.size;
  123. const auto unreadWidth = st.font->width(text);
  124. return {
  125. std::max(unreadWidth + 2 * st.padding, unreadRectHeight),
  126. unreadRectHeight,
  127. };
  128. }
  129. QRect PaintUnreadBadge(
  130. QPainter &p,
  131. const QString &unreadCount,
  132. int x,
  133. int y,
  134. const UnreadBadgeStyle &st,
  135. int allowDigits) {
  136. const auto text = ComputeUnreadBadgeText(unreadCount, allowDigits);
  137. const auto unreadRectHeight = st.size;
  138. const auto unreadWidth = st.font->width(text);
  139. const auto unreadRectWidth = std::max(
  140. unreadWidth + 2 * st.padding,
  141. unreadRectHeight);
  142. const auto unreadRectLeft = ((st.align & Qt::AlignHorizontal_Mask) & style::al_center)
  143. ? (x - unreadRectWidth) / 2
  144. : ((st.align & Qt::AlignHorizontal_Mask) & style::al_right)
  145. ? (x - unreadRectWidth)
  146. : x;
  147. const auto unreadRectTop = y;
  148. const auto badge = QRect(unreadRectLeft, unreadRectTop, unreadRectWidth, unreadRectHeight);
  149. PaintUnreadBadge(p, badge, st);
  150. const auto textTop = st.textTop ? st.textTop : (unreadRectHeight - st.font->height) / 2;
  151. p.setFont(st.font);
  152. p.setPen(st.active
  153. ? st::dialogsUnreadFgActive
  154. : st.selected
  155. ? st::dialogsUnreadFgOver
  156. : st::dialogsUnreadFg);
  157. p.drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st.font->ascent, text);
  158. return badge;
  159. }
  160. } // namespace Ui