| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988 |
- /*
- 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 "info/peer_gifts/info_peer_gifts_common.h"
- #include "boxes/send_credits_box.h" // SetButtonMarkedLabel
- #include "boxes/star_gift_box.h"
- #include "boxes/sticker_set_box.h"
- #include "chat_helpers/stickers_gift_box_pack.h"
- #include "chat_helpers/stickers_lottie.h"
- #include "core/ui_integration.h"
- #include "data/stickers/data_custom_emoji.h"
- #include "data/data_credits.h" // CreditsHistoryEntry
- #include "data/data_document.h"
- #include "data/data_document_media.h"
- #include "data/data_session.h"
- #include "history/view/media/history_view_sticker_player.h"
- #include "lang/lang_keys.h"
- #include "main/main_session.h"
- #include "settings/settings_credits_graphics.h"
- #include "ui/layers/generic_box.h"
- #include "ui/text/format_values.h"
- #include "ui/text/text_utilities.h"
- #include "ui/widgets/buttons.h"
- #include "ui/dynamic_image.h"
- #include "ui/dynamic_thumbnails.h"
- #include "ui/effects/premium_graphics.h"
- #include "ui/painter.h"
- #include "window/window_session_controller.h"
- #include "styles/style_credits.h"
- #include "styles/style_layers.h"
- #include "styles/style_premium.h"
- namespace Info::PeerGifts {
- namespace {
- constexpr auto kGiftsPerRow = 3;
- } // namespace
- std::strong_ordering operator<=>(const GiftBadge &a, const GiftBadge &b) {
- const auto result1 = (a.text <=> b.text);
- if (result1 != std::strong_ordering::equal) {
- return result1;
- }
- const auto result2 = (a.bg1.rgb() <=> b.bg1.rgb());
- if (result2 != std::strong_ordering::equal) {
- return result2;
- }
- const auto result3 = (a.bg2.rgb() <=> b.bg2.rgb());
- if (result3 != std::strong_ordering::equal) {
- return result3;
- }
- const auto result4 = (a.fg.rgb() <=> b.fg.rgb());
- if (result4 != std::strong_ordering::equal) {
- return result4;
- }
- return a.gradient <=> b.gradient;
- }
- GiftButton::GiftButton(
- QWidget *parent,
- not_null<GiftButtonDelegate*> delegate)
- : AbstractButton(parent)
- , _delegate(delegate) {
- }
- GiftButton::~GiftButton() {
- unsubscribe();
- }
- void GiftButton::unsubscribe() {
- if (base::take(_subscribed)) {
- _userpic->subscribeToUpdates(nullptr);
- }
- }
- void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
- if (_descriptor == descriptor) {
- return;
- }
- auto player = base::take(_player);
- const auto starsType = Ui::Premium::MiniStars::Type::SlowStars;
- _mediaLifetime.destroy();
- _descriptor = descriptor;
- unsubscribe();
- v::match(descriptor, [&](const GiftTypePremium &data) {
- const auto months = data.months;
- _text = Ui::Text::String(st::giftBoxGiftHeight / 4);
- _text.setMarkedText(
- st::defaultTextStyle,
- Ui::Text::Bold(
- tr::lng_months(tr::now, lt_count, months)
- ).append('\n').append(
- tr::lng_gift_premium_label(tr::now)
- ));
- _price.setText(
- st::semiboldTextStyle,
- Ui::FillAmountAndCurrency(
- data.cost,
- data.currency,
- true));
- if (const auto stars = data.stars) {
- const auto starsText = Lang::FormatCountDecimal(stars);
- _byStars.setMarkedText(
- st::giftBoxByStarsStyle,
- tr::lng_gift_premium_by_stars(
- tr::now,
- lt_amount,
- _delegate->ministar().append(' ' + starsText),
- Ui::Text::WithEntities),
- kMarkupTextOptions,
- _delegate->textContext());
- }
- _userpic = nullptr;
- if (!_stars) {
- _stars.emplace(this, true, starsType);
- }
- _stars->setColorOverride(QGradientStops{
- { 0., anim::with_alpha(st::windowActiveTextFg->c, .3) },
- { 1., st::windowActiveTextFg->c },
- });
- }, [&](const GiftTypeStars &data) {
- const auto unique = data.info.unique.get();
- const auto soldOut = data.info.limitedCount
- && !data.userpic
- && !data.info.limitedLeft;
- _userpic = !data.userpic
- ? nullptr
- : data.from
- ? Ui::MakeUserpicThumbnail(data.from)
- : Ui::MakeHiddenAuthorThumbnail();
- if (mode == Mode::Minimal) {
- _price = {};
- _stars.reset();
- return;
- }
- _price.setMarkedText(
- st::semiboldTextStyle,
- (unique
- ? tr::lng_gift_price_unique(tr::now, Ui::Text::WithEntities)
- : _delegate->star().append(
- ' ' + Lang::FormatCountDecimal(data.info.stars))),
- kMarkupTextOptions,
- _delegate->textContext());
- if (!_stars) {
- _stars.emplace(this, true, starsType);
- }
- if (unique) {
- const auto white = QColor(255, 255, 255);
- _stars->setColorOverride(QGradientStops{
- { 0., anim::with_alpha(white, .3) },
- { 1., white },
- });
- } else if (soldOut) {
- _stars.reset();
- } else {
- _stars->setColorOverride(
- Ui::Premium::CreditsIconGradientStops());
- }
- });
- _delegate->sticker(
- descriptor
- ) | rpl::start_with_next([=](not_null<DocumentData*> document) {
- setDocument(document);
- }, lifetime());
- _patterned = false;
- _uniqueBackgroundCache = QImage();
- _uniquePatternEmoji = nullptr;
- _uniquePatternCache.clear();
- if (mode != Mode::Full) {
- _button = QRect();
- _small = true;
- return;
- }
- const auto buttonw = _price.maxWidth();
- const auto buttonh = st::semiboldFont->height;
- const auto inner = QRect(
- QPoint(),
- QSize(buttonw, buttonh)
- ).marginsAdded(st::giftBoxButtonPadding);
- const auto skipy = _delegate->buttonSize().height()
- - (_byStars.isEmpty()
- ? st::giftBoxButtonBottom
- : st::giftBoxButtonBottomByStars)
- - inner.height();
- const auto skipx = (width() - inner.width()) / 2;
- const auto outer = (width() - 2 * skipx);
- _button = QRect(skipx, skipy, outer, inner.height());
- if (_stars) {
- const auto padding = _button.height() / 2;
- _stars->setCenter(_button - QMargins(padding, 0, padding, 0));
- }
- }
- bool GiftButton::documentResolved() const {
- return _player || _mediaLifetime;
- }
- void GiftButton::setDocument(not_null<DocumentData*> document) {
- const auto media = document->createMediaView();
- media->checkStickerLarge();
- media->goodThumbnailWanted();
- rpl::single() | rpl::then(
- document->owner().session().downloaderTaskFinished()
- ) | rpl::filter([=] {
- return media->loaded();
- }) | rpl::start_with_next([=] {
- _mediaLifetime.destroy();
- auto result = std::unique_ptr<HistoryView::StickerPlayer>();
- const auto sticker = document->sticker();
- if (sticker->isLottie()) {
- result = std::make_unique<HistoryView::LottiePlayer>(
- ChatHelpers::LottiePlayerFromDocument(
- media.get(),
- ChatHelpers::StickerLottieSize::InlineResults,
- st::giftBoxStickerSize,
- Lottie::Quality::High));
- } else if (sticker->isWebm()) {
- result = std::make_unique<HistoryView::WebmPlayer>(
- media->owner()->location(),
- media->bytes(),
- st::giftBoxStickerSize);
- } else {
- result = std::make_unique<HistoryView::StaticStickerPlayer>(
- media->owner()->location(),
- media->bytes(),
- st::giftBoxStickerSize);
- }
- result->setRepaintCallback([=] { update(); });
- _player = std::move(result);
- update();
- }, _mediaLifetime);
- }
- void GiftButton::setGeometry(QRect inner, QMargins extend) {
- _extend = extend;
- AbstractButton::setGeometry(inner.marginsAdded(extend));
- }
- QMargins GiftButton::currentExtend() const {
- const auto progress = _selectedAnimation.value(_selected ? 1. : 0.);
- const auto added = anim::interpolate(0, st::giftBoxSelectSkip, progress);
- return _extend + QMargins(added, added, added, added);
- }
- void GiftButton::toggleSelected(bool selected) {
- if (_selected == selected) {
- return;
- }
- const auto duration = st::defaultRoundCheckbox.duration;
- _selected = selected;
- _selectedAnimation.start([=] {
- update();
- }, selected ? 0. : 1., selected ? 1. : 0., duration, anim::easeOutCirc);
- }
- void GiftButton::paintBackground(QPainter &p, const QImage &background) {
- const auto removed = currentExtend() - _extend;
- const auto x = removed.left();
- const auto y = removed.top();
- const auto width = this->width() - x - removed.right();
- const auto height = this->height() - y - removed.bottom();
- const auto dpr = int(background.devicePixelRatio());
- const auto bwidth = background.width() / dpr;
- const auto bheight = background.height() / dpr;
- const auto fillRow = [&](int yfrom, int ytill, int bfrom) {
- const auto fill = [&](int xto, int wto, int xfrom, int wfrom = 0) {
- const auto fheight = ytill - yfrom;
- p.drawImage(
- QRect(x + xto, y + yfrom, wto, fheight),
- background,
- QRect(
- QPoint(xfrom, bfrom) * dpr,
- QSize((wfrom ? wfrom : wto), fheight) * dpr));
- };
- if (width < bwidth) {
- const auto xhalf = width / 2;
- fill(0, xhalf, 0);
- fill(xhalf, width - xhalf, bwidth - (width - xhalf));
- } else if (width == bwidth) {
- fill(0, width, 0);
- } else {
- const auto half = bwidth / (2 * dpr);
- fill(0, half, 0);
- fill(width - half, half, bwidth - half);
- fill(half, width - 2 * half, half, 1);
- }
- };
- if (height < bheight) {
- fillRow(0, height / 2, 0);
- fillRow(height / 2, height, bheight - (height - (height / 2)));
- } else {
- fillRow(0, height, 0);
- }
- auto hq = PainterHighQualityEnabler(p);
- const auto progress = _selectedAnimation.value(_selected ? 1. : 0.);
- const auto pwidth = progress * st::defaultRoundCheckbox.width;
- p.setPen(QPen(st::defaultRoundCheckbox.bgActive->c, pwidth));
- p.setBrush(Qt::NoBrush);
- const auto rounded = rect().marginsRemoved(_extend);
- const auto phalf = pwidth / 2.;
- const auto extended = QRectF(rounded).marginsRemoved(
- { phalf, phalf, phalf, phalf });
- const auto xradius = removed.left() + st::giftBoxGiftRadius - phalf;
- const auto yradius = removed.top() + st::giftBoxGiftRadius - phalf;
- p.drawRoundedRect(extended, xradius, yradius);
- }
- void GiftButton::resizeEvent(QResizeEvent *e) {
- if (!_button.isEmpty()) {
- _button.moveLeft((width() - _button.width()) / 2);
- if (_stars) {
- const auto padding = _button.height() / 2;
- _stars->setCenter(_button - QMargins(padding, 0, padding, 0));
- }
- }
- }
- void GiftButton::contextMenuEvent(QContextMenuEvent *e) {
- _contextMenuRequests.fire_copy((e->reason() == QContextMenuEvent::Mouse)
- ? e->globalPos()
- : QCursor::pos());
- }
- void GiftButton::cacheUniqueBackground(
- not_null<Data::UniqueGift*> unique,
- int width,
- int height) {
- if (!_uniquePatternEmoji) {
- _uniquePatternEmoji = _delegate->buttonPatternEmoji(unique, [=] {
- update();
- });
- [[maybe_unused]] const auto preload = _uniquePatternEmoji->ready();
- }
- const auto outer = QRect(0, 0, width, height);
- const auto extend = currentExtend();
- const auto inner = outer.marginsRemoved(
- extend
- ).translated(-extend.left(), -extend.top());
- const auto ratio = style::DevicePixelRatio();
- if (_uniqueBackgroundCache.size() != inner.size() * ratio) {
- _uniqueBackgroundCache = QImage(
- inner.size() * ratio,
- QImage::Format_ARGB32_Premultiplied);
- _uniqueBackgroundCache.fill(Qt::transparent);
- _uniqueBackgroundCache.setDevicePixelRatio(ratio);
- const auto radius = st::giftBoxGiftRadius;
- auto p = QPainter(&_uniqueBackgroundCache);
- auto hq = PainterHighQualityEnabler(p);
- auto gradient = QRadialGradient(inner.center(), inner.width() / 2);
- gradient.setStops({
- { 0., unique->backdrop.centerColor },
- { 1., unique->backdrop.edgeColor },
- });
- p.setBrush(gradient);
- p.setPen(Qt::NoPen);
- p.drawRoundedRect(inner, radius, radius);
- _patterned = false;
- }
- if (!_patterned && _uniquePatternEmoji->ready()) {
- _patterned = true;
- auto p = QPainter(&_uniqueBackgroundCache);
- p.setClipRect(inner);
- const auto skip = inner.width() / 3;
- Ui::PaintPoints(
- p,
- Ui::PatternPointsSmall(),
- _uniquePatternCache,
- _uniquePatternEmoji.get(),
- *unique,
- QRect(-skip, 0, inner.width() + 2 * skip, inner.height()));
- }
- }
- void GiftButton::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- const auto unique = v::is<GiftTypeStars>(_descriptor)
- ? v::get<GiftTypeStars>(_descriptor).info.unique.get()
- : nullptr;
- const auto hidden = v::is<GiftTypeStars>(_descriptor)
- && v::get<GiftTypeStars>(_descriptor).hidden;;
- const auto extend = currentExtend();
- const auto position = QPoint(extend.left(), extend.top());
- const auto background = _delegate->background();
- const auto width = this->width();
- const auto dpr = int(background.devicePixelRatio());
- paintBackground(p, background);
- if (unique) {
- cacheUniqueBackground(unique, width, background.height() / dpr);
- p.drawImage(extend.left(), extend.top(), _uniqueBackgroundCache);
- }
- if (_userpic) {
- if (!_subscribed) {
- _subscribed = true;
- _userpic->subscribeToUpdates([=] { update(); });
- }
- const auto image = _userpic->image(st::giftBoxUserpicSize);
- const auto skip = st::giftBoxUserpicSkip;
- p.drawImage(extend.left() + skip, extend.top() + skip, image);
- }
- auto frame = QImage();
- if (_player && _player->ready()) {
- const auto paused = !isOver();
- auto info = _player->frame(
- st::giftBoxStickerSize,
- QColor(0, 0, 0, 0),
- false,
- crl::now(),
- paused);
- frame = info.image;
- const auto finished = (info.index + 1 == _player->framesCount());
- if (!finished || !paused) {
- _player->markFrameShown();
- }
- const auto size = frame.size() / style::DevicePixelRatio();
- p.drawImage(
- QRect(
- (width - size.width()) / 2,
- (_small
- ? st::giftBoxSmallStickerTop
- : _text.isEmpty()
- ? st::giftBoxStickerStarTop
- : _byStars.isEmpty()
- ? st::giftBoxStickerTop
- : st::giftBoxStickerTopByStars),
- size.width(),
- size.height()),
- frame);
- }
- if (hidden) {
- const auto topleft = QPoint(
- (width - st::giftBoxStickerSize.width()) / 2,
- (_small
- ? st::giftBoxSmallStickerTop
- : _text.isEmpty()
- ? st::giftBoxStickerStarTop
- : _byStars.isEmpty()
- ? st::giftBoxStickerTop
- : st::giftBoxStickerTopByStars));
- _delegate->hiddenMark()->paint(
- p,
- frame,
- _hiddenBgCache,
- topleft,
- st::giftBoxStickerSize,
- width);
- }
- auto hq = PainterHighQualityEnabler(p);
- const auto premium = v::is<GiftTypePremium>(_descriptor);
- const auto singlew = width - extend.left() - extend.right();
- const auto font = st::semiboldFont;
- p.setFont(font);
- const auto badge = v::match(_descriptor, [&](GiftTypePremium data) {
- if (data.discountPercent > 0) {
- p.setBrush(st::attentionButtonFg);
- const auto kMinus = QChar(0x2212);
- return GiftBadge{
- .text = kMinus + QString::number(data.discountPercent) + '%',
- .bg1 = st::premiumButtonBg3->c,
- .bg2 = st::premiumButtonBg2->c,
- .fg = st::windowBg->c,
- .gradient = true,
- .small = true,
- };
- }
- return GiftBadge();
- }, [&](const GiftTypeStars &data) {
- const auto count = data.info.limitedCount;
- const auto pinned = data.pinned || data.pinnedSelection;
- if (count || pinned) {
- const auto soldOut = !pinned
- && !data.userpic
- && !data.info.limitedLeft;
- return GiftBadge{
- .text = (soldOut
- ? tr::lng_gift_stars_sold_out(tr::now)
- : (unique && pinned)
- ? ('#' + QString::number(unique->number))
- : (!data.userpic && !data.info.unique)
- ? tr::lng_gift_stars_limited(tr::now)
- : (count == 1)
- ? tr::lng_gift_limited_of_one(tr::now)
- : tr::lng_gift_limited_of_count(
- tr::now,
- lt_amount,
- (((count % 1000) && (count < 10'000))
- ? Lang::FormatCountDecimal(count)
- : Lang::FormatCountToShort(count).string))),
- .bg1 = (unique
- ? unique->backdrop.edgeColor
- : soldOut
- ? st::attentionButtonFg->c
- : st::windowActiveTextFg->c),
- .bg2 = (unique
- ? unique->backdrop.patternColor
- : QColor(0, 0, 0, 0)),
- .fg = unique ? QColor(255, 255, 255) : st::windowBg->c,
- .small = true,
- };
- }
- return GiftBadge();
- });
- if (badge) {
- const auto rubberOut = st::lineWidth;
- const auto inner = rect().marginsRemoved(extend);
- p.setClipRect(inner.marginsAdded(
- { rubberOut, rubberOut, rubberOut, rubberOut }));
- const auto cached = _delegate->cachedBadge(badge);
- const auto width = cached.width() / cached.devicePixelRatio();
- p.drawImage(
- position.x() + singlew + rubberOut - width,
- position.y() - rubberOut,
- cached);
- }
- v::match(_descriptor, [](const GiftTypePremium &) {
- }, [&](const GiftTypeStars &data) {
- if (unique && data.pinned) {
- auto hq = PainterHighQualityEnabler(p);
- const auto &icon = st::giftBoxPinIcon;
- const auto skip = st::giftBoxUserpicSkip;
- const auto add = (st::giftBoxUserpicSize - icon.width()) / 2;
- p.setPen(Qt::NoPen);
- p.setBrush(unique->backdrop.patternColor);
- const auto rect = QRect(
- QPoint(extend.left() + skip, extend.top() + skip),
- QSize(icon.width() + 2 * add, icon.height() + 2 * add));
- p.drawEllipse(rect);
- icon.paintInCenter(p, rect);
- }
- });
- if (!_button.isEmpty()) {
- p.setBrush(unique
- ? QBrush(QColor(255, 255, 255, .2 * 255))
- : premium
- ? st::lightButtonBgOver
- : st::creditsBg3);
- p.setPen(Qt::NoPen);
- if (!unique && !premium) {
- p.setOpacity(0.12);
- }
- const auto geometry = _button;
- const auto radius = geometry.height() / 2.;
- p.drawRoundedRect(geometry, radius, radius);
- if (!premium) {
- p.setOpacity(1.);
- }
- if (_stars) {
- if (unique) {
- _stars->paint(p);
- } else {
- auto clipPath = QPainterPath();
- clipPath.addRoundedRect(geometry, radius, radius);
- p.setClipPath(clipPath);
- _stars->paint(p);
- p.setClipping(false);
- }
- }
- }
- if (!_text.isEmpty()) {
- p.setPen(st::windowFg);
- _text.draw(p, {
- .position = (position + QPoint(0, _byStars.isEmpty()
- ? st::giftBoxPremiumTextTop
- : st::giftBoxPremiumTextTopByStars)),
- .availableWidth = singlew,
- .align = style::al_top,
- });
- }
- if (!_button.isEmpty()) {
- const auto padding = st::giftBoxButtonPadding;
- p.setPen(unique
- ? QPen(QColor(255, 255, 255))
- : premium
- ? st::windowActiveTextFg
- : st::creditsFg);
- _price.draw(p, {
- .position = (_button.topLeft()
- + QPoint(padding.left(), padding.top())),
- .availableWidth = _price.maxWidth(),
- });
- if (!_byStars.isEmpty()) {
- p.setPen(st::creditsFg);
- _byStars.draw(p, {
- .position = QPoint(
- position.x(),
- _button.y() + _button.height() + st::giftBoxByStarsSkip),
- .availableWidth = singlew,
- .align = style::al_top,
- });
- }
- }
- }
- Delegate::Delegate(not_null<Main::Session*> session, GiftButtonMode mode)
- : _session(session)
- , _hiddenMark(std::make_unique<StickerPremiumMark>(
- _session,
- st::giftBoxHiddenMark,
- RectPart::Center))
- , _mode(mode) {
- }
- Delegate::Delegate(Delegate &&other) = default;
- Delegate::~Delegate() = default;
- TextWithEntities Delegate::star() {
- return _session->data().customEmojiManager().creditsEmoji();
- }
- TextWithEntities Delegate::ministar() {
- const auto owner = &_session->data();
- const auto top = st::giftBoxByStarsStarTop;
- return owner->customEmojiManager().ministarEmoji({ 0, top, 0, 0 });
- }
- Ui::Text::MarkedContext Delegate::textContext() {
- return Core::TextContext({ .session = _session });
- }
- QSize Delegate::buttonSize() {
- if (!_single.isEmpty()) {
- return _single;
- }
- const auto width = st::boxWideWidth;
- const auto padding = st::giftBoxPadding;
- const auto available = width - padding.left() - padding.right();
- const auto singlew = (available - 2 * st::giftBoxGiftSkip.x())
- / kGiftsPerRow;
- const auto minimal = (_mode == GiftButtonMode::Minimal);
- _single = QSize(
- singlew,
- minimal ? st::giftBoxGiftSmall : st::giftBoxGiftHeight);
- return _single;
- }
- QMargins Delegate::buttonExtend() {
- return st::defaultDropdownMenu.wrap.shadow.extend;
- }
- auto Delegate::buttonPatternEmoji(
- not_null<Data::UniqueGift*> unique,
- Fn<void()> repaint)
- -> std::unique_ptr<Ui::Text::CustomEmoji> {
- return _session->data().customEmojiManager().create(
- unique->pattern.document,
- repaint,
- Data::CustomEmojiSizeTag::Large);
- }
- QImage Delegate::background() {
- if (!_bg.isNull()) {
- return _bg;
- }
- const auto single = buttonSize();
- const auto extend = buttonExtend();
- const auto bgSize = single.grownBy(extend);
- const auto ratio = style::DevicePixelRatio();
- auto bg = QImage(
- bgSize * ratio,
- QImage::Format_ARGB32_Premultiplied);
- bg.setDevicePixelRatio(ratio);
- bg.fill(Qt::transparent);
- const auto radius = st::giftBoxGiftRadius;
- const auto rect = QRect(QPoint(), bgSize).marginsRemoved(extend);
- {
- auto p = QPainter(&bg);
- auto hq = PainterHighQualityEnabler(p);
- p.setOpacity(0.3);
- p.setPen(Qt::NoPen);
- p.setBrush(st::windowShadowFg);
- p.drawRoundedRect(
- QRectF(rect).translated(0, radius / 12.),
- radius,
- radius);
- }
- bg = bg.scaled(
- (bgSize * ratio) / 2,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- bg = Images::Blur(std::move(bg), true);
- bg = bg.scaled(
- bgSize * ratio,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- {
- auto p = QPainter(&bg);
- auto hq = PainterHighQualityEnabler(p);
- p.setPen(Qt::NoPen);
- p.setBrush(st::windowBg);
- p.drawRoundedRect(rect, radius, radius);
- }
- _bg = std::move(bg);
- return _bg;
- }
- rpl::producer<not_null<DocumentData*>> Delegate::sticker(
- const GiftDescriptor &descriptor) {
- return GiftStickerValue(_session, descriptor);
- }
- not_null<StickerPremiumMark*> Delegate::hiddenMark() {
- return _hiddenMark.get();
- }
- QImage Delegate::cachedBadge(const GiftBadge &badge) {
- auto &image = _badges[badge];
- if (image.isNull()) {
- const auto &extend = buttonExtend();
- image = ValidateRotatedBadge(badge, extend.top());
- }
- return image;
- }
- DocumentData *LookupGiftSticker(
- not_null<Main::Session*> session,
- const GiftDescriptor &descriptor) {
- return v::match(descriptor, [&](GiftTypePremium data) {
- auto &packs = session->giftBoxStickersPacks();
- packs.load();
- return packs.lookup(data.months);
- }, [&](GiftTypeStars data) {
- return data.info.document.get();
- });
- }
- rpl::producer<not_null<DocumentData*>> GiftStickerValue(
- not_null<Main::Session*> session,
- const GiftDescriptor &descriptor) {
- return v::match(descriptor, [&](GiftTypePremium data) {
- const auto months = data.months;
- auto &packs = session->giftBoxStickersPacks();
- packs.load();
- if (const auto result = packs.lookup(months)) {
- return result->sticker()
- ? (rpl::single(not_null(result)) | rpl::type_erased())
- : rpl::never<not_null<DocumentData*>>();
- }
- return packs.updated(
- ) | rpl::map([=] {
- return session->giftBoxStickersPacks().lookup(data.months);
- }) | rpl::filter([](DocumentData *document) {
- return document && document->sticker();
- }) | rpl::take(1) | rpl::map([=](DocumentData *document) {
- return not_null(document);
- }) | rpl::type_erased();
- }, [&](GiftTypeStars data) {
- return rpl::single(data.info.document) | rpl::type_erased();
- });
- }
- QImage ValidateRotatedBadge(const GiftBadge &badge, int added) {
- const auto &font = badge.small
- ? st::giftBoxGiftBadgeFont
- : st::semiboldFont;
- const auto twidth = font->width(badge.text) + 2 * added;
- const auto skip = int(std::ceil(twidth / M_SQRT2));
- const auto ratio = style::DevicePixelRatio();
- const auto multiplier = ratio * 3;
- const auto size = (twidth + font->height * 2);
- const auto textpos = QPoint(size - skip, added);
- auto image = QImage(
- QSize(size, size) * multiplier,
- QImage::Format_ARGB32_Premultiplied);
- image.fill(Qt::transparent);
- image.setDevicePixelRatio(multiplier);
- {
- auto p = QPainter(&image);
- auto hq = PainterHighQualityEnabler(p);
- p.translate(textpos);
- p.rotate(45.);
- p.setFont(font);
- p.setPen(badge.fg);
- p.drawText(QPoint(added, font->ascent), badge.text);
- }
- auto scaled = image.scaled(
- QSize(size, size) * ratio,
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- scaled.setDevicePixelRatio(ratio);
- auto result = QImage(
- QSize(size, size) * ratio,
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(ratio);
- result.fill(Qt::transparent);
- {
- auto p = QPainter(&result);
- auto hq = PainterHighQualityEnabler(p);
- p.setPen(Qt::NoPen);
- p.save();
- p.translate(textpos);
- p.rotate(45.);
- const auto rect = QRect(-5 * twidth, 0, twidth * 12, font->height);
- if (badge.gradient) {
- const auto skip = font->height / M_SQRT2;
- auto gradient = QLinearGradient(
- QPointF(-twidth - skip, 0),
- QPointF(twidth + skip, 0));
- gradient.setStops({
- { 0., badge.bg1 },
- { 1., badge.bg2 },
- });
- p.setBrush(gradient);
- p.drawRect(rect);
- } else {
- p.setBrush(badge.bg1);
- p.drawRect(rect);
- if (badge.bg2.alpha() > 0) {
- p.setOpacity(0.5);
- p.setBrush(badge.bg2);
- p.drawRect(rect);
- p.setOpacity(1.);
- }
- }
- p.restore();
- p.drawImage(0, 0, scaled);
- }
- return result;
- }
- void SelectGiftToUnpin(
- std::shared_ptr<ChatHelpers::Show> show,
- const std::vector<Data::CreditsHistoryEntry> &pinned,
- Fn<void(Data::SavedStarGiftId)> chosen) {
- show->show(Box([=](not_null<Ui::GenericBox*> box) {
- struct State {
- explicit State(not_null<Main::Session*> session)
- : delegate(session, GiftButtonMode::Minimal) {
- }
- Delegate delegate;
- rpl::variable<int> selected = -1;
- std::vector<not_null<GiftButton*>> buttons;
- };
- const auto session = &show->session();
- const auto state = box->lifetime().make_state<State>(session);
- box->setStyle(st::giftTooManyPinnedBox);
- box->setWidth(st::boxWideWidth);
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box,
- tr::lng_gift_many_pinned_title(),
- st::giftBoxSubtitle),
- st::giftBoxSubtitleMargin);
- box->addRow(
- object_ptr<Ui::FlatLabel>(
- box,
- tr::lng_gift_many_pinned_choose(),
- st::giftTooManyPinnedChoose),
- st::giftBoxAboutMargin);
- const auto gifts = box->addRow(
- object_ptr<Ui::RpWidget>(box),
- QMargins(
- st::giftBoxPadding.left(),
- st::giftTooManyPinnedBox.buttonPadding.top(),
- st::giftBoxPadding.right(),
- 0));
- for (const auto &entry : pinned) {
- const auto index = int(state->buttons.size());
- state->buttons.push_back(
- Ui::CreateChild<GiftButton>(gifts, &state->delegate));
- const auto button = state->buttons.back();
- button->setDescriptor(GiftTypeStars{
- .info = {
- .id = entry.stargiftId,
- .unique = entry.uniqueGift,
- .document = entry.uniqueGift->model.document,
- },
- .pinnedSelection = true,
- }, GiftButton::Mode::Minimal);
- button->setClickedCallback([=] {
- const auto now = state->selected.current();
- state->selected = (now == index) ? -1 : index;
- });
- }
- state->selected.value(
- ) | rpl::combine_previous(
- ) | rpl::start_with_next([=](int old, int now) {
- if (old >= 0) state->buttons[old]->toggleSelected(false);
- if (now >= 0) state->buttons[now]->toggleSelected(true);
- }, gifts->lifetime());
- gifts->widthValue() | rpl::start_with_next([=](int width) {
- const auto singleMin = state->delegate.buttonSize();
- if (width < singleMin.width()) {
- return;
- }
- const auto count = int(state->buttons.size());
- const auto skipw = st::giftBoxGiftSkip.x();
- const auto skiph = st::giftBoxGiftSkip.y();
- const auto perRow = std::min(
- (width + skipw) / (singleMin.width() + skipw),
- std::max(count, 1));
- if (perRow <= 0) {
- return;
- }
- const auto single = (width - (perRow - 1) * skipw) / perRow;
- const auto height = singleMin.height();
- const auto rows = (count + perRow - 1) / perRow;
- for (auto row = 0; row != rows; ++row) {
- const auto y = row * (height + skiph);
- for (auto column = 0; column != perRow; ++column) {
- const auto index = row * perRow + column;
- if (index >= count) {
- break;
- }
- const auto &button = state->buttons[index];
- const auto x = column * (single + skipw);
- button->setGeometry(
- QRect(x, y, single, height),
- state->delegate.buttonExtend());
- }
- }
- gifts->resize(width, rows * (height + skiph) - skiph);
- }, gifts->lifetime());
- const auto button = box->addButton(rpl::single(QString()), [=] {
- const auto index = state->selected.current();
- if (index < 0) {
- return;
- }
- Assert(index < int(pinned.size()));
- const auto &entry = pinned[index];
- const auto weak = Ui::MakeWeak(box);
- chosen(::Settings::EntryToSavedStarGiftId(session, entry));
- if (const auto strong = weak.data()) {
- strong->closeBox();
- }
- });
- const auto label = Ui::SetButtonMarkedLabel(
- button,
- tr::lng_context_unpin_from_top(Ui::Text::WithEntities),
- &show->session(),
- st::creditsBoxButtonLabel,
- &st::giftTooManyPinnedBox.button.textFg);
- state->selected.value() | rpl::start_with_next([=](int value) {
- const auto has = (value >= 0);
- label->setOpacity(has ? 1. : 0.5);
- button->setAttribute(Qt::WA_TransparentForMouseEvents, !has);
- }, box->lifetime());
- const auto buttonPadding = st::giftTooManyPinnedBox.buttonPadding;
- const auto buttonWidth = st::boxWideWidth
- - buttonPadding.left()
- - buttonPadding.right();
- button->resizeToWidth(buttonWidth);
- button->widthValue() | rpl::start_with_next([=](int width) {
- if (width != buttonWidth) {
- button->resizeToWidth(buttonWidth);
- }
- }, button->lifetime());
- }));
- }
- } // namespace Info::PeerGifts
|