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

View File

@ -20,8 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "dialogs/dialogs_layout.h" #include "dialogs/dialogs_layout.h"
#include "styles/style_boxes.h" #include "lottie/lottie_single_player.h"
#include "styles/style_chat_helpers.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/widgets/scroll_area.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/discrete_sliders.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "window/window_session_controller.h"
#include "auth_session.h" #include "auth_session.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
namespace { namespace {
@ -655,7 +657,8 @@ StickersBox::Inner::Row::Row(
StickersBox::Inner::Row::~Row() = default; 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) , _section(section)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) { , _shiftingAnimation([=](crl::time now) {
@ -669,7 +672,8 @@ StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidg
setup(); 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) , _section(StickersBox::Section::Installed)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _shiftingAnimation([=](crl::time now) { , _shiftingAnimation([=](crl::time now) {
@ -794,7 +798,7 @@ QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const {
return QRect(buttonx, buttony, buttonw, buttonh); 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()); auto xadd = 0, yadd = qRound(set->yadd.current());
if (xadd || yadd) p.translate(xadd, yadd); if (xadd || yadd) p.translate(xadd, yadd);
@ -854,17 +858,7 @@ void StickersBox::Inner::paintRow(Painter &p, Row *set, int index) {
} }
if (set->sticker) { if (set->sticker) {
const auto origin = Data::FileOriginStickerSet( paintRowThumbnail(p, set, stickerx);
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);
}
} }
int namex = stickerx + st::contactsPhotoSize + st::contactsPadding.left(); 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); 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 removeButton = (_section == Section::Installed && !set->removed);
auto rect = relativeButtonRect(removeButton); auto rect = relativeButtonRect(removeButton);
if (_section != Section::Installed && set->installed && !set->archived && !set->removed) { if (_section != Section::Installed && set->installed && !set->archived && !set->removed) {
@ -1535,16 +1619,22 @@ void StickersBox::Inner::updateSize(int newWidth) {
void StickersBox::Inner::updateRows() { void StickersBox::Inner::updateRows() {
int maxNameWidth = countMaxNameWidth(); int maxNameWidth = countMaxNameWidth();
auto &sets = Auth().data().stickerSets(); auto &sets = Auth().data().stickerSets();
for_const (auto &row, _rows) { for (const auto &row : _rows) {
auto it = sets.constFind(row->id); const auto it = sets.constFind(row->id);
if (it != sets.cend()) { if (it == sets.cend()) {
auto &set = it.value(); continue;
}
const auto &set = it.value();
if (!row->sticker) { if (!row->sticker) {
auto thumbnail = ImagePtr(); auto thumbnail = ImagePtr();
auto sticker = (DocumentData*)nullptr; auto sticker = (DocumentData*)nullptr;
auto pixw = 0, pixh = 0; auto pixw = 0, pixh = 0;
fillSetCover(set, &thumbnail, &sticker, &pixw, &pixh); fillSetCover(set, &thumbnail, &sticker, &pixw, &pixh);
if (sticker) { if (sticker) {
if ((row->thumbnail.get() != thumbnail.get())
|| (!thumbnail && row->sticker != sticker)) {
row->lottie = nullptr;
}
row->thumbnail = thumbnail; row->thumbnail = thumbnail;
row->sticker = sticker; row->sticker = sticker;
row->pixw = pixw; row->pixw = pixw;
@ -1565,7 +1655,6 @@ void StickersBox::Inner::updateRows() {
row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth); row->title = fillSetTitle(set, maxNameWidth, &row->titleWidth);
row->count = fillSetCount(set); row->count = fillSetCount(set);
} }
}
update(); update();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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