credits_graphics.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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/effects/credits_graphics.h"
  8. #include <QtCore/QDateTime>
  9. #include "data/data_credits.h"
  10. #include "data/data_file_origin.h"
  11. #include "data/data_document.h"
  12. #include "data/data_document_media.h"
  13. #include "data/data_photo.h"
  14. #include "data/data_photo_media.h"
  15. #include "data/data_session.h"
  16. #include "history/view/media/history_view_sticker_player.h"
  17. #include "info/bot/starref/info_bot_starref_common.h"
  18. #include "info/userpic/info_userpic_emoji_builder_preview.h"
  19. #include "lang/lang_keys.h"
  20. #include "main/main_session.h"
  21. #include "ui/effects/premium_graphics.h"
  22. #include "ui/effects/spoiler_mess.h"
  23. #include "ui/empty_userpic.h"
  24. #include "ui/painter.h"
  25. #include "ui/rect.h"
  26. #include "ui/widgets/fields/number_input.h"
  27. #include "ui/wrap/padding_wrap.h"
  28. #include "ui/wrap/vertical_layout.h"
  29. #include "styles/style_channel_earn.h"
  30. #include "styles/style_credits.h"
  31. #include "styles/style_dialogs.h"
  32. #include "styles/style_intro.h" // introFragmentIcon.
  33. #include "styles/style_layers.h"
  34. #include "styles/style_settings.h"
  35. #include "styles/style_widgets.h"
  36. #include <QtSvg/QSvgRenderer>
  37. namespace Ui {
  38. namespace {
  39. PaintRoundImageCallback MultiThumbnail(
  40. PaintRoundImageCallback first,
  41. PaintRoundImageCallback second,
  42. int totalCount) {
  43. const auto cache = std::make_shared<QImage>();
  44. return [=](Painter &p, int x, int y, int outerWidth, int size) {
  45. const auto stroke = st::lineWidth * 2;
  46. const auto shift = stroke * 3;
  47. if (size <= 2 * shift) {
  48. first(p, x, y, outerWidth, size);
  49. return;
  50. }
  51. const auto smaller = size - shift;
  52. const auto ratio = style::DevicePixelRatio();
  53. const auto full = QSize(size, size) * ratio;
  54. if (cache->size() != full) {
  55. *cache = QImage(full, QImage::Format_ARGB32_Premultiplied);
  56. cache->setDevicePixelRatio(ratio);
  57. }
  58. cache->fill(Qt::transparent);
  59. auto q = Painter(cache.get());
  60. second(q, shift, 0, outerWidth, smaller);
  61. q.setCompositionMode(QPainter::CompositionMode_Source);
  62. q.setPen(QPen(Qt::transparent, 2 * stroke));
  63. q.setBrush(Qt::NoBrush);
  64. const auto radius = st::roundRadiusLarge;
  65. auto hq = PainterHighQualityEnabler(q);
  66. q.drawRoundedRect(QRect(0, shift, smaller, smaller), radius, radius);
  67. q.setCompositionMode(QPainter::CompositionMode_SourceOver);
  68. first(q, 0, shift, outerWidth, smaller);
  69. q.setPen(Qt::NoPen);
  70. q.setBrush(st::shadowFg);
  71. q.drawRoundedRect(QRect(0, shift, smaller, smaller), radius, radius);
  72. q.setPen(st::toastFg);
  73. q.setFont(style::font(smaller / 2, style::FontFlag::Semibold, 0));
  74. q.drawText(
  75. QRect(0, shift, smaller, smaller),
  76. QString::number(totalCount),
  77. style::al_center);
  78. q.end();
  79. p.drawImage(x, y, *cache);
  80. };
  81. }
  82. QByteArray CreditsIconSvg(int strokeWidth) {
  83. auto colorized = qs(Premium::ColorizedSvg(
  84. Premium::CreditsIconGradientStops()));
  85. colorized.replace(
  86. u"stroke=\"none\""_q,
  87. u"stroke=\"%1\""_q.arg(st::creditsStroke->c.name()));
  88. colorized.replace(
  89. u"stroke-width=\"1\""_q,
  90. u"stroke-width=\"%1\""_q.arg(strokeWidth));
  91. return colorized.toUtf8();
  92. }
  93. } // namespace
  94. QImage GenerateStars(int height, int count) {
  95. constexpr auto kOutlineWidth = .6;
  96. constexpr auto kStrokeWidth = 3;
  97. constexpr auto kShift = 3;
  98. auto svg = QSvgRenderer(CreditsIconSvg(kStrokeWidth));
  99. svg.setViewBox(svg.viewBox() + Margins(kStrokeWidth));
  100. const auto starSize = Size(height - kOutlineWidth * 2);
  101. auto frame = QImage(
  102. QSize(
  103. (height + kShift * (count - 1)) * style::DevicePixelRatio(),
  104. height * style::DevicePixelRatio()),
  105. QImage::Format_ARGB32_Premultiplied);
  106. frame.setDevicePixelRatio(style::DevicePixelRatio());
  107. frame.fill(Qt::transparent);
  108. const auto drawSingle = [&](QPainter &q) {
  109. const auto s = kOutlineWidth;
  110. q.save();
  111. q.translate(s, s);
  112. q.setCompositionMode(QPainter::CompositionMode_Clear);
  113. svg.render(&q, QRectF(QPointF(s, 0), starSize));
  114. svg.render(&q, QRectF(QPointF(s, s), starSize));
  115. svg.render(&q, QRectF(QPointF(0, s), starSize));
  116. svg.render(&q, QRectF(QPointF(-s, s), starSize));
  117. svg.render(&q, QRectF(QPointF(-s, 0), starSize));
  118. svg.render(&q, QRectF(QPointF(-s, -s), starSize));
  119. svg.render(&q, QRectF(QPointF(0, -s), starSize));
  120. svg.render(&q, QRectF(QPointF(s, -s), starSize));
  121. q.setCompositionMode(QPainter::CompositionMode_SourceOver);
  122. svg.render(&q, Rect(starSize));
  123. q.restore();
  124. };
  125. {
  126. auto q = QPainter(&frame);
  127. q.translate(frame.width() / style::DevicePixelRatio() - height, 0);
  128. for (auto i = count; i > 0; --i) {
  129. drawSingle(q);
  130. q.translate(-kShift, 0);
  131. }
  132. }
  133. return frame;
  134. }
  135. not_null<RpWidget*> CreateSingleStarWidget(
  136. not_null<RpWidget*> parent,
  137. int height) {
  138. const auto widget = CreateChild<RpWidget>(parent);
  139. const auto image = GenerateStars(height, 1);
  140. widget->resize(image.size() / style::DevicePixelRatio());
  141. widget->paintRequest(
  142. ) | rpl::start_with_next([=] {
  143. auto p = QPainter(widget);
  144. p.drawImage(0, 0, image);
  145. }, widget->lifetime());
  146. widget->setAttribute(Qt::WA_TransparentForMouseEvents);
  147. return widget;
  148. }
  149. not_null<MaskedInputField*> AddInputFieldForCredits(
  150. not_null<VerticalLayout*> container,
  151. rpl::producer<StarsAmount> value) {
  152. const auto &st = st::botEarnInputField;
  153. const auto inputContainer = container->add(
  154. CreateSkipWidget(container, st.heightMin));
  155. const auto currentValue = rpl::variable<StarsAmount>(
  156. rpl::duplicate(value));
  157. const auto input = CreateChild<NumberInput>(
  158. inputContainer,
  159. st,
  160. tr::lng_bot_earn_out_ph(),
  161. QString::number(currentValue.current().whole()),
  162. currentValue.current().whole());
  163. rpl::duplicate(
  164. value
  165. ) | rpl::start_with_next([=](StarsAmount v) {
  166. input->changeLimit(v.whole());
  167. input->setText(QString::number(v.whole()));
  168. }, input->lifetime());
  169. const auto icon = CreateSingleStarWidget(
  170. inputContainer,
  171. st.style.font->height);
  172. inputContainer->sizeValue(
  173. ) | rpl::start_with_next([=](const QSize &size) {
  174. input->resize(
  175. size.width() - rect::m::sum::h(st::boxRowPadding),
  176. st.heightMin);
  177. input->moveToLeft(st::boxRowPadding.left(), 0);
  178. icon->moveToLeft(
  179. st::boxRowPadding.left(),
  180. st.textMargins.top());
  181. }, input->lifetime());
  182. ToggleChildrenVisibility(inputContainer, true);
  183. return input;
  184. }
  185. PaintRoundImageCallback GenerateCreditsPaintUserpicCallback(
  186. const Data::CreditsHistoryEntry &entry) {
  187. using PeerType = Data::CreditsHistoryEntry::PeerType;
  188. if (entry.peerType == PeerType::PremiumBot) {
  189. const auto svg = std::make_shared<QSvgRenderer>(Ui::Premium::Svg());
  190. return [=](Painter &p, int x, int y, int, int size) mutable {
  191. const auto hq = PainterHighQualityEnabler(p);
  192. p.setPen(Qt::NoPen);
  193. {
  194. auto gradient = QLinearGradient(x + size, y + size, x, y);
  195. gradient.setStops(Ui::Premium::ButtonGradientStops());
  196. p.setBrush(gradient);
  197. }
  198. p.drawEllipse(x, y, size, size);
  199. svg->render(&p, QRectF(x, y, size, size) - Margins(size / 5.));
  200. };
  201. }
  202. const auto bg = [&]() -> EmptyUserpic::BgColors {
  203. switch (entry.peerType) {
  204. case Data::CreditsHistoryEntry::PeerType::API:
  205. return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 };
  206. case Data::CreditsHistoryEntry::PeerType::Peer:
  207. return EmptyUserpic::UserpicColor(0);
  208. case Data::CreditsHistoryEntry::PeerType::AppStore:
  209. return { st::historyPeer7UserpicBg, st::historyPeer7UserpicBg2 };
  210. case Data::CreditsHistoryEntry::PeerType::PlayMarket:
  211. return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 };
  212. case Data::CreditsHistoryEntry::PeerType::Fragment:
  213. return { st::windowSubTextFg, st::imageBg };
  214. case Data::CreditsHistoryEntry::PeerType::PremiumBot:
  215. return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 };
  216. case Data::CreditsHistoryEntry::PeerType::Ads:
  217. return { st::historyPeer6UserpicBg, st::historyPeer6UserpicBg2 };
  218. case Data::CreditsHistoryEntry::PeerType::Unsupported:
  219. return {
  220. st::historyPeerArchiveUserpicBg,
  221. st::historyPeerArchiveUserpicBg,
  222. };
  223. }
  224. Unexpected("Unknown peer type.");
  225. }();
  226. const auto userpic = std::make_shared<EmptyUserpic>(bg, QString());
  227. if (entry.peerType == PeerType::API) {
  228. const auto svg = std::make_shared<QSvgRenderer>(Ui::Premium::Svg());
  229. const auto image = std::make_shared<QImage>();
  230. return [=](Painter &p, int x, int y, int outer, int size) mutable {
  231. userpic->paintCircle(p, x, y, outer, size);
  232. if (image->isNull()) {
  233. *image = QImage(
  234. Size(size) * style::DevicePixelRatio(),
  235. QImage::Format_ARGB32_Premultiplied);
  236. image->setDevicePixelRatio(style::DevicePixelRatio());
  237. image->fill(Qt::transparent);
  238. constexpr auto kSize = 126.;
  239. constexpr auto kBubbleRatio = kSize / ((kSize - 70) / 2.);
  240. const auto rect = QRectF(0, 0, size, size)
  241. - Margins(size / kBubbleRatio);
  242. auto q = QPainter(image.get());
  243. const auto hq = PainterHighQualityEnabler(q);
  244. q.setPen(Qt::NoPen);
  245. q.setBrush(st::historyPeerUserpicFg);
  246. q.drawEllipse(rect);
  247. constexpr auto kTailX1 = 4;
  248. constexpr auto kTailY1 = 8;
  249. constexpr auto kTailX2 = 2;
  250. constexpr auto kTailY2 = 0;
  251. constexpr auto kTailX3 = 9;
  252. constexpr auto kTailY3 = 4;
  253. auto path = QPainterPath();
  254. path.moveTo(
  255. st::lineWidth * kTailX1,
  256. rect.height() - st::lineWidth * kTailY1);
  257. path.lineTo(
  258. st::lineWidth * kTailX2,
  259. rect.height() - st::lineWidth * kTailY2);
  260. path.lineTo(
  261. st::lineWidth * kTailX3,
  262. rect.height() - st::lineWidth * kTailY3);
  263. path.translate(rect.x(), rect.y());
  264. q.strokePath(
  265. path,
  266. QPen(
  267. st::historyPeerUserpicFg,
  268. st::lineWidth * 2,
  269. Qt::SolidLine,
  270. Qt::RoundCap,
  271. Qt::RoundJoin));
  272. q.fillPath(path, st::historyPeerUserpicFg);
  273. q.setCompositionMode(QPainter::CompositionMode_Clear);
  274. constexpr auto kStarRatio = kSize / ((kSize - 44) / 2.);
  275. svg->render(
  276. &q,
  277. QRectF(0, 0, size, size) - Margins(size / kStarRatio));
  278. }
  279. p.drawImage(x, y, *image);
  280. };
  281. }
  282. return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
  283. userpic->paintCircle(p, x, y, outerWidth, size);
  284. const auto rect = QRect(x, y, size, size);
  285. ((entry.peerType == PeerType::AppStore)
  286. ? st::sessionIconiPhone
  287. : (entry.peerType == PeerType::PlayMarket)
  288. ? st::sessionIconAndroid
  289. : (entry.peerType == PeerType::Fragment)
  290. ? st::introFragmentIcon
  291. : (entry.peerType == PeerType::Ads)
  292. ? st::creditsHistoryEntryTypeAds
  293. : st::dialogsInaccessibleUserpic).paintInCenter(p, rect);
  294. };
  295. }
  296. PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
  297. not_null<PhotoData*> photo,
  298. Fn<void()> update) {
  299. struct State {
  300. std::shared_ptr<Data::PhotoMedia> view;
  301. Image *imagePtr = nullptr;
  302. QImage image;
  303. rpl::lifetime downloadLifetime;
  304. bool entryImageLoaded = false;
  305. };
  306. const auto state = std::make_shared<State>();
  307. state->view = photo->createMediaView();
  308. photo->load(Data::PhotoSize::Large, {});
  309. rpl::single(rpl::empty_value()) | rpl::then(
  310. photo->owner().session().downloaderTaskFinished()
  311. ) | rpl::start_with_next([=] {
  312. using Size = Data::PhotoSize;
  313. if (const auto large = state->view->image(Size::Large)) {
  314. state->imagePtr = large;
  315. } else if (const auto small = state->view->image(Size::Small)) {
  316. state->imagePtr = small;
  317. } else if (const auto t = state->view->image(Size::Thumbnail)) {
  318. state->imagePtr = t;
  319. }
  320. update();
  321. if (state->view->loaded()) {
  322. state->entryImageLoaded = true;
  323. state->downloadLifetime.destroy();
  324. }
  325. }, state->downloadLifetime);
  326. return [=](Painter &p, int x, int y, int outerWidth, int size) {
  327. if (state->imagePtr
  328. && (!state->entryImageLoaded || state->image.isNull())) {
  329. const auto image = state->imagePtr->original();
  330. const auto minSize = std::min(image.width(), image.height());
  331. state->image = Images::Prepare(
  332. image.copy(
  333. (image.width() - minSize) / 2,
  334. (image.height() - minSize) / 2,
  335. minSize,
  336. minSize),
  337. size * style::DevicePixelRatio(),
  338. { .options = Images::Option::RoundLarge });
  339. }
  340. p.drawImage(x, y, state->image);
  341. };
  342. }
  343. PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
  344. not_null<DocumentData*> video,
  345. Fn<void()> update) {
  346. struct State {
  347. std::shared_ptr<Data::DocumentMedia> view;
  348. Image *imagePtr = nullptr;
  349. QImage image;
  350. rpl::lifetime downloadLifetime;
  351. bool entryImageLoaded = false;
  352. };
  353. const auto state = std::make_shared<State>();
  354. state->view = video->createMediaView();
  355. video->loadThumbnail({});
  356. rpl::single(rpl::empty_value()) | rpl::then(
  357. video->owner().session().downloaderTaskFinished()
  358. ) | rpl::start_with_next([=] {
  359. if (const auto thumbnail = state->view->thumbnail()) {
  360. state->imagePtr = thumbnail;
  361. }
  362. update();
  363. if (state->imagePtr) {
  364. state->entryImageLoaded = true;
  365. state->downloadLifetime.destroy();
  366. }
  367. }, state->downloadLifetime);
  368. return [=](Painter &p, int x, int y, int outerWidth, int size) {
  369. if (state->imagePtr
  370. && (!state->entryImageLoaded || state->image.isNull())) {
  371. const auto image = state->imagePtr->original();
  372. const auto minSize = std::min(image.width(), image.height());
  373. state->image = Images::Prepare(
  374. image.copy(
  375. (image.width() - minSize) / 2,
  376. (image.height() - minSize) / 2,
  377. minSize,
  378. minSize),
  379. size * style::DevicePixelRatio(),
  380. { .options = Images::Option::RoundLarge });
  381. }
  382. p.drawImage(x, y, state->image);
  383. };
  384. }
  385. PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
  386. not_null<Main::Session*> session,
  387. Data::CreditsHistoryMedia media,
  388. Fn<void()> update) {
  389. return (media.type == Data::CreditsHistoryMediaType::Photo)
  390. ? GenerateCreditsPaintEntryCallback(
  391. session->data().photo(media.id),
  392. std::move(update))
  393. : GenerateCreditsPaintEntryCallback(
  394. session->data().document(media.id),
  395. std::move(update));
  396. }
  397. PaintRoundImageCallback GenerateCreditsPaintEntryCallback(
  398. not_null<Main::Session*> session,
  399. const std::vector<Data::CreditsHistoryMedia> &media,
  400. Fn<void()> update) {
  401. if (media.size() == 1) {
  402. return GenerateCreditsPaintEntryCallback(
  403. session,
  404. media.front(),
  405. std::move(update));
  406. }
  407. return MultiThumbnail(
  408. GenerateCreditsPaintEntryCallback(session, media[0], update),
  409. GenerateCreditsPaintEntryCallback(session, media[1], update),
  410. media.size());
  411. }
  412. PaintRoundImageCallback GeneratePaidPhotoPaintCallback(
  413. not_null<PhotoData*> photo,
  414. Fn<void()> update) {
  415. struct State {
  416. explicit State(Fn<void()> update) : spoiler(std::move(update)) {
  417. }
  418. QImage image;
  419. QImage spoilerCornerCache;
  420. SpoilerAnimation spoiler;
  421. };
  422. const auto state = std::make_shared<State>(update);
  423. return [=](Painter &p, int x, int y, int outerWidth, int size) {
  424. if (state->image.isNull()) {
  425. const auto media = photo->createMediaView();
  426. const auto thumbnail = media->thumbnailInline();
  427. const auto ratio = style::DevicePixelRatio();
  428. const auto scaled = QSize(size, size) * ratio;
  429. auto image = thumbnail
  430. ? Images::Blur(thumbnail->original(), true)
  431. : QImage(scaled, QImage::Format_ARGB32_Premultiplied);
  432. if (!thumbnail) {
  433. image.fill(Qt::black);
  434. image.setDevicePixelRatio(ratio);
  435. }
  436. const auto minSize = std::min(image.width(), image.height());
  437. state->image = Images::Prepare(
  438. image.copy(
  439. (image.width() - minSize) / 2,
  440. (image.height() - minSize) / 2,
  441. minSize,
  442. minSize),
  443. size * ratio,
  444. { .options = Images::Option::RoundLarge });
  445. }
  446. p.drawImage(x, y, state->image);
  447. FillSpoilerRect(
  448. p,
  449. QRect(x, y, size, size),
  450. Images::CornersMaskRef(
  451. Images::CornersMask(ImageRoundRadius::Large)),
  452. DefaultImageSpoiler().frame(
  453. state->spoiler.index(crl::now(), false)),
  454. state->spoilerCornerCache);
  455. };
  456. }
  457. PaintRoundImageCallback GeneratePaidMediaPaintCallback(
  458. not_null<PhotoData*> photo,
  459. PhotoData *second,
  460. int totalCount,
  461. Fn<void()> update) {
  462. if (!second) {
  463. return GeneratePaidPhotoPaintCallback(photo, update);
  464. }
  465. return MultiThumbnail(
  466. GeneratePaidPhotoPaintCallback(photo, update),
  467. GeneratePaidPhotoPaintCallback(second, update),
  468. totalCount);
  469. }
  470. PaintRoundImageCallback GenerateGiftStickerUserpicCallback(
  471. not_null<Main::Session*> session,
  472. uint64 stickerId,
  473. Fn<void()> update) {
  474. struct State {
  475. std::optional<UserpicBuilder::PreviewPainter> painter;
  476. int size = 0;
  477. };
  478. const auto state = std::make_shared<State>();
  479. //const auto document = session->data().document(stickerId);
  480. return [=](Painter &p, int x, int y, int outerWidth, int size) {
  481. if (state->size != size || !state->painter) {
  482. state->size = size;
  483. state->painter.emplace(size * M_SQRT2);
  484. state->painter->setDocument(
  485. session->data().document(stickerId),
  486. update);
  487. }
  488. const auto skip = int(base::SafeRound((size * (M_SQRT2 - 1.)) / 2.));
  489. auto hq = PainterHighQualityEnabler(p);
  490. p.translate(x - skip, y - skip);
  491. state->painter->paintForeground(p);
  492. p.translate(skip - x, skip - y);
  493. };
  494. }
  495. Fn<PaintRoundImageCallback(Fn<void()>)> PaintPreviewCallback(
  496. not_null<Main::Session*> session,
  497. const Data::CreditsHistoryEntry &entry) {
  498. const auto &extended = entry.extended;
  499. if (!extended.empty()) {
  500. return [=](Fn<void()> update) {
  501. return GenerateCreditsPaintEntryCallback(
  502. session,
  503. extended,
  504. std::move(update));
  505. };
  506. } else if (entry.photoId && entry.subscriptionUntil.isNull()) {
  507. const auto photo = session->data().photo(entry.photoId);
  508. return [=](Fn<void()> update) {
  509. return GenerateCreditsPaintEntryCallback(
  510. photo,
  511. std::move(update));
  512. };
  513. }
  514. return nullptr;
  515. }
  516. TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) {
  517. return (entry.starrefCommission && !entry.starrefAmount)
  518. ? tr::lng_credits_commission(
  519. tr::now,
  520. lt_amount,
  521. TextWithEntities{
  522. Info::BotStarRef::FormatCommission(entry.starrefCommission)
  523. },
  524. TextWithEntities::Simple)
  525. : entry.paidMessagesCount
  526. ? tr::lng_credits_paid_messages_fee(
  527. tr::now,
  528. lt_count,
  529. entry.paidMessagesCount,
  530. TextWithEntities::Simple)
  531. : (entry.premiumMonthsForStars
  532. ? tr::lng_premium_summary_title
  533. : entry.floodSkip
  534. ? tr::lng_credits_box_history_entry_api
  535. : entry.reaction
  536. ? tr::lng_credits_box_history_entry_reaction_name
  537. : entry.giftUpgraded
  538. ? tr::lng_credits_box_history_entry_gift_upgrade
  539. : entry.bareGiveawayMsgId
  540. ? tr::lng_credits_box_history_entry_giveaway_name
  541. : entry.converted
  542. ? tr::lng_credits_box_history_entry_gift_converted
  543. : (entry.gift && !entry.in && entry.uniqueGift)
  544. ? tr::lng_credits_box_history_entry_gift_transfer
  545. : (entry.starsConverted || (entry.gift && !entry.in))
  546. ? tr::lng_credits_box_history_entry_gift_sent
  547. : entry.gift
  548. ? tr::lng_credits_box_history_entry_gift_name
  549. : (entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment)
  550. ? tr::lng_credits_box_history_entry_fragment
  551. : (entry.peerType == Data::CreditsHistoryEntry::PeerType::PremiumBot)
  552. ? tr::lng_credits_box_history_entry_premium_bot
  553. : (entry.peerType == Data::CreditsHistoryEntry::PeerType::Ads)
  554. ? tr::lng_credits_box_history_entry_ads
  555. : tr::lng_credits_summary_history_entry_inner_in)(
  556. tr::now,
  557. TextWithEntities::Simple);
  558. }
  559. Fn<void(QPainter &)> PaintOutlinedColoredCreditsIconCallback(
  560. int size,
  561. float64 outlineRatio) {
  562. // constexpr auto kIdealSize = 42;
  563. constexpr auto kPoints = uint(16);
  564. constexpr auto kAngleStep = 2. * M_PI / kPoints;
  565. constexpr auto kOutlineWidth = 1.6;
  566. // constexpr auto kStarShift = 3.8;
  567. constexpr auto kStrokeWidth = 3;
  568. const auto starSize = Size(size);
  569. auto svg = std::make_shared<QSvgRenderer>(CreditsIconSvg(kStrokeWidth));
  570. svg->setViewBox(svg->viewBox() + Margins(kStrokeWidth));
  571. const auto s = style::ConvertFloatScale(kOutlineWidth * outlineRatio);
  572. return [=](QPainter &q) {
  573. q.save();
  574. q.setCompositionMode(QPainter::CompositionMode_Clear);
  575. for (auto i = 0; i < kPoints; ++i) {
  576. const auto angle = i * kAngleStep;
  577. const auto x = s * std::cos(angle);
  578. const auto y = s * std::sin(angle);
  579. svg->render(&q, QRectF(QPointF(x, y), starSize));
  580. }
  581. q.setCompositionMode(QPainter::CompositionMode_SourceOver);
  582. svg->render(&q, Rect(starSize));
  583. q.restore();
  584. };
  585. }
  586. QImage CreditsWhiteDoubledIcon(int size, float64 outlineRatio) {
  587. auto svg = QSvgRenderer(Ui::Premium::Svg());
  588. auto result = QImage(
  589. Size(size) * style::DevicePixelRatio(),
  590. QImage::Format_ARGB32_Premultiplied);
  591. result.fill(Qt::transparent);
  592. result.setDevicePixelRatio(style::DevicePixelRatio());
  593. // constexpr auto kIdealSize = 42;
  594. constexpr auto kPoints = uint(16);
  595. constexpr auto kAngleStep = 2. * M_PI / kPoints;
  596. constexpr auto kOutlineWidth = 1.6;
  597. constexpr auto kStarShift = 3.8;
  598. const auto userpicRect = Rect(Size(size));
  599. const auto starRect = userpicRect - Margins(userpicRect.width() / 4.);
  600. const auto starSize = starRect.size();
  601. const auto drawSingle = [&](QPainter &q) {
  602. const auto s = style::ConvertFloatScale(kOutlineWidth * outlineRatio);
  603. q.save();
  604. q.setCompositionMode(QPainter::CompositionMode_Clear);
  605. for (auto i = 0; i < kPoints; ++i) {
  606. const auto angle = i * kAngleStep;
  607. const auto x = s * std::cos(angle);
  608. const auto y = s * std::sin(angle);
  609. svg.render(&q, QRectF(QPointF(x, y), starSize));
  610. }
  611. q.setCompositionMode(QPainter::CompositionMode_SourceOver);
  612. svg.render(&q, Rect(starSize));
  613. q.restore();
  614. };
  615. {
  616. auto p = QPainter(&result);
  617. p.setPen(Qt::NoPen);
  618. p.setBrush(st::lightButtonFg);
  619. p.translate(starRect.topLeft());
  620. p.translate(
  621. style::ConvertFloatScale(kStarShift * outlineRatio) / 2.,
  622. 0);
  623. drawSingle(p);
  624. {
  625. // Remove the previous star at bottom.
  626. p.setCompositionMode(QPainter::CompositionMode_Clear);
  627. p.save();
  628. p.resetTransform();
  629. p.fillRect(
  630. userpicRect.x(),
  631. userpicRect.y(),
  632. userpicRect.width() / 2.,
  633. userpicRect.height(),
  634. Qt::transparent);
  635. p.restore();
  636. }
  637. p.translate(
  638. -style::ConvertFloatScale(kStarShift * outlineRatio),
  639. 0);
  640. drawSingle(p);
  641. }
  642. return result;
  643. }
  644. } // namespace Ui