diff --git a/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp b/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp index eeea9b166..efb9fc573 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_unique_gift.cpp @@ -469,7 +469,7 @@ auto GenerateUniqueGiftMedia( st::chatUniqueButtonPadding, [=] { parent->repaint(); }, std::move(link), - gift->backdrop.patternColor)); + anim::with_alpha(gift->backdrop.patternColor, 0.75))); } }; } diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp index 3023a46f7..4174948d5 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/peer_gifts/info_peer_gifts_common.h" +#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" @@ -86,12 +87,16 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) { { 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; _price.setMarkedText( st::semiboldTextStyle, - _delegate->star().append(' ' + QString::number(data.info.stars)), + (unique + ? tr::lng_gift_price_unique(tr::now, Ui::Text::WithEntities) + : _delegate->star().append( + ' ' + QString::number(data.info.stars))), kMarkupTextOptions, _delegate->textContext()); _userpic = !data.userpic @@ -99,7 +104,13 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) { : data.from ? Ui::MakeUserpicThumbnail(data.from) : Ui::MakeHiddenAuthorThumbnail(); - if (soldOut) { + if (unique) { + const auto white = QColor(255, 255, 255); + _stars.setColorOverride(QGradientStops{ + { 0., anim::with_alpha(white, .3) }, + { 1., white }, + }); + } else if (soldOut) { _stars.setColorOverride(QGradientStops{ { 0., Qt::transparent }, { 1., Qt::transparent }, @@ -187,8 +198,61 @@ void GiftButton::resizeEvent(QResizeEvent *e) { } } +void GiftButton::cacheUniqueBackground( + not_null 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 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.setOpacity(0.5); + p.setClipRect(inner); + const auto skip = inner.width() / 3; + Ui::PaintPoints( + p, + _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(_descriptor) + ? v::get(_descriptor).info.unique.get() + : nullptr; const auto hidden = v::is(_descriptor) && v::get(_descriptor).hidden;; const auto position = QPoint(_extend.left(), _extend.top()); @@ -214,6 +278,10 @@ void GiftButton::paintEvent(QPaintEvent *e) { background, QRect(half, 0, 1, height)); } + if (unique) { + cacheUniqueBackground(unique, width, background.height() / dpr); + p.drawImage(_extend.left(), _extend.top(), _uniqueBackgroundCache); + } if (_userpic) { if (!_subscribed) { @@ -280,7 +348,9 @@ void GiftButton::paintEvent(QPaintEvent *e) { }, [&](const GiftTypeStars &data) { if (const auto count = data.info.limitedCount) { const auto soldOut = !data.userpic && !data.info.limitedLeft; - p.setBrush(soldOut + p.setBrush(unique + ? QBrush(unique->backdrop.patternColor) + : soldOut ? st::attentionButtonFg : st::windowActiveTextFg); return soldOut @@ -311,13 +381,17 @@ void GiftButton::paintEvent(QPaintEvent *e) { p.rotate(45.); p.translate(-pos); p.drawRect(-5 * twidth, position.y(), twidth * 12, font->height); - p.setPen(st::windowBg); + p.setPen(unique ? QPen(QColor(255, 255, 255)) : st::windowBg); p.drawText(pos - QPoint(0, font->descent), text); p.restore(); } - p.setBrush(premium ? st::lightButtonBgOver : st::creditsBg3); + p.setBrush(unique + ? QBrush(QColor(255, 255, 255, .2 * 255)) + : premium + ? st::lightButtonBgOver + : st::creditsBg3); p.setPen(Qt::NoPen); - if (!premium) { + if (!unique && !premium) { p.setOpacity(0.12); } const auto geometry = _button; @@ -326,7 +400,9 @@ void GiftButton::paintEvent(QPaintEvent *e) { if (!premium) { p.setOpacity(1.); } - { + if (unique) { + _stars.paint(p); + } else { auto clipPath = QPainterPath(); clipPath.addRoundedRect(geometry, radius, radius); p.setClipPath(clipPath); @@ -345,7 +421,11 @@ void GiftButton::paintEvent(QPaintEvent *e) { } const auto padding = st::giftBoxButtonPadding; - p.setPen(premium ? st::windowActiveTextFg : st::creditsFg); + p.setPen(unique + ? QPen(QColor(255, 255, 255)) + : premium + ? st::windowActiveTextFg + : st::creditsFg); _price.draw(p, { .position = (geometry.topLeft() + QPoint(padding.left(), padding.top())), @@ -394,6 +474,16 @@ QMargins Delegate::buttonExtend() { return st::defaultDropdownMenu.wrap.shadow.extend; } +auto Delegate::buttonPatternEmoji( + not_null unique, + Fn repaint) +-> std::unique_ptr { + return _window->session().data().customEmojiManager().create( + unique->pattern.document, + repaint, + Data::CustomEmojiSizeTag::Large); +} + QImage Delegate::background() { if (!_bg.isNull()) { return _bg; diff --git a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h index 2db0ee2f0..48e502ea7 100644 --- a/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h +++ b/Telegram/SourceFiles/info/peer_gifts/info_peer_gifts_common.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class StickerPremiumMark; +namespace Data { +struct UniqueGift; +} // namespace Data + namespace HistoryView { class StickerPlayer; } // namespace HistoryView @@ -26,6 +30,10 @@ namespace Ui { class DynamicImage; } // namespace Ui +namespace Ui::Text { +class CustomEmoji; +} // namespace Ui::Text + namespace Window { class SessionController; } // namespace Window @@ -69,6 +77,10 @@ public: [[nodiscard]] virtual std::any textContext() = 0; [[nodiscard]] virtual QSize buttonSize() = 0; [[nodiscard]] virtual QMargins buttonExtend() = 0; + [[nodiscard]] virtual auto buttonPatternEmoji( + not_null unique, + Fn repaint) + -> std::unique_ptr = 0; [[nodiscard]] virtual QImage background() = 0; [[nodiscard]] virtual rpl::producer> sticker( const GiftDescriptor &descriptor) = 0; @@ -87,6 +99,11 @@ private: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; + void cacheUniqueBackground( + not_null unique, + int width, + int height); + void setDocument(not_null document); [[nodiscard]] bool documentResolved() const; @@ -98,8 +115,12 @@ private: Ui::Text::String _text; Ui::Text::String _price; std::shared_ptr _userpic; + QImage _uniqueBackgroundCache; + std::unique_ptr _uniquePatternEmoji; + base::flat_map _uniquePatternCache; Ui::Premium::ColoredMiniStars _stars; bool _subscribed = false; + bool _patterned = false; QRect _button; QMargins _extend; @@ -119,6 +140,10 @@ public: std::any textContext() override; QSize buttonSize() override; QMargins buttonExtend() override; + auto buttonPatternEmoji( + not_null unique, + Fn repaint) + -> std::unique_ptr override; QImage background() override; rpl::producer> sticker( const GiftDescriptor &descriptor) override;