Show animated thumbnails in sets box.

This commit is contained in:
John Preston 2019-07-02 15:20:04 +02:00
parent 3b645422ff
commit 76630528f7
8 changed files with 168 additions and 60 deletions

View File

@ -559,14 +559,12 @@ void StickerSetBox::Inner::paintSticker(
if (h < 1) h = 1;
QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (element.animated && element.animated->ready()) {
auto request = Lottie::FrameRequest();
request.box = boundingBoxSize() * cIntRetinaFactor();
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
if (!paused) {
element.animated->markFrameShown();
}
const auto frame = element.animated->frame(request);
const auto frame = element.animated->frame();
p.drawImage(
QRect(ppos, frame.size() / cIntRetinaFactor()),
frame);

View File

@ -20,8 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "storage/localstorage.h"
#include "dialogs/dialogs_layout.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
#include "lottie/lottie_single_player.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/scroll_area.h"
@ -31,7 +30,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/input_fields.h"
#include "ui/image/image.h"
#include "window/window_session_controller.h"
#include "auth_session.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
namespace {
@ -655,7 +657,8 @@ StickersBox::Inner::Row::Row(
StickersBox::Inner::Row::~Row() = default;
StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidget(parent)
StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section)
: RpWidget(parent)
, _section(section)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) {
@ -669,7 +672,8 @@ StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidg
setup();
}
StickersBox::Inner::Inner(QWidget *parent, not_null<ChannelData*> megagroup) : TWidget(parent)
StickersBox::Inner::Inner(QWidget *parent, not_null<ChannelData*> megagroup)
: RpWidget(parent)
, _section(StickersBox::Section::Installed)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) {
@ -794,7 +798,7 @@ QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const {
return QRect(buttonx, buttony, buttonw, buttonh);
}
void StickersBox::Inner::paintRow(Painter &p, Row *set, int index) {
void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
auto xadd = 0, yadd = qRound(set->yadd.current());
if (xadd || yadd) p.translate(xadd, yadd);
@ -854,17 +858,7 @@ void StickersBox::Inner::paintRow(Painter &p, Row *set, int index) {
}
if (set->sticker) {
const auto origin = Data::FileOriginStickerSet(
set->id,
set->accessHash);
const auto thumb = set->thumbnail
? set->thumbnail.get()
: set->sticker->thumbnail();
if (thumb) {
thumb->load(origin);
auto pix = thumb->pix(origin, set->pixw, set->pixh);
p.drawPixmapLeft(stickerx + (st::contactsPhotoSize - set->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - set->pixh) / 2, width(), pix);
}
paintRowThumbnail(p, set, stickerx);
}
int namex = stickerx + st::contactsPhotoSize + st::contactsPadding.left();
@ -897,7 +891,97 @@ void StickersBox::Inner::paintRow(Painter &p, Row *set, int index) {
if (xadd || yadd) p.translate(-xadd, -yadd);
}
void StickersBox::Inner::paintFakeButton(Painter &p, Row *set, int index) {
void StickersBox::Inner::paintRowThumbnail(
Painter &p,
not_null<Row*> set,
int left) {
const auto origin = Data::FileOriginStickerSet(
set->id,
set->accessHash);
const auto thumb = set->thumbnail
? set->thumbnail.get()
: set->sticker->thumbnail();
if (!thumb) {
return;
}
thumb->load(origin);
validateLottieAnimation(set);
if (!set->lottie) {
if (!thumb->loaded()) {
return;
}
p.drawPixmapLeft(
left + (st::contactsPhotoSize - set->pixw) / 2,
st::contactsPadding.top() + (st::contactsPhotoSize - set->pixh) / 2,
width(),
thumb->pix(origin, set->pixw, set->pixh));
} else if (set->lottie->ready()) {
const auto frame = set->lottie->frame();
const auto size = frame.size() / cIntRetinaFactor();
p.drawImage(
QRect(
left + (st::contactsPhotoSize - size.width()) / 2,
st::contactsPadding.top() + (st::contactsPhotoSize - size.height()) / 2,
size.width(),
size.height()),
frame);
const auto controller = App::wnd()->sessionController();
const auto paused = controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
if (!paused) {
set->lottie->markFrameShown();
}
}
}
void StickersBox::Inner::validateLottieAnimation(not_null<Row*> set) {
if (set->lottie
|| !Stickers::HasLottieThumbnail(set->thumbnail, set->sticker)) {
return;
}
auto player = Stickers::LottieThumbnail(
set->thumbnail,
set->sticker,
Stickers::LottieSize::SetsListThumbnail,
QSize(
st::contactsPhotoSize,
st::contactsPhotoSize) * cIntRetinaFactor());
if (!player) {
return;
}
set->lottie = std::move(player);
set->lottie->updates(
) | rpl::start_with_next([=] {
updateRowThumbnail(set);
}, lifetime());
}
void StickersBox::Inner::updateRowThumbnail(not_null<Row*> set) {
const auto rowTop = [&] {
if (set == _megagroupSelectedSet.get()) {
return _megagroupDivider->y() - _rowHeight;
}
auto top = _itemsTop;
for (const auto &row : _rows) {
if (row.get() == set) {
return top + qRound(row->yadd.current());
}
top += _rowHeight;
}
Unexpected("StickersBox::Inner::updateRowThumbnail: row not found");
}();
const auto left = st::contactsPadding.left()
+ ((!_megagroupSet && _section == Section::Installed)
? st::stickersReorderIcon.width() + st::stickersReorderSkip
: 0);
update(
left,
rowTop + st::contactsPadding.top(),
st::contactsPhotoSize,
st::contactsPhotoSize);
}
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> set, int index) {
auto removeButton = (_section == Section::Installed && !set->removed);
auto rect = relativeButtonRect(removeButton);
if (_section != Section::Installed && set->installed && !set->archived && !set->removed) {
@ -1535,36 +1619,41 @@ void StickersBox::Inner::updateSize(int newWidth) {
void StickersBox::Inner::updateRows() {
int maxNameWidth = countMaxNameWidth();
auto &sets = Auth().data().stickerSets();
for_const (auto &row, _rows) {
auto it = sets.constFind(row->id);
if (it != sets.cend()) {
auto &set = it.value();
if (!row->sticker) {
auto thumbnail = ImagePtr();
auto sticker = (DocumentData*)nullptr;
auto pixw = 0, pixh = 0;
fillSetCover(set, &thumbnail, &sticker, &pixw, &pixh);
if (sticker) {
row->thumbnail = thumbnail;
row->sticker = sticker;
row->pixw = pixw;
row->pixh = pixh;
}
}
if (!row->isRecentSet()) {
auto wasInstalled = row->installed;
auto wasArchived = row->archived;
fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived);
if (_section == Section::Installed) {
row->archived = false;
}
if (row->installed != wasInstalled || row->archived != wasArchived) {
row->ripple.reset();
}
}
row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth);
row->count = fillSetCount(set);
for (const auto &row : _rows) {
const auto it = sets.constFind(row->id);
if (it == sets.cend()) {
continue;
}
const auto &set = it.value();
if (!row->sticker) {
auto thumbnail = ImagePtr();
auto sticker = (DocumentData*)nullptr;
auto pixw = 0, pixh = 0;
fillSetCover(set, &thumbnail, &sticker, &pixw, &pixh);
if (sticker) {
if ((row->thumbnail.get() != thumbnail.get())
|| (!thumbnail && row->sticker != sticker)) {
row->lottie = nullptr;
}
row->thumbnail = thumbnail;
row->sticker = sticker;
row->pixw = pixw;
row->pixh = pixh;
}
}
if (!row->isRecentSet()) {
auto wasInstalled = row->installed;
auto wasArchived = row->archived;
fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived);
if (_section == Section::Installed) {
row->archived = false;
}
if (row->installed != wasInstalled || row->archived != wasArchived) {
row->ripple.reset();
}
}
row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth);
row->count = fillSetCount(set);
}
update();
}

View File

@ -136,7 +136,10 @@ private:
int stickerPacksCount(bool includeArchivedOfficial = false);
// This class is hold in header because it requires Qt preprocessing.
class StickersBox::Inner : public TWidget, private base::Subscriber, private MTP::Sender {
class StickersBox::Inner
: public Ui::RpWidget
, private base::Subscriber
, private MTP::Sender {
Q_OBJECT
public:
@ -233,6 +236,7 @@ private:
int32 pixh = 0;
anim::value yadd;
std::unique_ptr<Ui::RippleAnimation> ripple;
std::unique_ptr<Lottie::SinglePlayer> lottie;
};
struct MegagroupSet {
inline bool operator==(const MegagroupSet &other) const {
@ -272,11 +276,14 @@ private:
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
bool shiftingAnimationCallback(crl::time now);
void paintRow(Painter &p, Row *set, int index);
void paintFakeButton(Painter &p, Row *set, int index);
void paintRow(Painter &p, not_null<Row*> set, int index);
void paintRowThumbnail(Painter &p, not_null<Row*> set, int left);
void paintFakeButton(Painter &p, not_null<Row*> set, int index);
void clear();
void setActionSel(int32 actionSel);
float64 aboveShadowOpacity() const;
void validateLottieAnimation(not_null<Row*> set);
void updateRowThumbnail(not_null<Row*> set);
void readVisibleSets();

View File

@ -710,11 +710,11 @@ QSize StickersListWidget::Footer::iconBox() const {
void StickersListWidget::Footer::validateIconLottieAnimation(
const StickerIcon &icon) {
if (icon.lottie
|| !Stickers::HasLottieThumbnail(ImagePtr(), icon.sticker)) {
|| !Stickers::HasLottieThumbnail(icon.thumbnail, icon.sticker)) {
return;
}
auto player = Stickers::LottieThumbnail(
ImagePtr(),
icon.thumbnail,
icon.sticker,
Stickers::LottieSize::StickersFooter,
iconBox() * cIntRetinaFactor(),
@ -757,14 +757,16 @@ void StickersListWidget::Footer::paintSetIcon(
return;
}
thumb->load(origin);
if (!thumb->loaded()) {
return;
}
const_cast<Footer*>(this)->validateIconLottieAnimation(icon);
if (!icon.lottie) {
auto pix = thumb->pix(origin, icon.pixw, icon.pixh);
p.drawPixmapLeft(x + (st::stickerIconWidth - icon.pixw) / 2, _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2, width(), pix);
if (!thumb->loaded()) {
return;
}
p.drawPixmapLeft(
x + (st::stickerIconWidth - icon.pixw) / 2,
_iconsTop + (st::emojiFooterHeight - icon.pixh) / 2,
width(),
thumb->pix(origin, icon.pixw, icon.pixh));
} else if (icon.lottie->ready()) {
auto request = Lottie::FrameRequest();
request.box = iconBox() * cIntRetinaFactor();

View File

@ -192,6 +192,12 @@ void Animation::parseFailed(Error error) {
_player->failed(this, error);
}
QImage Animation::frame() const {
Expects(_state != nullptr);
return PrepareFrameByRequest(_state->frameForPaint(), true);
}
QImage Animation::frame(const FrameRequest &request) const {
Expects(_state != nullptr);

View File

@ -48,6 +48,7 @@ public:
const FrameRequest &request);
[[nodiscard]] bool ready() const;
[[nodiscard]] QImage frame() const;
[[nodiscard]] QImage frame(const FrameRequest &request) const;
private:

View File

@ -68,6 +68,10 @@ bool SinglePlayer::ready() const {
return _animation.ready();
}
QImage SinglePlayer::frame() const {
return _animation.frame();
}
QImage SinglePlayer::frame(const FrameRequest &request) const {
return _animation.frame(request);
}

View File

@ -53,6 +53,7 @@ public:
rpl::producer<Update, Error> updates() const;
[[nodiscard]] bool ready() const;
[[nodiscard]] QImage frame() const;
[[nodiscard]] QImage frame(const FrameRequest &request) const;
private: