mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 21:27:07 +02:00
Show effect animation with correct geometry.
This commit is contained in:
parent
a19e71324b
commit
92133e7f50
15 changed files with 197 additions and 43 deletions
|
@ -286,7 +286,7 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
|
|||
document,
|
||||
media->videoThumbnailContent(),
|
||||
QString(),
|
||||
true);
|
||||
Stickers::EffectType::PremiumSticker);
|
||||
|
||||
const auto update = [=] {
|
||||
if (!state->readyInvoked
|
||||
|
|
|
@ -269,10 +269,10 @@ std::unique_ptr<Lottie::SinglePlayer> EmojiPack::effectPlayer(
|
|||
not_null<DocumentData*> document,
|
||||
QByteArray data,
|
||||
QString filepath,
|
||||
bool premium) {
|
||||
EffectType type) {
|
||||
// Shortened copy from stickers_lottie module.
|
||||
const auto baseKey = document->bigFileBaseCacheKey();
|
||||
const auto tag = uint8(0);
|
||||
const auto tag = uint8(type);
|
||||
const auto keyShift = ((tag << 4) & 0xF0)
|
||||
| (uint8(ChatHelpers::StickerLottieSize::EmojiInteraction) & 0x0F);
|
||||
const auto key = Storage::Cache::Key{
|
||||
|
@ -292,19 +292,24 @@ std::unique_ptr<Lottie::SinglePlayer> EmojiPack::effectPlayer(
|
|||
std::move(data));
|
||||
});
|
||||
};
|
||||
const auto size = premium
|
||||
const auto size = (type == EffectType::PremiumSticker)
|
||||
? HistoryView::Sticker::PremiumEffectSize(document)
|
||||
: HistoryView::Sticker::EmojiEffectSize();
|
||||
: (type == EffectType::EmojiInteraction)
|
||||
? HistoryView::Sticker::EmojiEffectSize()
|
||||
: HistoryView::Sticker::MessageEffectSize();
|
||||
const auto request = Lottie::FrameRequest{
|
||||
size * style::DevicePixelRatio(),
|
||||
};
|
||||
auto &weakProvider = _sharedProviders[document];
|
||||
auto &weakProvider = _sharedProviders[{ document, type }];
|
||||
auto shared = [&] {
|
||||
if (const auto result = weakProvider.lock()) {
|
||||
return result;
|
||||
}
|
||||
const auto count = (type == EffectType::PremiumSticker)
|
||||
? kPremiumCachesCount
|
||||
: kEmojiCachesCount;
|
||||
const auto result = Lottie::SinglePlayer::SharedProvider(
|
||||
premium ? kPremiumCachesCount : kEmojiCachesCount,
|
||||
count,
|
||||
get,
|
||||
put,
|
||||
Lottie::ReadContent(data, filepath),
|
||||
|
|
|
@ -50,6 +50,12 @@ struct LargeEmojiImage {
|
|||
[[nodiscard]] static QSize Size();
|
||||
};
|
||||
|
||||
enum class EffectType : uint8 {
|
||||
EmojiInteraction,
|
||||
PremiumSticker,
|
||||
MessageEffect,
|
||||
};
|
||||
|
||||
class EmojiPack final {
|
||||
public:
|
||||
using ViewElement = HistoryView::Element;
|
||||
|
@ -95,11 +101,23 @@ public:
|
|||
not_null<DocumentData*> document,
|
||||
QByteArray data,
|
||||
QString filepath,
|
||||
bool premium);
|
||||
EffectType type);
|
||||
|
||||
private:
|
||||
class ImageLoader;
|
||||
|
||||
struct ProviderKey {
|
||||
not_null<DocumentData*> document;
|
||||
Stickers::EffectType type = {};
|
||||
|
||||
friend inline auto operator<=>(
|
||||
const ProviderKey &,
|
||||
const ProviderKey &) = default;
|
||||
friend inline bool operator==(
|
||||
const ProviderKey &,
|
||||
const ProviderKey &) = default;
|
||||
};
|
||||
|
||||
void refresh();
|
||||
void refreshDelayed();
|
||||
void refreshAnimations();
|
||||
|
@ -135,7 +153,7 @@ private:
|
|||
mtpRequestId _animationsRequestId = 0;
|
||||
|
||||
base::flat_map<
|
||||
not_null<DocumentData*>,
|
||||
ProviderKey,
|
||||
std::weak_ptr<Lottie::FrameProvider>> _sharedProviders;
|
||||
|
||||
rpl::event_stream<> _refreshed;
|
||||
|
|
|
@ -737,6 +737,9 @@ void Reactions::resolveEffectImages() {
|
|||
LOG(("API Error: Effect '%1' not found!"
|
||||
).arg(ReactionIdToLog(id)));
|
||||
}
|
||||
if (i != end(_effects)) {
|
||||
preloadEffect(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -173,20 +173,10 @@ ClickHandlerPtr BottomInfo::replayEffectLink(
|
|||
left += width() - available;
|
||||
top += st::msgDateFont->height;
|
||||
}
|
||||
auto x = left;
|
||||
auto y = top;
|
||||
auto widthLeft = available;
|
||||
if (_effect) {
|
||||
const auto add = st::reactionInfoBetween;
|
||||
const auto width = st::reactionInfoSize;
|
||||
if (x > left && widthLeft < width) {
|
||||
x = left;
|
||||
y += st::msgDateFont->height;
|
||||
widthLeft = available;
|
||||
}
|
||||
const auto image = QRect(
|
||||
x,
|
||||
y,
|
||||
left,
|
||||
top,
|
||||
st::reactionInfoSize,
|
||||
st::msgDateFont->height);
|
||||
if (image.contains(position)) {
|
||||
|
@ -195,8 +185,6 @@ ClickHandlerPtr BottomInfo::replayEffectLink(
|
|||
}
|
||||
return _replayLink;
|
||||
}
|
||||
x += width + add;
|
||||
widthLeft -= width + add;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -544,6 +532,25 @@ void BottomInfo::continueEffectAnimation(
|
|||
}
|
||||
}
|
||||
|
||||
QRect BottomInfo::effectIconGeometry() const {
|
||||
if (!_effect) {
|
||||
return {};
|
||||
}
|
||||
auto left = 0;
|
||||
auto top = 0;
|
||||
auto available = width();
|
||||
if (height() != minHeight()) {
|
||||
available = std::min(available, _effectMaxWidth);
|
||||
left += width() - available;
|
||||
top += st::msgDateFont->height;
|
||||
}
|
||||
return QRect(
|
||||
left + (st::reactionInfoSize - st::effectInfoImage) / 2,
|
||||
top + (st::msgDateFont->height - st::effectInfoImage) / 2,
|
||||
st::effectInfoImage,
|
||||
st::effectInfoImage);
|
||||
}
|
||||
|
||||
BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
|
||||
using Flag = BottomInfo::Data::Flag;
|
||||
const auto item = message->data();
|
||||
|
|
|
@ -82,6 +82,8 @@ public:
|
|||
void continueEffectAnimation(
|
||||
std::unique_ptr<Ui::ReactionFlyAnimation> animation);
|
||||
|
||||
QRect effectIconGeometry() const;
|
||||
|
||||
private:
|
||||
struct Effect;
|
||||
|
||||
|
|
|
@ -1786,6 +1786,10 @@ auto Element::takeEffectAnimation()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
QRect Element::effectIconGeometry() const {
|
||||
return QRect();
|
||||
}
|
||||
|
||||
Element::~Element() {
|
||||
// Delete media while owner still exists.
|
||||
clearSpecialOnlyEmoji();
|
||||
|
|
|
@ -523,6 +523,7 @@ public:
|
|||
void previousInBlocksChanged();
|
||||
void nextInBlocksRemoved();
|
||||
|
||||
[[nodiscard]] virtual QRect effectIconGeometry() const;
|
||||
[[nodiscard]] virtual QRect innerGeometry() const = 0;
|
||||
|
||||
void customEmojiRepaint();
|
||||
|
|
|
@ -119,7 +119,7 @@ bool EmojiInteractions::playPremiumEffect(
|
|||
document->createMediaView()->videoThumbnailContent(),
|
||||
QString(),
|
||||
false,
|
||||
true);
|
||||
Stickers::EffectType::PremiumSticker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ void EmojiInteractions::play(
|
|||
media->bytes(),
|
||||
media->owner()->filepath(),
|
||||
incoming,
|
||||
false);
|
||||
Stickers::EffectType::EmojiInteraction);
|
||||
}
|
||||
|
||||
void EmojiInteractions::playEffectOnRead(not_null<const Element*> view) {
|
||||
|
@ -214,7 +214,7 @@ void EmojiInteractions::playEffect(
|
|||
resolved.content,
|
||||
resolved.filepath,
|
||||
false,
|
||||
false);
|
||||
Stickers::EffectType::MessageEffect);
|
||||
}
|
||||
|
||||
void EmojiInteractions::addPendingEffect(not_null<const Element*> view) {
|
||||
|
@ -272,7 +272,7 @@ void EmojiInteractions::play(
|
|||
QByteArray data,
|
||||
QString filepath,
|
||||
bool incoming,
|
||||
bool premium) {
|
||||
Stickers::EffectType type) {
|
||||
const auto top = _itemTop(view);
|
||||
const auto bottom = top + view->height();
|
||||
if (_visibleTop >= bottom
|
||||
|
@ -286,12 +286,14 @@ void EmojiInteractions::play(
|
|||
document,
|
||||
data,
|
||||
filepath,
|
||||
premium);
|
||||
type);
|
||||
|
||||
const auto inner = premium
|
||||
const auto inner = (type == Stickers::EffectType::PremiumSticker)
|
||||
? HistoryView::Sticker::Size(document)
|
||||
: HistoryView::Sticker::EmojiSize();
|
||||
const auto shift = premium ? QPoint() : GenerateRandomShift(inner);
|
||||
const auto shift = (type == Stickers::EffectType::EmojiInteraction)
|
||||
? GenerateRandomShift(inner)
|
||||
: QPoint();
|
||||
const auto raw = lottie.get();
|
||||
lottie->updates(
|
||||
) | rpl::start_with_next([=](Lottie::Update update) {
|
||||
|
@ -312,16 +314,18 @@ void EmojiInteractions::play(
|
|||
.lottie = std::move(lottie),
|
||||
.shift = shift,
|
||||
.inner = inner,
|
||||
.outer = (premium
|
||||
.outer = ((type == Stickers::EffectType::PremiumSticker)
|
||||
? HistoryView::Sticker::PremiumEffectSize(document)
|
||||
: HistoryView::Sticker::EmojiEffectSize()),
|
||||
.premium = premium,
|
||||
: (type == Stickers::EffectType::EmojiInteraction)
|
||||
? HistoryView::Sticker::EmojiEffectSize()
|
||||
: HistoryView::Sticker::MessageEffectSize()),
|
||||
.type = type,
|
||||
});
|
||||
if (incoming) {
|
||||
_playStarted.fire(std::move(emoticon));
|
||||
}
|
||||
if (const auto media = view->media()) {
|
||||
if (!premium) {
|
||||
if (type == Stickers::EffectType::EmojiInteraction) {
|
||||
media->stickerClearLoopPlayed();
|
||||
}
|
||||
}
|
||||
|
@ -336,9 +340,28 @@ void EmojiInteractions::visibleAreaUpdated(
|
|||
|
||||
QRect EmojiInteractions::computeRect(const Play &play) const {
|
||||
const auto view = play.view;
|
||||
const auto viewTop = _itemTop(view);
|
||||
if (viewTop < 0) {
|
||||
return QRect();
|
||||
}
|
||||
if (play.type == Stickers::EffectType::MessageEffect) {
|
||||
const auto icon = view->effectIconGeometry();
|
||||
if (icon.isEmpty()) {
|
||||
return QRect();
|
||||
}
|
||||
const auto size = play.outer;
|
||||
const auto shift = view->hasRightLayout()
|
||||
? (-size.width() / 3)
|
||||
: (size.width() / 3);
|
||||
return QRect(
|
||||
shift + icon.x() + (icon.width() - size.width()) / 2,
|
||||
viewTop + icon.y() + (icon.height() - size.height()) / 2,
|
||||
size.width(),
|
||||
size.height());
|
||||
}
|
||||
const auto sticker = play.inner;
|
||||
const auto size = play.outer;
|
||||
const auto shift = play.premium
|
||||
const auto shift = (play.type == Stickers::EffectType::PremiumSticker)
|
||||
? int(sticker.width() * kPremiumShift)
|
||||
: (size.width() / 40);
|
||||
const auto inner = view->innerGeometry();
|
||||
|
@ -346,11 +369,9 @@ QRect EmojiInteractions::computeRect(const Play &play) const {
|
|||
const auto left = rightAligned
|
||||
? (inner.x() + inner.width() + shift - size.width())
|
||||
: (inner.x() - shift);
|
||||
const auto viewTop = _itemTop(view) + inner.y();
|
||||
if (viewTop < 0) {
|
||||
return QRect();
|
||||
}
|
||||
const auto top = viewTop + (sticker.height() - size.height()) / 2;
|
||||
const auto top = viewTop
|
||||
+ inner.y()
|
||||
+ (sticker.height() - size.height()) / 2;
|
||||
return QRect(QPoint(left, top), size).translated(play.shift);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,10 @@ namespace Main {
|
|||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Stickers {
|
||||
enum class EffectType : uint8;
|
||||
} // namespace Stickers
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class Element;
|
||||
|
@ -60,7 +64,7 @@ private:
|
|||
int frame = 0;
|
||||
int framesCount = 0;
|
||||
int frameRate = 0;
|
||||
bool premium = false;
|
||||
Stickers::EffectType type = {};
|
||||
bool started = false;
|
||||
bool finished = false;
|
||||
};
|
||||
|
@ -96,7 +100,7 @@ private:
|
|||
QByteArray data,
|
||||
QString filepath,
|
||||
bool incoming,
|
||||
bool premium);
|
||||
Stickers::EffectType type);
|
||||
void checkDelayed();
|
||||
void addPendingEffect(not_null<const Element*> view);
|
||||
|
||||
|
|
|
@ -707,6 +707,88 @@ auto Message::takeEffectAnimation()
|
|||
return _bottomInfo.takeEffectAnimation();
|
||||
}
|
||||
|
||||
QRect Message::effectIconGeometry() const {
|
||||
const auto item = data();
|
||||
const auto media = this->media();
|
||||
|
||||
auto g = countGeometry();
|
||||
if (g.width() < 1 || isHidden()) {
|
||||
return {};
|
||||
}
|
||||
const auto bubble = drawBubble();
|
||||
const auto reactionsInBubble = _reactions && embedReactionsInBubble();
|
||||
const auto mediaDisplayed = media && media->isDisplayed();
|
||||
const auto keyboard = item->inlineReplyKeyboard();
|
||||
auto keyboardHeight = 0;
|
||||
if (keyboard) {
|
||||
keyboardHeight = keyboard->naturalHeight();
|
||||
g.setHeight(g.height() - st::msgBotKbButton.margin - keyboardHeight);
|
||||
}
|
||||
|
||||
const auto fromBottomInfo = [&](QPoint bottomRight) {
|
||||
const auto size = _bottomInfo.currentSize();
|
||||
return _bottomInfo.effectIconGeometry().translated(
|
||||
bottomRight - QPoint(size.width(), size.height()));
|
||||
};
|
||||
if (bubble) {
|
||||
auto entry = logEntryOriginal();
|
||||
|
||||
// Entry page is always a bubble bottom.
|
||||
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
|
||||
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
|
||||
|
||||
auto inner = g;
|
||||
if (_comments) {
|
||||
inner.setHeight(inner.height() - st::historyCommentsButtonHeight);
|
||||
}
|
||||
auto trect = inner.marginsRemoved(st::msgPadding);
|
||||
const auto reactionsTop = (reactionsInBubble && !_viewButton)
|
||||
? st::mediaInBubbleSkip
|
||||
: 0;
|
||||
const auto reactionsHeight = reactionsInBubble
|
||||
? (reactionsTop + _reactions->height())
|
||||
: 0;
|
||||
if (_viewButton) {
|
||||
const auto belowInfo = _viewButton->belowMessageInfo();
|
||||
const auto infoHeight = reactionsInBubble
|
||||
? (reactionsHeight + 2 * st::mediaInBubbleSkip)
|
||||
: _bottomInfo.height();
|
||||
const auto heightMargins = QMargins(0, 0, 0, infoHeight);
|
||||
if (belowInfo) {
|
||||
inner -= heightMargins;
|
||||
}
|
||||
trect.setHeight(trect.height() - _viewButton->height());
|
||||
if (reactionsInBubble) {
|
||||
trect.setHeight(trect.height() - st::mediaInBubbleSkip + st::msgPadding.bottom());
|
||||
} else if (mediaDisplayed) {
|
||||
trect.setHeight(trect.height() - st::mediaInBubbleSkip);
|
||||
}
|
||||
}
|
||||
if (mediaOnBottom) {
|
||||
trect.setHeight(trect.height()
|
||||
+ st::msgPadding.bottom()
|
||||
- viewButtonHeight());
|
||||
}
|
||||
if (mediaOnTop) {
|
||||
trect.setY(trect.y() - st::msgPadding.top());
|
||||
}
|
||||
if (mediaDisplayed && mediaOnBottom && media->customInfoLayout()) {
|
||||
auto mediaHeight = media->height();
|
||||
auto mediaLeft = trect.x() - st::msgPadding.left();
|
||||
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
|
||||
return fromBottomInfo(QPoint(mediaLeft, mediaTop) + media->resolveCustomInfoRightBottom());
|
||||
} else {
|
||||
return fromBottomInfo({
|
||||
inner.left() + inner.width() - (st::msgPadding.right() - st::msgDateDelta.x()),
|
||||
inner.top() + inner.height() - (st::msgPadding.bottom() - st::msgDateDelta.y()),
|
||||
});
|
||||
}
|
||||
} else if (mediaDisplayed) {
|
||||
return fromBottomInfo(g.topLeft() + media->resolveCustomInfoRightBottom());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QSize Message::performCountOptimalSize() {
|
||||
const auto item = data();
|
||||
|
||||
|
|
|
@ -162,6 +162,7 @@ public:
|
|||
auto takeEffectAnimation()
|
||||
-> std::unique_ptr<Ui::ReactionFlyAnimation> override;
|
||||
|
||||
QRect effectIconGeometry() const override;
|
||||
QRect innerGeometry() const override;
|
||||
[[nodiscard]] BottomRippleMask bottomRippleMask(int buttonHeight) const;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ constexpr auto kMaxSizeFixed = 512;
|
|||
constexpr auto kMaxEmojiSizeFixed = 256;
|
||||
constexpr auto kPremiumMultiplier = (1 + 0.245 * 2);
|
||||
constexpr auto kEmojiMultiplier = 3;
|
||||
constexpr auto kMessageEffectMultiplier = 2;
|
||||
|
||||
[[nodiscard]] QImage CacheDiceImage(
|
||||
const QString &emoji,
|
||||
|
@ -208,6 +209,10 @@ QSize Sticker::EmojiEffectSize() {
|
|||
return EmojiSize() * kEmojiMultiplier;
|
||||
}
|
||||
|
||||
QSize Sticker::MessageEffectSize() {
|
||||
return EmojiSize() * kMessageEffectMultiplier;
|
||||
}
|
||||
|
||||
QSize Sticker::EmojiSize() {
|
||||
const auto side = std::min(st::maxAnimatedEmojiSize, kMaxEmojiSizeFixed);
|
||||
return { side, side };
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
not_null<DocumentData*> document);
|
||||
[[nodiscard]] static QSize UsualPremiumEffectSize();
|
||||
[[nodiscard]] static QSize EmojiEffectSize();
|
||||
[[nodiscard]] static QSize MessageEffectSize();
|
||||
[[nodiscard]] static QSize EmojiSize();
|
||||
[[nodiscard]] static ClickHandlerPtr ShowSetHandler(
|
||||
not_null<DocumentData*> document);
|
||||
|
|
|
@ -339,7 +339,7 @@ void MediaPreviewWidget::setupLottie() {
|
|||
_document,
|
||||
_documentMedia->videoThumbnailContent(),
|
||||
QString(),
|
||||
true);
|
||||
Stickers::EffectType::PremiumSticker);
|
||||
} else {
|
||||
const auto size = currentDimensions();
|
||||
_lottie = std::make_unique<Lottie::SinglePlayer>(
|
||||
|
|
Loading…
Add table
Reference in a new issue