diff --git a/Telegram/SourceFiles/history/view/history_view_about_view.cpp b/Telegram/SourceFiles/history/view/history_view_about_view.cpp index d26a8818a..2fe291427 100644 --- a/Telegram/SourceFiles/history/view/history_view_about_view.cpp +++ b/Telegram/SourceFiles/history/view/history_view_about_view.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "data/data_user.h" +#include "history/view/media/history_view_giveaway.h" #include "history/view/media/history_view_service_box.h" #include "history/view/media/history_view_sticker_player_abstract.h" #include "history/view/media/history_view_sticker.h" @@ -106,6 +107,49 @@ private: }; +auto GenerateChatIntro( + not_null parent, + Element *replacing, + const Data::ChatIntro &data) +-> Fn)>)> { + return [=](Fn)> push) { + auto pushText = [&]( + TextWithEntities text, + QMargins margins = {}, + const base::flat_map &links = {}) { + if (text.empty()) { + return; + } + push(std::make_unique( + std::move(text), + margins, + links)); + }; + const auto title = data + ? data.title + : tr::lng_chat_intro_default_title(tr::now); + const auto description = data + ? data.description + : tr::lng_chat_intro_default_message(tr::now); + pushText(Ui::Text::Bold(title), st::chatIntroTitleMargin); + pushText({ description }, title.isEmpty() + ? st::chatIntroTitleMargin + : st::chatIntroMargin); + const auto sticker = [=] { + using Tag = ChatHelpers::StickerLottieSize; + return StickerInBubblePart::Data{ + .sticker = data.sticker, + .size = st::chatIntroStickerSize, + .cacheTag = Tag::ChatIntroHelloSticker, + }; + }; + push(std::make_unique( + parent, + replacing, + sticker)); + }; +} + PremiumRequiredBox::PremiumRequiredBox(not_null parent) : _parent(parent) { } @@ -330,13 +374,18 @@ void AboutView::make(Data::ChatIntro data) { | MessageFlag::FakeHistoryItem | MessageFlag::Local), .from = _history->peer->id, - }, PreparedServiceText{ { data.description } }); + }, PreparedServiceText{ { } }); - setItem(AdminLog::OwnedItem(_delegate, item), data.sticker); - - _item->overrideMedia(std::make_unique( - _item.get(), - std::make_unique(_item.get(), data))); + auto owned = AdminLog::OwnedItem(_delegate, item); + owned->overrideMedia(std::make_unique( + owned.get(), + GenerateChatIntro(owned.get(), _item.get(), data), + HistoryView::MediaInBubbleDescriptor{ + .maxWidth = st::chatIntroWidth, + .service = true, + .hideServiceText = true, + })); + setItem(std::move(owned), data.sticker); } void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index f47937516..6db33ff4b 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -632,6 +632,4 @@ private: }; -constexpr auto size = sizeof(Element); - } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp index 663d3a04e..544ff717f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.cpp @@ -73,10 +73,21 @@ bool MediaInBubble::Part::hasHeavyPart() { void MediaInBubble::Part::unloadHeavyPart() { } +auto MediaInBubble::Part::stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements +) -> std::unique_ptr { + return nullptr; +} + MediaInBubble::MediaInBubble( not_null parent, - Fn)>)> generate) -: Media(parent) { + Fn)>)> generate, + MediaInBubbleDescriptor &&descriptor) +: Media(parent) +, _maxWidthCap(descriptor.maxWidth) +, _service(descriptor.service) +, _hideServiceText(descriptor.hideServiceText) { generate([&](std::unique_ptr part) { _entries.push_back({ .object = std::move(part), @@ -92,7 +103,9 @@ MediaInBubble::~MediaInBubble() { } QSize MediaInBubble::countOptimalSize() { - const auto maxWidth = st::chatGiveawayWidth; + const auto maxWidth = _maxWidthCap + ? _maxWidthCap + : st::chatGiveawayWidth; auto top = 0; for (auto &entry : _entries) { @@ -104,19 +117,26 @@ QSize MediaInBubble::countOptimalSize() { } QSize MediaInBubble::countCurrentSize(int newWidth) { - return { maxWidth(), minHeight()}; + return { maxWidth(), minHeight() }; } void MediaInBubble::draw(Painter &p, const PaintContext &context) const { const auto outer = width(); if (outer < st::msgPadding.left() + st::msgPadding.right() + 1) { return; + } else { + PainterHighQualityEnabler hq(p); + const auto radius = st::msgServiceGiftBoxRadius; + p.setPen(Qt::NoPen); + p.setBrush(context.st->msgServiceBg()); + p.drawRoundedRect(QRect(0, 0, width(), height()), radius, radius); } + auto translated = 0; for (const auto &entry : _entries) { const auto raw = entry.object.get(); const auto height = raw->height(); - raw->draw(p, context, outer); + raw->draw(p, this, context, outer); translated += height; p.translate(0, height); } @@ -159,10 +179,28 @@ void MediaInBubble::clickHandlerPressedChanged( } } + +std::unique_ptr MediaInBubble::stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) { + for (const auto &entry : _entries) { + if (auto result = entry.object->stickerTakePlayer( + data, + replacements)) { + return result; + } + } + return nullptr; +} + bool MediaInBubble::hideFromName() const { return !parent()->data()->Has(); } +bool MediaInBubble::hideServiceText() const { + return _hideServiceText; +} + bool MediaInBubble::hasHeavyPart() const { for (const auto &entry : _entries) { if (entry.object->hasHeavyPart()) { @@ -200,15 +238,21 @@ TextMediaInBubblePart::TextMediaInBubblePart( void TextMediaInBubblePart::draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const { - p.setPen(context.messageStyle()->historyTextFg); + const auto service = owner->service(); + p.setPen(service + ? context.st->msgServiceFg() + : context.messageStyle()->historyTextFg); _text.draw(p, { .position = { (outerWidth - width()) / 2, _margins.top() }, .outerWidth = outerWidth, .availableWidth = width(), .align = style::al_top, - .palette = &context.messageStyle()->textPalette, + .palette = &(service + ? context.st->serviceTextPalette() + : context.messageStyle()->textPalette), .now = context.now, }); } @@ -253,6 +297,7 @@ TextDelimeterPart::TextDelimeterPart( void TextDelimeterPart::draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const { const auto stm = context.messageStyle(); @@ -294,18 +339,18 @@ QSize TextDelimeterPart::countCurrentSize(int newWidth) { return { newWidth, minHeight() }; } -StickerWithBadgePart::StickerWithBadgePart( +StickerInBubblePart::StickerInBubblePart( not_null parent, - Fn lookup, - QString badge) + Element *replacing, + Fn lookup) : _parent(parent) -, _lookup(std::move(lookup)) -, _badgeText(badge) { - ensureCreated(); +, _lookup(std::move(lookup)) { + ensureCreated(replacing); } -void StickerWithBadgePart::draw( +void StickerInBubblePart::draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const { const auto stickerSize = st::msgServiceGiftBoxStickerSize; @@ -314,51 +359,99 @@ void StickerWithBadgePart::draw( st::chatGiveawayStickerTop + _skipTop, stickerSize, stickerSize); - + ensureCreated(); if (_sticker) { _sticker->draw(p, context, sticker); - paintBadge(p, context); - } else { - ensureCreated(); } } -bool StickerWithBadgePart::hasHeavyPart() { +bool StickerInBubblePart::hasHeavyPart() { return _sticker && _sticker->hasHeavyPart(); } -void StickerWithBadgePart::unloadHeavyPart() { +void StickerInBubblePart::unloadHeavyPart() { if (_sticker) { _sticker->unloadHeavyPart(); } } -QSize StickerWithBadgePart::countOptimalSize() { +std::unique_ptr StickerInBubblePart::stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) { + return _sticker + ? _sticker->stickerTakePlayer(data, replacements) + : nullptr; +} + +QSize StickerInBubblePart::countOptimalSize() { const auto size = st::msgServiceGiftBoxStickerSize; return { size, st::chatGiveawayStickerTop + size }; } -QSize StickerWithBadgePart::countCurrentSize(int newWidth) { +QSize StickerInBubblePart::countCurrentSize(int newWidth) { return { newWidth, minHeight() }; } -void StickerWithBadgePart::ensureCreated() const { +void StickerInBubblePart::ensureCreated(Element *replacing) const { if (_sticker) { return; } else if (const auto data = _lookup()) { - const auto document = data.sticker; - if (const auto sticker = document->sticker()) { - const auto skipPremiumEffect = false; + const auto sticker = data.sticker; + if (const auto info = sticker->sticker()) { + const auto skipPremiumEffect = true; _skipTop = data.skipTop; - _sticker.emplace(_parent, document, skipPremiumEffect, _parent); - _sticker->setDiceIndex(sticker->alt, 1); - _sticker->initSize(data.isGiftBoxSticker - ? st::msgServiceGiftBoxStickerSize - : 0); + _sticker.emplace(_parent, sticker, skipPremiumEffect, replacing); + if (data.singleTimePlayback) { + _sticker->setDiceIndex(info->alt, 1); + } + _sticker->initSize(data.size); + _sticker->setCustomCachingTag(data.cacheTag); } } } +StickerWithBadgePart::StickerWithBadgePart( + not_null parent, + Element *replacing, + Fn lookup, + QString badge) +: _sticker(parent, replacing, std::move(lookup)) +, _badgeText(badge) { +} + +void StickerWithBadgePart::draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const { + _sticker.draw(p, owner, context, outerWidth); + if (_sticker.resolved()) { + paintBadge(p, context); + } +} + +bool StickerWithBadgePart::hasHeavyPart() { + return _sticker.hasHeavyPart(); +} + +void StickerWithBadgePart::unloadHeavyPart() { + _sticker.unloadHeavyPart(); +} + +std::unique_ptr StickerWithBadgePart::stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) { + return _sticker.stickerTakePlayer(data, replacements); +} + +QSize StickerWithBadgePart::countOptimalSize() { + return _sticker.countOptimalSize(); +} + +QSize StickerWithBadgePart::countCurrentSize(int newWidth) { + return _sticker.countCurrentSize(newWidth); +} + void StickerWithBadgePart::paintBadge( Painter &p, const PaintContext &context) const { @@ -383,7 +476,7 @@ void StickerWithBadgePart::paintBadge( p.drawRoundedRect(inner, radius, radius); } - if (!_parent->usesBubblePattern(context)) { + if (!_sticker.parent()->usesBubblePattern(context)) { paintContent(p); } else { Ui::PaintPatternBubblePart( @@ -458,6 +551,7 @@ PeerBubbleListPart::~PeerBubbleListPart() = default; void PeerBubbleListPart::draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const { if (_peers.empty()) { @@ -647,10 +741,15 @@ auto GenerateGiveawayStart( const auto sticker = [=] { const auto &session = parent->history()->session(); auto &packs = session.giftBoxStickersPacks(); - return Data{ packs.lookup(months), 0, true }; + return Data{ + .sticker = packs.lookup(months), + .size = st::msgServiceGiftBoxStickerSize, + .singleTimePlayback = true, + }; }; push(std::make_unique( parent, + nullptr, sticker, tr::lng_prizes_badge( tr::now, @@ -778,11 +877,15 @@ auto GenerateGiveawayResults( const auto &session = parent->history()->session(); auto &packs = session.diceStickersPacks(); const auto &emoji = Stickers::DicePacks::kPartyPopper; - const auto skip = st::chatGiveawayWinnersTopSkip; - return Data{ packs.lookup(emoji, 0), skip }; + return Data{ + .sticker = packs.lookup(emoji, 0), + .skipTop = st::chatGiveawayWinnersTopSkip, + .singleTimePlayback = true, + }; }; push(std::make_unique( parent, + nullptr, sticker, tr::lng_prizes_badge( tr::now, diff --git a/Telegram/SourceFiles/history/view/media/history_view_giveaway.h b/Telegram/SourceFiles/history/view/media/history_view_giveaway.h index 1aae9340d..33b84f23d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_giveaway.h +++ b/Telegram/SourceFiles/history/view/media/history_view_giveaway.h @@ -22,6 +22,12 @@ class RippleAnimation; namespace HistoryView { +struct MediaInBubbleDescriptor { + int maxWidth = 0; + bool service = false; + bool hideServiceText = false; +}; + class MediaInBubble final : public Media { public: class Part : public Object { @@ -30,6 +36,7 @@ public: virtual void draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const = 0; [[nodiscard]] virtual TextState textState( @@ -41,13 +48,22 @@ public: bool pressed); [[nodiscard]] virtual bool hasHeavyPart(); virtual void unloadHeavyPart(); + [[nodiscard]] virtual auto stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements + ) -> std::unique_ptr; }; MediaInBubble( not_null parent, - Fn)>)> generate); + Fn)>)> generate, + MediaInBubbleDescriptor &&descriptor = {}); ~MediaInBubble(); + [[nodiscard]] bool service() const { + return _service; + } + void draw(Painter &p, const PaintContext &context) const override; TextState textState(QPoint point, StateRequest request) const override; @@ -59,12 +75,16 @@ public: bool pressed) override; bool needsBubble() const override { - return true; + return !_service;; } bool customInfoLayout() const override { return false; } + std::unique_ptr stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) override; + bool toggleSelectionByHandlerClick( const ClickHandlerPtr &p) const override { return true; @@ -74,6 +94,7 @@ public: } bool hideFromName() const override; + bool hideServiceText() const override; void unloadHeavyPart() override; bool hasHeavyPart() const override; @@ -89,6 +110,9 @@ private: [[nodiscard]] QMargins inBubblePadding() const; std::vector _entries; + int _maxWidthCap = 0; + bool _service : 1 = false; + bool _hideServiceText : 1 = false; }; @@ -101,6 +125,7 @@ public: void draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const override; TextState textState( @@ -123,6 +148,7 @@ public: void draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const override; @@ -135,24 +161,34 @@ private: }; -class StickerWithBadgePart final : public MediaInBubble::Part { +class StickerInBubblePart final : public MediaInBubble::Part { public: struct Data { DocumentData *sticker = nullptr; int skipTop = 0; - bool isGiftBoxSticker = false; + int size = 0; + ChatHelpers::StickerLottieSize cacheTag = {}; + bool singleTimePlayback = false; explicit operator bool() const { return sticker != nullptr; } }; - StickerWithBadgePart( + StickerInBubblePart( not_null parent, - Fn lookup, - QString badge); + Element *replacing, + Fn lookup); + + [[nodiscard]] not_null parent() const { + return _parent; + } + [[nodiscard]] bool resolved() const { + return _sticker.has_value(); + } void draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const override; bool hasHeavyPart() override; @@ -161,16 +197,50 @@ public: QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; + std::unique_ptr stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) override; + private: - void ensureCreated() const; - void validateBadge(const PaintContext &context) const; - void paintBadge(Painter &p, const PaintContext &context) const; + void ensureCreated(Element *replacing = nullptr) const; const not_null _parent; Fn _lookup; - QString _badgeText; mutable int _skipTop = 0; mutable std::optional _sticker; + +}; + +class StickerWithBadgePart final : public MediaInBubble::Part { +public: + using Data = StickerInBubblePart::Data; + StickerWithBadgePart( + not_null parent, + Element *replacing, + Fn lookup, + QString badge); + + void draw( + Painter &p, + not_null owner, + const PaintContext &context, + int outerWidth) const override; + bool hasHeavyPart() override; + void unloadHeavyPart() override; + + QSize countOptimalSize() override; + QSize countCurrentSize(int newWidth) override; + + std::unique_ptr stickerTakePlayer( + not_null data, + const Lottie::ColorReplacements *replacements) override; + +private: + void validateBadge(const PaintContext &context) const; + void paintBadge(Painter &p, const PaintContext &context) const; + + StickerInBubblePart _sticker; + QString _badgeText; mutable QColor _badgeFg; mutable QColor _badgeBorder; mutable QImage _badge; @@ -187,6 +257,7 @@ public: void draw( Painter &p, + not_null owner, const PaintContext &context, int outerWidth) const override; TextState textState( diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 92b68860f..16dcf7f14 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1064,3 +1064,5 @@ historyIvIconPadding: margins(2px, 2px, 2px, 0px); chatIntroStickerSize: 96px; chatIntroWidth: 224px; +chatIntroTitleMargin: margins(11px, 16px, 11px, 4px); +chatIntroMargin: margins(11px, 0px, 11px, 0px);