diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 36a7cdd31..67254c64b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -1012,6 +1012,13 @@ void StickersListWidget::refreshSearchRows( const std::vector *cloudSets) { clearSelection(); + const auto wasSection = _section; + const auto guard = gsl::finally([&] { + if (_section == wasSection && _section == Section::Search) { + refillLottieData(); + } + }); + _searchSets.clear(); fillLocalSearchRows(_searchNextQuery); @@ -1022,9 +1029,7 @@ void StickersListWidget::refreshSearchRows( return; } - if (_section != Section::Search) { - _section = Section::Search; - } + setSection(Section::Search); if (cloudSets) { fillCloudSearchRows(*cloudSets); } @@ -1345,7 +1350,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { } void StickersListWidget::markLottieFrameShown(Set &set) { - if (const auto player = set.lottiePlayer.get()) { + if (const auto player = set.lottiePlayer) { const auto paused = controller()->isGifPausedAtLeastFor( Window::GifPauseReason::SavedGifs); if (!paused) { @@ -1384,11 +1389,12 @@ void StickersListWidget::destroyLottieIn(Set &set) { for (auto &sticker : set.stickers) { sticker.animated = nullptr; } + _lottieData.remove(set.id); } void StickersListWidget::pauseInvisibleLottieIn(const SectionInfo &info) { auto &set = shownSets()[info.section]; - const auto player = set.lottiePlayer.get(); + const auto player = set.lottiePlayer; if (!player) { return; } @@ -1474,15 +1480,19 @@ void StickersListWidget::ensureLottiePlayer(Set &set) { if (set.lottiePlayer) { return; } - set.lottiePlayer = std::make_unique( - getLottieRenderer()); - const auto raw = set.lottiePlayer.get(); - set.lottiePlayer->updates( + const auto [i, ok] = _lottieData.emplace( + set.id, + LottieSet{ std::make_unique( + getLottieRenderer()) }); + Assert(ok); + const auto raw = set.lottiePlayer = i->second.player.get(); + + raw->updates( ) | rpl::start_with_next([=] { const auto &sets = shownSets(); PROFILE_LOG(("WIDGET REPAINT REQUESTED")); enumerateSections([&](const SectionInfo &info) { - if (shownSets()[info.section].lottiePlayer.get() == raw) { + if (shownSets()[info.section].lottiePlayer == raw) { update( 0, info.rowsTop, @@ -1492,7 +1502,7 @@ void StickersListWidget::ensureLottiePlayer(Set &set) { } return true; }); - }, lifetime()); + },i->second.lifetime); } void StickersListWidget::setupLottie(Set &set, int section, int index) { @@ -1501,10 +1511,13 @@ void StickersListWidget::setupLottie(Set &set, int section, int index) { ensureLottiePlayer(set); sticker.animated = Stickers::LottieAnimationFromDocument( - set.lottiePlayer.get(), + set.lottiePlayer, document, Stickers::LottieSize::StickersPanel, boundingBoxSize() * cIntRetinaFactor()); + _lottieData[set.id].items.emplace( + document->id, + LottieSet::Item{ sticker.animated }); } QSize StickersListWidget::boundingBoxSize() const { @@ -1789,7 +1802,7 @@ void StickersListWidget::removeRecentSticker(int section, int index) { clearSelection(); bool refresh = false; - auto &sticker = _mySets[section].stickers[index]; + const auto &sticker = _mySets[section].stickers[index]; const auto document = sticker.document; auto &recent = Stickers::GetRecentPack(); for (int32 i = 0, l = recent.size(); i < l; ++i) { @@ -1830,7 +1843,7 @@ void StickersListWidget::removeFavedSticker(int section, int index) { } clearSelection(); - auto &sticker = _mySets[section].stickers[index]; + const auto &sticker = _mySets[section].stickers[index]; const auto document = sticker.document; Stickers::SetFaved(document, false); Auth().api().toggleFavedSticker( @@ -1887,44 +1900,42 @@ TabbedSelector::InnerFooter *StickersListWidget::getFooter() const { void StickersListWidget::processHideFinished() { clearSelection(); + clearLottieData(); } void StickersListWidget::processPanelHideFinished() { clearInstalledLocally(); - + clearLottieData(); // Preserve panel state through visibility toggles. //// Reset to the recent stickers section. //if (_section == Section::Featured && (!_footer || !_footer->hasOnlyFeaturedSets())) { - // _section = Section::Stickers; + // setSection(Section::Stickers); // validateSelectedIcon(ValidateIconAnimations::None); //} } +void StickersListWidget::setSection(Section section) { + if (_section == section) { + return; + } + clearLottieData(); + _section = section; +} + +void StickersListWidget::clearLottieData() { + for (auto &set : shownSets()) { + destroyLottieIn(set); + } + _lottieData.clear(); +} + void StickersListWidget::refreshStickers() { clearSelection(); - _mySets.clear(); - _favedStickersMap.clear(); - _mySets.reserve(Auth().data().stickerSetsOrder().size() + 3); - - refreshFavedStickers(); - refreshRecentStickers(false); - refreshMegagroupStickers(GroupStickersPlace::Visible); - for (const auto setId : Auth().data().stickerSetsOrder()) { - const auto externalLayout = false; - appendSet(_mySets, setId, externalLayout, AppendSkip::Archived); - } - refreshMegagroupStickers(GroupStickersPlace::Hidden); - - _featuredSets.clear(); - _featuredSets.reserve(Auth().data().featuredStickerSetsOrder().size()); - - for (const auto setId : Auth().data().featuredStickerSetsOrder()) { - const auto externalLayout = true; - appendSet(_featuredSets, setId, externalLayout, AppendSkip::Installed); - } - + refreshMySets(); + refreshFeaturedSets(); refreshSearchSets(); + refillLottieData(); resizeToWidth(width()); @@ -1938,6 +1949,31 @@ void StickersListWidget::refreshStickers() { update(); } +void StickersListWidget::refreshMySets() { + _mySets.clear(); + _favedStickersMap.clear(); + _mySets.reserve(Auth().data().stickerSetsOrder().size() + 3); + + refreshFavedStickers(); + refreshRecentStickers(false); + refreshMegagroupStickers(GroupStickersPlace::Visible); + for (const auto setId : Auth().data().stickerSetsOrder()) { + const auto externalLayout = false; + appendSet(_mySets, setId, externalLayout, AppendSkip::Archived); + } + refreshMegagroupStickers(GroupStickersPlace::Hidden); +} + +void StickersListWidget::refreshFeaturedSets() { + _featuredSets.clear(); + _featuredSets.reserve(Auth().data().featuredStickerSetsOrder().size()); + + for (const auto setId : Auth().data().featuredStickerSetsOrder()) { + const auto externalLayout = true; + appendSet(_featuredSets, setId, externalLayout, AppendSkip::Installed); + } +} + void StickersListWidget::refreshSearchSets() { refreshSearchIndex(); @@ -1946,7 +1982,7 @@ void StickersListWidget::refreshSearchSets() { if (const auto it = sets.find(set.id); it != sets.end()) { set.flags = it->flags; if (!it->stickers.empty()) { - // #TODO stickers preserve lottie etc. + set.lottiePlayer = nullptr; set.stickers = PrepareStickers(it->stickers); } if (!SetInMyList(set.flags)) { @@ -1969,6 +2005,53 @@ void StickersListWidget::refreshSearchIndex() { } } +void StickersListWidget::refillLottieData() { + for (auto &set : _lottieData) { + set.second.stale = true; + } + for (auto &set : shownSets()) { + refillLottieData(set); + } + for (auto i = begin(_lottieData); i != end(_lottieData);) { + if (i->second.stale) { + i = _lottieData.erase(i); + } else { + ++i; + } + } +} + +void StickersListWidget::refillLottieData(Set &set) { + const auto i = _lottieData.find(set.id); + if (i == end(_lottieData)) { + return; + } + i->second.stale = true; + auto &items = i->second.items; + for (auto &item : items) { + item.second.stale = true; + } + for (auto &sticker : set.stickers) { + const auto j = items.find(sticker.document->id); + if (j != end(items)) { + sticker.animated = j->second.animation; + i->second.stale = j->second.stale = false; + } + } + if (i->second.stale) { + _lottieData.erase(i); + return; + } + for (auto j = begin(items); j != end(items);) { + if (j->second.stale) { + j = items.erase(j); + } else { + ++j; + } + } + set.lottiePlayer = i->second.player.get(); +} + void StickersListWidget::refreshSettingsVisibility() { const auto visible = (_section == Section::Stickers) && _mySets.empty(); _settings->setVisible(visible); @@ -2128,9 +2211,12 @@ void StickersListWidget::refreshRecentStickers(bool performResize) { recentPack.size(), std::move(recentPack)); } else { + recentIt->lottiePlayer = nullptr; recentIt->stickers = std::move(recentPack); + refillLottieData(*recentIt); } } else if (recentIt != _mySets.end()) { + _lottieData.remove(recentIt->id); _mySets.erase(recentIt); } @@ -2449,8 +2535,7 @@ void StickersListWidget::showStickerSet(uint64 setId) { if (setId == Stickers::FeaturedSetId) { if (_section != Section::Featured) { - _section = Section::Featured; - + setSection(Section::Featured); refreshRecentStickers(true); refreshSettingsVisibility(); if (_footer) { @@ -2466,7 +2551,7 @@ void StickersListWidget::showStickerSet(uint64 setId) { auto needRefresh = (_section != Section::Stickers); if (needRefresh) { - _section = Section::Stickers; + setSection(Section::Stickers); refreshRecentStickers(true); refreshSettingsVisibility(); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 6d1060618..a54674376 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -167,10 +167,20 @@ private: ImagePtr thumbnail; std::vector stickers; std::unique_ptr ripple; - std::unique_ptr lottiePlayer; + Lottie::MultiPlayer *lottiePlayer = nullptr; bool externalLayout = false; int count = 0; }; + struct LottieSet { + struct Item { + not_null animation; + bool stale = false; + }; + std::unique_ptr player; + base::flat_map items; + bool stale = false; + rpl::lifetime lifetime; + }; static std::vector PrepareStickers(const Stickers::Pack &pack); @@ -181,6 +191,7 @@ private: SectionInfo sectionInfo(int section) const; SectionInfo sectionInfoByOffset(int yOffset) const; + void setSection(Section section); void displaySet(uint64 setId); void installSet(uint64 setId); void removeMegagroupSet(bool locally); @@ -188,6 +199,8 @@ private: void sendInstallRequest( uint64 setId, const MTPInputStickerSet &input); + void refreshMySets(); + void refreshFeaturedSets(); void refreshSearchSets(); void refreshSearchIndex(); @@ -233,6 +246,9 @@ private: void checkVisibleLottie(); void pauseInvisibleLottieIn(const SectionInfo &info); void destroyLottieIn(Set &set); + void refillLottieData(); + void refillLottieData(Set &set); + void clearLottieData(); int stickersRight() const; bool featuredHasAddButton(int index) const; @@ -320,6 +336,8 @@ private: QString _searchQuery, _searchNextQuery; mtpRequestId _searchRequestId = 0; + base::flat_map _lottieData; + rpl::event_stream> _chosen; rpl::event_stream<> _scrollUpdated; rpl::event_stream<> _checkForHide;