Sync premium sticker effect with sticker frame index.

This commit is contained in:
John Preston 2022-04-26 20:31:10 +04:00
parent 323cb78f22
commit a079139c3b
16 changed files with 226 additions and 26 deletions

View file

@ -673,7 +673,12 @@ void InnerWidget::elementReplyTo(const FullMsgId &to) {
void InnerWidget::elementStartInteraction(not_null<const Element*> view) {
}
void InnerWidget::elementStartPremium(not_null<const Element*> view) {
void InnerWidget::elementStartPremium(
not_null<const Element*> view,
Element *replacing) {
}
void InnerWidget::elementCancelPremium(not_null<const Element*> view) {
}
void InnerWidget::elementShowSpoilerAnimation() {

View file

@ -139,6 +139,9 @@ public:
void elementStartInteraction(
not_null<const HistoryView::Element*> view) override;
void elementStartPremium(
not_null<const HistoryView::Element*> view,
HistoryView::Element *replacing) override;
void elementCancelPremium(
not_null<const HistoryView::Element*> view) override;
void elementShowSpoilerAnimation() override;

View file

@ -254,11 +254,20 @@ public:
_widget->elementStartInteraction(view);
}
}
void elementStartPremium(not_null<const Element*> view) override {
void elementStartPremium(
not_null<const Element*> view,
Element *replacing) override {
if (_widget) {
_widget->elementStartPremium(view);
_widget->elementStartPremium(view, replacing);
}
}
void elementCancelPremium(not_null<const Element*> view) override {
if (_widget) {
_widget->elementCancelPremium(view);
}
}
void elementShowSpoilerAnimation() override {
if (_widget) {
_widget->elementShowSpoilerAnimation();
@ -3175,11 +3184,17 @@ void HistoryInner::elementStartInteraction(not_null<const Element*> view) {
_controller->emojiInteractions().startOutgoing(view);
}
void HistoryInner::elementStartPremium(not_null<const Element*> view) {
_emojiInteractions->playPremiumEffect(view);
void HistoryInner::elementStartPremium(
not_null<const Element*> view,
Element *replacing) {
_emojiInteractions->playPremiumEffect(view, replacing);
_animatedStickersPlayed.emplace(view->data());
}
void HistoryInner::elementCancelPremium(not_null<const Element*> view) {
_emojiInteractions->cancelPremiumEffect(view);
}
void HistoryInner::elementShowSpoilerAnimation() {
_spoilerOpacity.stop();
_spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration);

View file

@ -145,7 +145,10 @@ public:
not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
void elementReplyTo(const FullMsgId &to);
void elementStartInteraction(not_null<const Element*> view);
void elementStartPremium(not_null<const Element*> view);
void elementStartPremium(
not_null<const Element*> view,
Element *replacing);
void elementCancelPremium(not_null<const Element*> view);
void elementShowSpoilerAnimation();
void updateBotInfo(bool recount = true);

View file

@ -206,6 +206,11 @@ void SimpleElementDelegate::elementStartInteraction(
}
void SimpleElementDelegate::elementStartPremium(
not_null<const Element*> view,
Element *replacing) {
}
void SimpleElementDelegate::elementCancelPremium(
not_null<const Element*> 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);
}

View file

@ -104,7 +104,10 @@ public:
virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0;
virtual void elementReplyTo(const FullMsgId &to) = 0;
virtual void elementStartInteraction(not_null<const Element*> view) = 0;
virtual void elementStartPremium(not_null<const Element*> view) = 0;
virtual void elementStartPremium(
not_null<const Element*> view,
Element *replacing) = 0;
virtual void elementCancelPremium(not_null<const Element*> view) = 0;
virtual void elementShowSpoilerAnimation() = 0;
virtual ~ElementDelegate() {
@ -163,7 +166,10 @@ public:
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override;
void elementStartInteraction(not_null<const Element*> view) override;
void elementStartPremium(not_null<const Element*> view) override;
void elementStartPremium(
not_null<const Element*> view,
Element *replacing) override;
void elementCancelPremium(not_null<const Element*> 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;

View file

@ -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<const Element*> view) {
void EmojiInteractions::playPremiumEffect(
not_null<const Element*> 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<const Element*> view) {
}
}
void EmojiInteractions::cancelPremiumEffect(not_null<const Element*> 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<const Element*> 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);
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();
}

View file

@ -38,7 +38,10 @@ public:
void play(
ChatHelpers::EmojiInteractionPlayRequest request,
not_null<Element*> view);
void playPremiumEffect(not_null<const Element*> view);
void playPremiumEffect(
not_null<const Element*> view,
Element *replacing);
void cancelPremiumEffect(not_null<const Element*> view);
void visibleAreaUpdated(int visibleTop, int visibleBottom);
void paint(QPainter &p);

View file

@ -1519,7 +1519,12 @@ void ListWidget::elementReplyTo(const FullMsgId &to) {
void ListWidget::elementStartInteraction(not_null<const Element*> view) {
}
void ListWidget::elementStartPremium(not_null<const Element*> view) {
void ListWidget::elementStartPremium(
not_null<const Element*> view,
Element *replacing) {
}
void ListWidget::elementCancelPremium(not_null<const Element*> view) {
}
void ListWidget::elementShowSpoilerAnimation() {

View file

@ -292,7 +292,11 @@ public:
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override;
void elementStartInteraction(not_null<const Element*> view) override;
void elementStartPremium(not_null<const Element*> view) override;
void elementStartPremium(
not_null<const Element*> view,
Element *replacing) override;
void elementCancelPremium(not_null<const Element*> view) override;
void elementShowSpoilerAnimation() override;
void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);

View file

@ -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.");
}

View file

@ -460,6 +460,18 @@ std::unique_ptr<Lottie::SinglePlayer> 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();

View file

@ -40,6 +40,16 @@ public:
virtual std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie(
not_null<DocumentData*> 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<DocumentData*> 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();
}

View file

@ -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<Lottie::SinglePlayer> 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

View file

@ -51,6 +51,10 @@ public:
not_null<DocumentData*> 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<Element*> _parent;
const not_null<DocumentData*> _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;

@ -1 +1 @@
Subproject commit 478da45cf4a3fe8fc75c836de5371234adeada90
Subproject commit fb3bb0ef3d46e61debbe3b3dd996bd5050275ba3