mirror of
https://github.com/vale981/tdesktop
synced 2025-03-05 17:51:41 -05:00
Use Lottie::MultiPlayer in StickerSetBox.
This commit is contained in:
parent
0dd1b4eae6
commit
a4fbbc06d1
3 changed files with 79 additions and 36 deletions
|
@ -21,7 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/emoji_config.h"
|
#include "ui/emoji_config.h"
|
||||||
#include "lottie/lottie_single_player.h"
|
#include "lottie/lottie_multi_player.h"
|
||||||
|
#include "lottie/lottie_animation.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
@ -65,10 +66,12 @@ protected:
|
||||||
private:
|
private:
|
||||||
struct Element {
|
struct Element {
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
std::unique_ptr<Lottie::SinglePlayer> animated;
|
Lottie::Animation *animated = nullptr;
|
||||||
Ui::Animations::Simple overAnimation;
|
Ui::Animations::Simple overAnimation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void visibleTopBottomUpdated(int visibleTop, int visibleBottom) override;
|
||||||
|
|
||||||
QSize boundingBoxSize() const;
|
QSize boundingBoxSize() const;
|
||||||
|
|
||||||
void paintSticker(Painter &p, int index, QPoint position) const;
|
void paintSticker(Painter &p, int index, QPoint position) const;
|
||||||
|
@ -86,11 +89,14 @@ private:
|
||||||
return (_setFlags & MTPDstickerSet::Flag::f_masks);
|
return (_setFlags & MTPDstickerSet::Flag::f_masks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<Lottie::MultiPlayer*> getLottiePlayer();
|
||||||
|
|
||||||
void showPreview();
|
void showPreview();
|
||||||
|
|
||||||
not_null<Window::SessionController*> _controller;
|
not_null<Window::SessionController*> _controller;
|
||||||
MTP::Sender _mtp;
|
MTP::Sender _mtp;
|
||||||
std::vector<Element> _elements;
|
std::vector<Element> _elements;
|
||||||
|
std::unique_ptr<Lottie::MultiPlayer> _lottiePlayer;
|
||||||
Stickers::Pack _pack;
|
Stickers::Pack _pack;
|
||||||
Stickers::ByEmojiMap _emoji;
|
Stickers::ByEmojiMap _emoji;
|
||||||
bool _loaded = false;
|
bool _loaded = false;
|
||||||
|
@ -472,6 +478,18 @@ void StickerSetBox::Inner::showPreview() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
|
||||||
|
if (!_lottiePlayer) {
|
||||||
|
_lottiePlayer = std::make_unique<Lottie::MultiPlayer>(
|
||||||
|
Lottie::MakeFrameRenderer());
|
||||||
|
_lottiePlayer->updates(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
update();
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
return _lottiePlayer.get();
|
||||||
|
}
|
||||||
|
|
||||||
int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
|
int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
|
||||||
QPoint l(mapFromGlobal(p));
|
QPoint l(mapFromGlobal(p));
|
||||||
if (rtl()) l.setX(width() - l.x());
|
if (rtl()) l.setX(width() - l.x());
|
||||||
|
@ -492,7 +510,8 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 rows = _elements.size() / kStickersPanelPerRow + ((_elements.size() % kStickersPanelPerRow) ? 1 : 0);
|
int32 rows = (_elements.size() / kStickersPanelPerRow)
|
||||||
|
+ ((_elements.size() % kStickersPanelPerRow) ? 1 : 0);
|
||||||
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
|
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
|
||||||
|
|
||||||
for (int32 i = from; i < to; ++i) {
|
for (int32 i = from; i < to; ++i) {
|
||||||
|
@ -505,6 +524,14 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
paintSticker(p, index, pos);
|
paintSticker(p, index, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_lottiePlayer) {
|
||||||
|
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Layer);
|
||||||
|
if (!paused) {
|
||||||
|
_lottiePlayer->markFrameShown();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize StickerSetBox::Inner::boundingBoxSize() const {
|
QSize StickerSetBox::Inner::boundingBoxSize() const {
|
||||||
|
@ -513,20 +540,56 @@ QSize StickerSetBox::Inner::boundingBoxSize() const {
|
||||||
st::stickersSize.height() - st::buttonRadius * 2);
|
st::stickersSize.height() - st::buttonRadius * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||||
|
int visibleTop,
|
||||||
|
int visibleBottom) {
|
||||||
|
const auto pauseInRows = [&](int fromRow, int tillRow) {
|
||||||
|
Expects(fromRow <= tillRow);
|
||||||
|
|
||||||
|
for (auto i = fromRow; i != tillRow; ++i) {
|
||||||
|
for (auto j = 0; j != kStickersPanelPerRow; ++j) {
|
||||||
|
const auto index = i * kStickersPanelPerRow + j;
|
||||||
|
if (index >= _elements.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (const auto animated = _elements[index].animated) {
|
||||||
|
_lottiePlayer->pause(animated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto count = int(_elements.size());
|
||||||
|
const auto rowsCount = (count / kStickersPanelPerRow)
|
||||||
|
+ ((count % kStickersPanelPerRow) ? 1 : 0);
|
||||||
|
const auto rowsTop = st::stickersPadding.top();
|
||||||
|
const auto singleHeight = st::stickersSize.height();
|
||||||
|
const auto rowsBottom = rowsTop + rowsCount * singleHeight;
|
||||||
|
if (visibleTop >= rowsTop + singleHeight && visibleTop < rowsBottom) {
|
||||||
|
const auto pauseHeight = (visibleTop - rowsTop);
|
||||||
|
const auto pauseRows = std::min(
|
||||||
|
pauseHeight / singleHeight,
|
||||||
|
rowsCount);
|
||||||
|
pauseInRows(0, pauseRows);
|
||||||
|
}
|
||||||
|
if (visibleBottom > rowsTop
|
||||||
|
&& visibleBottom + singleHeight <= rowsBottom) {
|
||||||
|
const auto pauseHeight = (rowsBottom - visibleBottom);
|
||||||
|
const auto pauseRows = std::min(
|
||||||
|
pauseHeight / singleHeight,
|
||||||
|
rowsCount);
|
||||||
|
pauseInRows(rowsCount - pauseRows, rowsCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::setupLottie(int index) {
|
void StickerSetBox::Inner::setupLottie(int index) {
|
||||||
auto &element = _elements[index];
|
auto &element = _elements[index];
|
||||||
const auto document = element.document;
|
const auto document = element.document;
|
||||||
|
|
||||||
element.animated = Stickers::LottiePlayerFromDocument(
|
element.animated = Stickers::LottieAnimationFromDocument(
|
||||||
|
getLottiePlayer(),
|
||||||
document,
|
document,
|
||||||
Stickers::LottieSize::StickerSet,
|
Stickers::LottieSize::StickerSet,
|
||||||
boundingBoxSize() * cIntRetinaFactor());
|
boundingBoxSize() * cIntRetinaFactor());
|
||||||
const auto animation = element.animated.get();
|
|
||||||
|
|
||||||
animation->updates(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
update();
|
|
||||||
}, lifetime());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::paintSticker(
|
void StickerSetBox::Inner::paintSticker(
|
||||||
|
@ -558,15 +621,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()) {
|
||||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
|
||||||
Window::GifPauseReason::Layer);
|
|
||||||
if (!paused) {
|
|
||||||
element.animated->markFrameShown();
|
|
||||||
}
|
|
||||||
const auto frame = element.animated->frame();
|
const auto frame = element.animated->frame();
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
QRect(ppos, frame.size() / cIntRetinaFactor()),
|
QRect(ppos, frame.size() / cIntRetinaFactor()),
|
||||||
frame);
|
frame);
|
||||||
|
|
||||||
|
_lottiePlayer->unpause(element.animated);
|
||||||
} else if (const auto image = document->getStickerSmall()) {
|
} else if (const auto image = document->getStickerSmall()) {
|
||||||
p.drawPixmapLeft(
|
p.drawPixmapLeft(
|
||||||
ppos,
|
ppos,
|
||||||
|
|
|
@ -98,7 +98,7 @@ void MultiPlayer::start(
|
||||||
if (_active.empty()
|
if (_active.empty()
|
||||||
|| (_lastSyncTime == kTimeUnknown
|
|| (_lastSyncTime == kTimeUnknown
|
||||||
&& _nextFrameTime == kTimeUnknown)) {
|
&& _nextFrameTime == kTimeUnknown)) {
|
||||||
startBeforeLifeCycle(animation, std::move(info));
|
addNewToActive(animation, std::move(info));
|
||||||
} else {
|
} else {
|
||||||
// We always try to mark as shown at the same time, so we start a new
|
// We always try to mark as shown at the same time, so we start a new
|
||||||
// animation at the same time we mark all existing as shown.
|
// animation at the same time we mark all existing as shown.
|
||||||
|
@ -107,7 +107,7 @@ void MultiPlayer::start(
|
||||||
_updates.fire({});
|
_updates.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiPlayer::startBeforeLifeCycle(
|
void MultiPlayer::addNewToActive(
|
||||||
not_null<Animation*> animation,
|
not_null<Animation*> animation,
|
||||||
StartingInfo &&info) {
|
StartingInfo &&info) {
|
||||||
_active.emplace(animation, info.state.get());
|
_active.emplace(animation, info.state.get());
|
||||||
|
@ -117,20 +117,6 @@ void MultiPlayer::startBeforeLifeCycle(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiPlayer::startInsideLifeCycle(
|
|
||||||
not_null<Animation*> animation,
|
|
||||||
StartingInfo &&info) {
|
|
||||||
const auto state = info.state.get();
|
|
||||||
if (info.paused) {
|
|
||||||
_paused.emplace(
|
|
||||||
animation,
|
|
||||||
PausedInfo{ state, _lastSyncTime, _delay });
|
|
||||||
} else {
|
|
||||||
_active.emplace(animation, state);
|
|
||||||
}
|
|
||||||
startAtRightTime(std::move(info.state));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiPlayer::processPending() {
|
void MultiPlayer::processPending() {
|
||||||
Expects(_lastSyncTime != kTimeUnknown);
|
Expects(_lastSyncTime != kTimeUnknown);
|
||||||
|
|
||||||
|
@ -141,7 +127,7 @@ void MultiPlayer::processPending() {
|
||||||
unpauseAndKeepUp(animation);
|
unpauseAndKeepUp(animation);
|
||||||
}
|
}
|
||||||
for (auto &[animation, info] : base::take(_pendingToStart)) {
|
for (auto &[animation, info] : base::take(_pendingToStart)) {
|
||||||
startInsideLifeCycle(animation, std::move(info));
|
addNewToActive(animation, std::move(info));
|
||||||
}
|
}
|
||||||
for (const auto &animation : base::take(_pendingRemove)) {
|
for (const auto &animation : base::take(_pendingRemove)) {
|
||||||
removeNow(animation);
|
removeNow(animation);
|
||||||
|
|
|
@ -69,10 +69,7 @@ private:
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void startBeforeLifeCycle(
|
void addNewToActive(
|
||||||
not_null<Animation*> animation,
|
|
||||||
StartingInfo &&info);
|
|
||||||
void startInsideLifeCycle(
|
|
||||||
not_null<Animation*> animation,
|
not_null<Animation*> animation,
|
||||||
StartingInfo &&info);
|
StartingInfo &&info);
|
||||||
[[nodiscard]] int countFrameIndex(
|
[[nodiscard]] int countFrameIndex(
|
||||||
|
|
Loading…
Add table
Reference in a new issue