diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 5a4ab88e7d..b6df77a809 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -673,7 +673,12 @@ void InnerWidget::elementReplyTo(const FullMsgId &to) { void InnerWidget::elementStartInteraction(not_null view) { } -void InnerWidget::elementStartPremium(not_null view) { +void InnerWidget::elementStartPremium( + not_null view, + Element *replacing) { +} + +void InnerWidget::elementCancelPremium(not_null view) { } void InnerWidget::elementShowSpoilerAnimation() { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index f51184a0cf..1c0a7b42ef 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -139,6 +139,9 @@ public: void elementStartInteraction( not_null view) override; void elementStartPremium( + not_null view, + HistoryView::Element *replacing) override; + void elementCancelPremium( not_null view) override; void elementShowSpoilerAnimation() override; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 644c26a797..4d92ebf9a5 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -254,11 +254,20 @@ public: _widget->elementStartInteraction(view); } } - void elementStartPremium(not_null view) override { + void elementStartPremium( + not_null view, + Element *replacing) override { if (_widget) { - _widget->elementStartPremium(view); + _widget->elementStartPremium(view, replacing); } } + + void elementCancelPremium(not_null view) override { + if (_widget) { + _widget->elementCancelPremium(view); + } + } + void elementShowSpoilerAnimation() override { if (_widget) { _widget->elementShowSpoilerAnimation(); @@ -3175,11 +3184,17 @@ void HistoryInner::elementStartInteraction(not_null view) { _controller->emojiInteractions().startOutgoing(view); } -void HistoryInner::elementStartPremium(not_null view) { - _emojiInteractions->playPremiumEffect(view); +void HistoryInner::elementStartPremium( + not_null view, + Element *replacing) { + _emojiInteractions->playPremiumEffect(view, replacing); _animatedStickersPlayed.emplace(view->data()); } +void HistoryInner::elementCancelPremium(not_null view) { + _emojiInteractions->cancelPremiumEffect(view); +} + void HistoryInner::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 503bc15663..0fcef0e309 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -145,7 +145,10 @@ public: not_null elementPathShiftGradient(); void elementReplyTo(const FullMsgId &to); void elementStartInteraction(not_null view); - void elementStartPremium(not_null view); + void elementStartPremium( + not_null view, + Element *replacing); + void elementCancelPremium(not_null view); void elementShowSpoilerAnimation(); void updateBotInfo(bool recount = true); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 64e7c2f864..f191cba36d 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -206,6 +206,11 @@ void SimpleElementDelegate::elementStartInteraction( } void SimpleElementDelegate::elementStartPremium( + not_null view, + Element *replacing) { +} + +void SimpleElementDelegate::elementCancelPremium( not_null view) { } @@ -407,6 +412,19 @@ void Element::setY(int y) { void Element::refreshDataIdHook() { } +void Element::externalLottieProgressing(bool external) const { + if (const auto media = _media.get()) { + media->externalLottieProgressing(external); + } +} + +bool Element::externalLottieTill(int frame) const { + if (const auto media = _media.get()) { + return media->externalLottieTill(frame); + } + return true; +} + void Element::repaint() const { history()->owner().requestViewRepaint(this); } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 7b158446d4..3571b405b9 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -104,7 +104,10 @@ public: virtual not_null elementPathShiftGradient() = 0; virtual void elementReplyTo(const FullMsgId &to) = 0; virtual void elementStartInteraction(not_null view) = 0; - virtual void elementStartPremium(not_null view) = 0; + virtual void elementStartPremium( + not_null view, + Element *replacing) = 0; + virtual void elementCancelPremium(not_null view) = 0; virtual void elementShowSpoilerAnimation() = 0; virtual ~ElementDelegate() { @@ -163,7 +166,10 @@ public: not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; - void elementStartPremium(not_null view) override; + void elementStartPremium( + not_null view, + Element *replacing) override; + void elementCancelPremium(not_null view) override; void elementShowSpoilerAnimation() override; protected: @@ -265,6 +271,9 @@ public: Context context() const; void refreshDataId(); + void externalLottieProgressing(bool external) const; + bool externalLottieTill(int frame) const; + QDateTime dateTime() const; int y() const; diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp index 70cc94fac4..4e0b201aef 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp @@ -63,7 +63,13 @@ EmojiInteractions::EmojiInteractions( _premiumSize = Sticker::Size(); } -EmojiInteractions::~EmojiInteractions() = default; +EmojiInteractions::~EmojiInteractions() { + for (const auto &play : _plays) { + if (play.premium) { + play.view->externalLottieProgressing(false); + } + } +} void EmojiInteractions::play( ChatHelpers::EmojiInteractionPlayRequest request, @@ -90,7 +96,22 @@ void EmojiInteractions::play( } } -void EmojiInteractions::playPremiumEffect(not_null view) { +void EmojiInteractions::playPremiumEffect( + not_null view, + Element *replacing) { + if (replacing) { + const auto i = ranges::find(_plays, replacing, &Play::view); + if (i != end(_plays)) { + if (i->premium) { + replacing->externalLottieProgressing(false); + } + i->view = view; + if (i->premium) { + view->externalLottieProgressing(true); + } + return; + } + } if (const auto media = view->media()) { if (const auto document = media->getDocument()) { if (document->isPremiumSticker()) { @@ -107,6 +128,17 @@ void EmojiInteractions::playPremiumEffect(not_null view) { } } +void EmojiInteractions::cancelPremiumEffect(not_null view) { + _plays.erase(ranges::remove_if(_plays, [&](const Play &play) { + if (play.view != view) { + return false; + } else if (play.premium) { + play.view->externalLottieProgressing(false); + } + return true; + }), end(_plays)); +} + void EmojiInteractions::play( QString emoticon, not_null view, @@ -142,17 +174,26 @@ void EmojiInteractions::play( auto lottie = preparePlayer(document, data, filepath, premium); const auto shift = premium ? QPoint() : GenerateRandomShift(_emojiSize); + const auto raw = lottie.get(); lottie->updates( ) | rpl::start_with_next([=](Lottie::Update update) { v::match(update.data, [&](const Lottie::Information &information) { }, [&](const Lottie::DisplayFrameRequest &request) { - const auto rect = computeRect(view, premium).translated(shift); + const auto i = ranges::find(_plays, raw, [](const Play &p) { + return p.lottie.get(); + }); + const auto rect = computeRect( + i->view, + i->premium).translated(shift); if (rect.y() + rect.height() >= _visibleTop && rect.y() <= _visibleBottom) { _updateRequests.fire_copy(rect); } }); }, lottie->lifetime()); + if (premium) { + view->externalLottieProgressing(true); + } _plays.push_back({ .view = view, .lottie = std::move(lottie), @@ -285,9 +326,18 @@ void EmojiInteractions::paint(QPainter &p) { p.drawImage( QRect(rect.topLeft(), frame.image.size() / factor), frame.image); - play.lottie->markFrameShown(); + if (!play.premium || play.view->externalLottieTill(frame.index)) { + play.lottie->markFrameShown(); + } } - _plays.erase(ranges::remove(_plays, true, &Play::finished), end(_plays)); + _plays.erase(ranges::remove_if(_plays, [](const Play &play) { + if (!play.finished) { + return false; + } else if (play.premium) { + play.view->externalLottieProgressing(false); + } + return true; + }), end(_plays)); checkDelayed(); } diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h index 83414a9ae0..9af23d5e20 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h @@ -38,7 +38,10 @@ public: void play( ChatHelpers::EmojiInteractionPlayRequest request, not_null view); - void playPremiumEffect(not_null view); + void playPremiumEffect( + not_null view, + Element *replacing); + void cancelPremiumEffect(not_null view); void visibleAreaUpdated(int visibleTop, int visibleBottom); void paint(QPainter &p); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index d4de422232..1eee492dd0 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1519,7 +1519,12 @@ void ListWidget::elementReplyTo(const FullMsgId &to) { void ListWidget::elementStartInteraction(not_null view) { } -void ListWidget::elementStartPremium(not_null view) { +void ListWidget::elementStartPremium( + not_null view, + Element *replacing) { +} + +void ListWidget::elementCancelPremium(not_null view) { } void ListWidget::elementShowSpoilerAnimation() { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 6a697a9b86..930621c904 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -292,7 +292,11 @@ public: not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; - void elementStartPremium(not_null view) override; + void elementStartPremium( + not_null view, + Element *replacing) override; + void elementCancelPremium(not_null view) override; + void elementShowSpoilerAnimation() override; void setEmptyInfoWidget(base::unique_qptr &&w); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index f239ded35b..a5716ed9ff 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -173,6 +173,15 @@ public: virtual void checkAnimation() { } + virtual void externalLottieProgressing(bool external) { + } + virtual bool externalLottieTill(int frame) { + return true; + } + virtual int externalLottieTillFrame() const { + return -1; + } + [[nodiscard]] virtual QSize sizeForGroupingOptimal(int maxWidth) const { Unexpected("Grouping method call."); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp index 67e4a7e485..80a8f0d180 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.cpp @@ -460,6 +460,18 @@ std::unique_ptr UnwrappedMedia::stickerTakeLottie( return _content->stickerTakeLottie(data, replacements); } +void UnwrappedMedia::externalLottieProgressing(bool external) { + _content->externalLottieProgressing(external); +} + +bool UnwrappedMedia::externalLottieTill(int frame) { + return _content->externalLottieTill(frame); +} + +int UnwrappedMedia::externalLottieTillFrame() const { + return _content->externalLottieTillFrame(); +} + int UnwrappedMedia::calculateFullRight(const QRect &inner) const { const auto rightAligned = _parent->hasOutLayout() && !_parent->delegate()->elementIsChatWide(); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h index 447338196f..e01bb9f192 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_unwrapped.h @@ -40,6 +40,16 @@ public: virtual std::unique_ptr stickerTakeLottie( not_null data, const Lottie::ColorReplacements *replacements); + + virtual void externalLottieProgressing(bool external) { + } + virtual bool externalLottieTill(int frame) { + return true; + } + virtual int externalLottieTillFrame() const { + return -1; + } + virtual bool hasHeavyPart() const { return false; } @@ -92,6 +102,10 @@ public: not_null data, const Lottie::ColorReplacements *replacements) override; + void externalLottieProgressing(bool external) override; + bool externalLottieTill(int frame) override; + int externalLottieTillFrame() const override; + bool hasHeavyPart() const override { return _content->hasHeavyPart(); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index a959c2c567..a6c4c45b91 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -75,6 +75,12 @@ Sticker::Sticker( if (const auto media = replacing ? replacing->media() : nullptr) { _lottie = media->stickerTakeLottie(_data, _replacements); if (_lottie) { + _externalTillFrame = media->externalLottieTillFrame(); + if (_data->isPremiumSticker() + && !_premiumEffectPlayed) { + _premiumEffectPlayed = true; + _parent->delegate()->elementStartPremium(_parent, replacing); + } lottieCreated(); } } @@ -208,21 +214,24 @@ void Sticker::paintLottie( return; } - const auto paused = _parent->delegate()->elementIsGifPaused(); + const auto count = _lottie->information().framesCount; + _frameIndex = frame.index; + _framesCount = count; + const auto paused = (_externalTillFrame >= 0) + ? (_frameIndex >= _externalTillFrame) + : _parent->delegate()->elementIsGifPaused(); + _nextLastDiceFrame = !paused + && (_diceIndex > 0) + && (_frameIndex + 2 == count); const auto playOnce = (_diceIndex > 0) ? true : (_diceIndex == 0) ? false : (isEmojiSticker() || !Core::App().settings().loopAnimatedStickers()); - const auto count = _lottie->information().framesCount; - _frameIndex = frame.index; - _framesCount = count; - _nextLastDiceFrame = !paused - && (_diceIndex > 0) - && (_frameIndex + 2 == count); const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd(); - const auto switchToNext = !playOnce + const auto switchToNext = (_externalTillFrame >= 0) + || !playOnce || (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed)); if (!paused && switchToNext @@ -403,7 +412,7 @@ void Sticker::setupLottie() { if (_data->isPremiumSticker() && !_premiumEffectPlayed) { _premiumEffectPlayed = true; - _parent->delegate()->elementStartPremium(_parent); + _parent->delegate()->elementStartPremium(_parent, nullptr); } lottieCreated(); } @@ -417,6 +426,7 @@ void Sticker::lottieCreated() { ) | rpl::start_with_next([=](Lottie::Update update) { v::match(update.data, [&](const Lottie::Information &information) { _parent->history()->owner().requestViewResize(_parent); + markFramesTillExternal(); }, [&](const Lottie::DisplayFrameRequest &request) { _parent->history()->owner().requestViewRepaint(_parent); }); @@ -441,6 +451,9 @@ void Sticker::unloadLottie() { _lottieOncePlayed = false; } _lottie = nullptr; + if (_data->isPremiumSticker()) { + _parent->delegate()->elementCancelPremium(_parent); + } _parent->checkHeavyPart(); } @@ -452,4 +465,35 @@ std::unique_ptr Sticker::stickerTakeLottie( : nullptr; } +void Sticker::externalLottieProgressing(bool external) { + _externalTillFrame = !external + ? -1 + : (_externalTillFrame > 0) + ? _externalTillFrame + : 0; +} + +bool Sticker::externalLottieTill(int frame) { + _externalTillFrame = (_externalTillFrame >= 0) ? frame : -1; + return markFramesTillExternal(); +} + +int Sticker::externalLottieTillFrame() const { + return _externalTillFrame; +} + +bool Sticker::markFramesTillExternal() { + if (_externalTillFrame < 0 || !_lottie) { + return true; + } else if (!_lottie->ready()) { + return false; + } + while (_lottie->frameIndex() < _externalTillFrame) { + if (!_lottie->markFrameShown()) { + return false; + } + } + return true; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.h b/Telegram/SourceFiles/history/view/media/history_view_sticker.h index da28cb4af0..908357d7d3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.h +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.h @@ -51,6 +51,10 @@ public: not_null data, const Lottie::ColorReplacements *replacements) override; + void externalLottieProgressing(bool external) override; + bool externalLottieTill(int frame) override; + int externalLottieTillFrame() const override; + bool hasHeavyPart() const override; void unloadHeavyPart() override; @@ -92,6 +96,7 @@ private: void lottieCreated(); void unloadLottie(); void emojiStickerClicked(); + bool markFramesTillExternal(); const not_null _parent; const not_null _data; @@ -105,6 +110,7 @@ private: int _diceIndex = -1; mutable int _frameIndex = -1; mutable int _framesCount = -1; + int _externalTillFrame = -1; mutable bool _lottieOncePlayed = false; mutable bool _premiumEffectPlayed = false; mutable bool _nextLastDiceFrame = false; diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie index 478da45cf4..fb3bb0ef3d 160000 --- a/Telegram/lib_lottie +++ b/Telegram/lib_lottie @@ -1 +1 @@ -Subproject commit 478da45cf4a3fe8fc75c836de5371234adeada90 +Subproject commit fb3bb0ef3d46e61debbe3b3dd996bd5050275ba3