From c3596467026c3eaaa803a0f831527800dbefc567 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 25 Jan 2022 10:00:38 +0300 Subject: [PATCH] Animate webm pack icon in the panel. --- .../chat_helpers/stickers_list_widget.cpp | 185 +++++++++++++----- 1 file changed, 136 insertions(+), 49 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 306f7caeb9..04f55cd69f 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -124,7 +124,7 @@ public: uint64 setId, ValidateIconAnimations animations); void refreshIcons(ValidateIconAnimations animations); - bool hasOnlyFeaturedSets() const; + [[nodiscard]] bool hasOnlyFeaturedSets() const; void leaveToChildEvent(QEvent *e, QWidget *child) override; @@ -134,7 +134,7 @@ public: void clearHeavyData(); - rpl::producer<> openSettingsRequests() const; + [[nodiscard]] rpl::producer<> openSettingsRequests() const; protected: void paintEvent(QPaintEvent *e) override; @@ -153,8 +153,14 @@ private: Settings, }; using OverState = std::variant; + struct IconInfo { + int index = 0; + int left = 0; + bool visible = false; + }; - void enumerateVisibleIcons(Fn callback); + void enumerateVisibleIcons(Fn callback); + void enumerateIcons(Fn callback); bool iconsAnimationCallback(crl::time now); void setSelectedIcon( @@ -167,10 +173,15 @@ private: void refreshIconsGeometry(ValidateIconAnimations animations); void updateSelected(); void updateSetIcon(uint64 setId); + void updateSetIconAt(int left); void finishDragging(); void paintStickerSettingsIcon(Painter &p) const; void paintSearchIcon(Painter &p) const; - void paintSetIcon(Painter &p, const StickerIcon &icon, int x) const; + void paintSetIcon( + Painter &p, + const IconInfo &info, + crl::time now, + bool paused) const; void paintSelectionBar(Painter &p) const; void paintLeftRightFading(Painter &p) const; @@ -179,6 +190,8 @@ private: void resizeSearchControls(); void scrollByWheelEvent(not_null e); + void clipCallback(Media::Clip::Notification notification, uint64 setId); + const not_null _pan; const bool _searchButtonVisible = true; @@ -371,7 +384,7 @@ void StickersListWidget::Footer::returnFocus() { } void StickersListWidget::Footer::enumerateVisibleIcons( - Fn callback) { + Fn callback) { auto iconsX = qRound(_iconsX.current()); auto x = _iconsLeft - (iconsX % st::stickerIconWidth); auto first = floorclamp(iconsX, st::stickerIconWidth, 0, _icons.size()); @@ -381,13 +394,32 @@ void StickersListWidget::Footer::enumerateVisibleIcons( 0, _icons.size()); for (auto index = first; index != last; ++index) { - callback(_icons[index], x); + callback({ .index = index, .left = x, .visible = true }); + x += st::stickerIconWidth; + } +} + +void StickersListWidget::Footer::enumerateIcons( + Fn callback) { + auto iconsX = qRound(_iconsX.current()); + auto x = _iconsLeft - (iconsX % st::stickerIconWidth); + auto first = floorclamp(iconsX, st::stickerIconWidth, 0, _icons.size()); + auto last = ceilclamp( + iconsX + width(), + st::stickerIconWidth, + 0, + _icons.size()); + x -= first * st::stickerIconWidth; + for (auto i = 0, count = int(_icons.size()); i != count; ++i) { + const auto visible = (i >= first && i < last); + callback({ .index = i, .left = x, .visible = visible }); x += st::stickerIconWidth; } } void StickersListWidget::Footer::preloadImages() { - enumerateVisibleIcons([](const StickerIcon &icon, int x) { + enumerateVisibleIcons([&](const IconInfo &info) { + const auto &icon = _icons[info.index]; if (const auto sticker = icon.sticker) { Assert(icon.set != nullptr); if (icon.set->hasThumbnail()) { @@ -496,8 +528,11 @@ void StickersListWidget::Footer::paintEvent(QPaintEvent *e) { } p.setClipRect(clip); - enumerateVisibleIcons([&](const StickerIcon &icon, int x) { - paintSetIcon(p, icon, x); + const auto now = crl::now(); + const auto paused = _pan->controller()->isGifPausedAtLeastFor( + Window::GifPauseReason::SavedGifs); + enumerateVisibleIcons([&](const IconInfo &info) { + paintSetIcon(p, info, now, paused); }); paintSelectionBar(p); @@ -690,6 +725,36 @@ void StickersListWidget::Footer::scrollByWheelEvent( } } +void StickersListWidget::Footer::clipCallback( + Media::Clip::Notification notification, + uint64 setId) { + using namespace Media::Clip; + switch (notification) { + case Notification::Reinit: { + enumerateIcons([&](const IconInfo &info) { + auto &icon = _icons[info.index]; + if (icon.setId != setId || !icon.webm) { + return; + } else if (icon.webm->state() == State::Error) { + icon.webm.setBad(); + } else if (!info.visible) { + icon.webm = nullptr; + } else if (icon.webm->ready() && !icon.webm->started()) { + icon.webm->start({ + .frame = { icon.pixw, icon.pixh }, + .keepAlpha = true, + }); + } + updateSetIconAt(info.left); + }); + } break; + + case Notification::Repaint: + updateSetIcon(setId); + break; + } +} + void StickersListWidget::Footer::updateSelected() { if (_iconDown != SpecialOver::None) { return; @@ -848,10 +913,13 @@ void StickersListWidget::Footer::validateIconWebmAnimation( return; } const auto id = icon.setId; + auto callback = [=](Media::Clip::Notification notification) { + clipCallback(notification, id); + }; icon.webm = WebmThumbnail( icon.thumbnailMedia.get(), icon.stickerMedia.get(), - [=](Media::Clip::Notification) { updateSetIcon(id); }); + std::move(callback)); } void StickersListWidget::Footer::validateIconAnimation( @@ -861,18 +929,24 @@ void StickersListWidget::Footer::validateIconAnimation( } void StickersListWidget::Footer::updateSetIcon(uint64 setId) { - enumerateVisibleIcons([&](const StickerIcon &icon, int x) { - if (icon.setId != setId) { + enumerateVisibleIcons([&](const IconInfo &info) { + if (_icons[info.index].setId != setId) { return; } - update(x, _iconsTop, st::stickerIconWidth, st::emojiFooterHeight); + updateSetIconAt(info.left); }); } +void StickersListWidget::Footer::updateSetIconAt(int left) { + update(left, _iconsTop, st::stickerIconWidth, st::emojiFooterHeight); +} + void StickersListWidget::Footer::paintSetIcon( Painter &p, - const StickerIcon &icon, - int x) const { + const IconInfo &info, + crl::time now, + bool paused) const { + const auto &icon = _icons[info.index]; if (icon.sticker) { icon.ensureMediaCreated(); const_cast(this)->validateIconAnimation(icon); @@ -882,8 +956,35 @@ void StickersListWidget::Footer::paintSetIcon( : icon.stickerMedia ? icon.stickerMedia->thumbnail() : nullptr; - if (!icon.lottie - || (!icon.lottie->ready() && !icon.savedFrame.isNull())) { + const auto x = info.left + (st::stickerIconWidth - icon.pixw) / 2; + const auto y = _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2; + if (icon.lottie && icon.lottie->ready()) { + const auto frame = icon.lottie->frame(); + const auto size = frame.size() / cIntRetinaFactor(); + if (icon.savedFrame.isNull()) { + icon.savedFrame = QPixmap::fromImage(frame, Qt::ColorOnly); + icon.savedFrame.setDevicePixelRatio(cRetinaFactor()); + } + p.drawImage( + QRect( + info.left + (st::stickerIconWidth - size.width()) / 2, + _iconsTop + (st::emojiFooterHeight - size.height()) / 2, + size.width(), + size.height()), + frame); + if (!paused) { + icon.lottie->markFrameShown(); + } + } else if (icon.webm && icon.webm->started()) { + const auto frame = icon.webm->current( + { .frame = { icon.pixw, icon.pixh }, .keepAlpha = true }, + paused ? 0 : now); + if (icon.savedFrame.isNull()) { + icon.savedFrame = frame; + icon.savedFrame.setDevicePixelRatio(cRetinaFactor()); + } + p.drawPixmapLeft(x, y, width(), frame); + } else if (!icon.savedFrame.isNull() || thumb) { const auto pixmap = !icon.savedFrame.isNull() ? icon.savedFrame : (!icon.lottie && thumb) @@ -894,33 +995,17 @@ void StickersListWidget::Footer::paintSetIcon( } else if (icon.savedFrame.isNull()) { icon.savedFrame = pixmap; } - p.drawPixmapLeft( - x + (st::stickerIconWidth - icon.pixw) / 2, - _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2, - width(), - pixmap); - } else if (icon.lottie->ready()) { - const auto frame = icon.lottie->frame(); - const auto size = frame.size() / cIntRetinaFactor(); - if (icon.savedFrame.isNull()) { - icon.savedFrame = QPixmap::fromImage(frame, Qt::ColorOnly); - icon.savedFrame.setDevicePixelRatio(cRetinaFactor()); - } - p.drawImage( - QRect( - x + (st::stickerIconWidth - size.width()) / 2, - _iconsTop + (st::emojiFooterHeight - size.height()) / 2, - size.width(), - size.height()), - frame); - const auto paused = _pan->controller()->isGifPausedAtLeastFor( - Window::GifPauseReason::SavedGifs); - if (!paused) { - icon.lottie->markFrameShown(); - } + p.drawPixmapLeft(x, y, width(), pixmap); } } else if (icon.megagroup) { - icon.megagroup->paintUserpicLeft(p, icon.megagroupUserpic, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); + const auto size = st::stickerGroupCategorySize; + icon.megagroup->paintUserpicLeft( + p, + icon.megagroupUserpic, + info.left + (st::stickerIconWidth - size) / 2, + _iconsTop + (st::emojiFooterHeight - size) / 2, + width(), + st::stickerGroupCategorySize); } else { const auto paintedIcon = [&] { if (icon.setId == Data::Stickers::FeaturedSetId) { @@ -935,7 +1020,7 @@ void StickersListWidget::Footer::paintSetIcon( }(); paintedIcon->paint( p, - x + (st::stickerIconWidth - paintedIcon->width()) / 2, + info.left + (st::stickerIconWidth - paintedIcon->width()) / 2, _iconsTop + (st::emojiFooterHeight - paintedIcon->height()) / 2, width()); } @@ -2158,12 +2243,14 @@ void StickersListWidget::paintSticker( } set.lottiePlayer->unpause(sticker.lottie); } else if (sticker.webm && sticker.webm->started()) { - p.drawPixmapLeft( - ppos, - width(), - sticker.webm->current( - { .frame = size, .keepAlpha = true }, - paused ? 0 : now)); + const auto frame = sticker.webm->current( + { .frame = size, .keepAlpha = true }, + paused ? 0 : now); + if (sticker.savedFrame.isNull()) { + sticker.savedFrame = frame; + sticker.savedFrame.setDevicePixelRatio(cRetinaFactor()); + } + p.drawPixmapLeft(ppos, width(), frame); } else { const auto image = media->getStickerSmall(); const auto pixmap = !sticker.savedFrame.isNull()