mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Support multiple reaction animations in one message.
This commit is contained in:
parent
4f1e04cf9e
commit
8bde488662
10 changed files with 149 additions and 78 deletions
|
@ -313,19 +313,22 @@ void BottomInfo::paintReactions(
|
|||
int top,
|
||||
int availableWidth,
|
||||
const PaintContext &context) const {
|
||||
struct SingleAnimation {
|
||||
not_null<Reactions::Animation*> animation;
|
||||
QRect target;
|
||||
};
|
||||
std::vector<SingleAnimation> animations;
|
||||
|
||||
auto x = left;
|
||||
auto y = top;
|
||||
auto widthLeft = availableWidth;
|
||||
const auto animated = _reactionAnimation
|
||||
? _reactionAnimation->playingAroundEmoji()
|
||||
: QString();
|
||||
if (_reactionAnimation
|
||||
&& context.reactionInfo
|
||||
&& animated.isEmpty()) {
|
||||
_reactionAnimation = nullptr;
|
||||
}
|
||||
for (const auto &reaction : _reactions) {
|
||||
const auto animating = (reaction.emoji == animated);
|
||||
if (context.reactionInfo
|
||||
&& reaction.animation
|
||||
&& reaction.animation->finished()) {
|
||||
reaction.animation = nullptr;
|
||||
}
|
||||
const auto animating = (reaction.animation != nullptr);
|
||||
const auto add = (reaction.countTextWidth > 0)
|
||||
? st::reactionInfoDigitSkip
|
||||
: st::reactionInfoBetween;
|
||||
|
@ -349,14 +352,15 @@ void BottomInfo::paintReactions(
|
|||
st::reactionInfoImage,
|
||||
st::reactionInfoImage);
|
||||
const auto skipImage = animating
|
||||
&& (reaction.count < 2 || !_reactionAnimation->flying());
|
||||
&& (reaction.count < 2 || !reaction.animation->flying());
|
||||
if (!reaction.image.isNull() && !skipImage) {
|
||||
p.drawImage(image.topLeft(), reaction.image);
|
||||
}
|
||||
if (animating) {
|
||||
context.reactionInfo->effectPaint = [=](QPainter &p) {
|
||||
return _reactionAnimation->paintGetArea(p, origin, image);
|
||||
};
|
||||
animations.push_back({
|
||||
.animation = reaction.animation.get(),
|
||||
.target = image,
|
||||
});
|
||||
}
|
||||
if (reaction.countTextWidth > 0) {
|
||||
p.drawText(
|
||||
|
@ -367,6 +371,19 @@ void BottomInfo::paintReactions(
|
|||
x += width + add;
|
||||
widthLeft -= width + add;
|
||||
}
|
||||
if (!animations.empty()) {
|
||||
context.reactionInfo->effectPaint = [=](QPainter &p) {
|
||||
auto result = QRect();
|
||||
for (const auto &single : animations) {
|
||||
const auto area = single.animation->paintGetArea(
|
||||
p,
|
||||
origin,
|
||||
single.target);
|
||||
result = result.isEmpty() ? area : result.united(area);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
QSize BottomInfo::countCurrentSize(int newWidth) {
|
||||
|
@ -440,11 +457,6 @@ void BottomInfo::layoutRepliesText() {
|
|||
}
|
||||
|
||||
void BottomInfo::layoutReactionsText() {
|
||||
if (_reactionAnimation
|
||||
&& !_data.reactions.contains(
|
||||
_reactionAnimation->playingAroundEmoji())) {
|
||||
_reactionAnimation = nullptr;
|
||||
}
|
||||
if (_data.reactions.empty()) {
|
||||
_reactions.clear();
|
||||
return;
|
||||
|
@ -515,21 +527,39 @@ void BottomInfo::setReactionCount(Reaction &reaction, int count) {
|
|||
void BottomInfo::animateReaction(
|
||||
ReactionAnimationArgs &&args,
|
||||
Fn<void()> repaint) {
|
||||
_reactionAnimation = std::make_unique<Reactions::Animation>(
|
||||
const auto i = ranges::find(_reactions, args.emoji, &Reaction::emoji);
|
||||
if (i == end(_reactions)) {
|
||||
return;
|
||||
}
|
||||
i->animation = std::make_unique<Reactions::Animation>(
|
||||
_reactionsOwner,
|
||||
args.translated(QPoint(width(), height())),
|
||||
std::move(repaint),
|
||||
st::reactionInfoImage);
|
||||
}
|
||||
|
||||
auto BottomInfo::takeSendReactionAnimation()
|
||||
-> std::unique_ptr<Reactions::Animation> {
|
||||
return std::move(_reactionAnimation);
|
||||
auto BottomInfo::takeReactionAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>> {
|
||||
auto result = base::flat_map<
|
||||
QString,
|
||||
std::unique_ptr<Reactions::Animation>>();
|
||||
for (auto &reaction : _reactions) {
|
||||
if (reaction.animation) {
|
||||
result.emplace(reaction.emoji, std::move(reaction.animation));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void BottomInfo::continueSendReactionAnimation(
|
||||
std::unique_ptr<Reactions::Animation> animation) {
|
||||
_reactionAnimation = std::move(animation);
|
||||
void BottomInfo::continueReactionAnimations(base::flat_map<
|
||||
QString,
|
||||
std::unique_ptr<Reactions::Animation>> animations) {
|
||||
for (auto &[emoji, animation] : animations) {
|
||||
const auto i = ranges::find(_reactions, emoji, &Reaction::emoji);
|
||||
if (i != end(_reactions)) {
|
||||
i->animation = std::move(animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
|
||||
|
|
|
@ -76,13 +76,15 @@ public:
|
|||
void animateReaction(
|
||||
ReactionAnimationArgs &&args,
|
||||
Fn<void()> repaint);
|
||||
[[nodiscard]] auto takeSendReactionAnimation()
|
||||
-> std::unique_ptr<Reactions::Animation>;
|
||||
void continueSendReactionAnimation(
|
||||
std::unique_ptr<Reactions::Animation> animation);
|
||||
[[nodiscard]] auto takeReactionAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>>;
|
||||
void continueReactionAnimations(base::flat_map<
|
||||
QString,
|
||||
std::unique_ptr<Reactions::Animation>> animations);
|
||||
|
||||
private:
|
||||
struct Reaction {
|
||||
mutable std::unique_ptr<Reactions::Animation> animation;
|
||||
mutable QImage image;
|
||||
QString emoji;
|
||||
QString countText;
|
||||
|
@ -124,7 +126,6 @@ private:
|
|||
Ui::Text::String _replies;
|
||||
std::vector<Reaction> _reactions;
|
||||
mutable ClickHandlerPtr _revokeLink;
|
||||
mutable std::unique_ptr<Reactions::Animation> _reactionAnimation;
|
||||
int _reactionsMaxWidth = 0;
|
||||
int _dateWidth = 0;
|
||||
bool _authorElided = false;
|
||||
|
|
|
@ -1072,9 +1072,9 @@ void Element::animateUnreadReactions() {
|
|||
}
|
||||
}
|
||||
|
||||
auto Element::takeSendReactionAnimation()
|
||||
-> std::unique_ptr<Reactions::Animation> {
|
||||
return nullptr;
|
||||
auto Element::takeReactionAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>> {
|
||||
return {};
|
||||
}
|
||||
|
||||
Element::~Element() {
|
||||
|
|
|
@ -423,8 +423,8 @@ public:
|
|||
|
||||
virtual void animateReaction(ReactionAnimationArgs &&args);
|
||||
void animateUnreadReactions();
|
||||
[[nodiscard]] virtual auto takeSendReactionAnimation()
|
||||
-> std::unique_ptr<Reactions::Animation>;
|
||||
[[nodiscard]] virtual auto takeReactionAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>>;
|
||||
|
||||
virtual ~Element();
|
||||
|
||||
|
|
|
@ -253,15 +253,18 @@ Message::Message(
|
|||
initLogEntryOriginal();
|
||||
initPsa();
|
||||
refreshReactions();
|
||||
auto animation = replacing
|
||||
? replacing->takeSendReactionAnimation()
|
||||
: nullptr;
|
||||
if (animation) {
|
||||
animation->setRepaintCallback([=] { repaint(); });
|
||||
auto animations = replacing
|
||||
? replacing->takeReactionAnimations()
|
||||
: base::flat_map<QString, std::unique_ptr<Reactions::Animation>>();
|
||||
if (!animations.empty()) {
|
||||
const auto repainter = [=] { repaint(); };
|
||||
for (const auto &[emoji, animation] : animations) {
|
||||
animation->setRepaintCallback(repainter);
|
||||
}
|
||||
if (_reactions) {
|
||||
_reactions->continueSendAnimation(std::move(animation));
|
||||
_reactions->continueAnimations(std::move(animations));
|
||||
} else {
|
||||
_bottomInfo.continueSendReactionAnimation(std::move(animation));
|
||||
_bottomInfo.continueReactionAnimations(std::move(animations));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -424,11 +427,11 @@ void Message::animateReaction(ReactionAnimationArgs &&args) {
|
|||
}
|
||||
}
|
||||
|
||||
auto Message::takeSendReactionAnimation()
|
||||
-> std::unique_ptr<Reactions::Animation> {
|
||||
auto Message::takeReactionAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>> {
|
||||
return _reactions
|
||||
? _reactions->takeSendAnimation()
|
||||
: _bottomInfo.takeSendReactionAnimation();
|
||||
? _reactions->takeAnimations()
|
||||
: _bottomInfo.takeReactionAnimations();
|
||||
}
|
||||
|
||||
QSize Message::performCountOptimalSize() {
|
||||
|
|
|
@ -136,8 +136,8 @@ public:
|
|||
const base::flat_set<UserId> &changes) override;
|
||||
|
||||
void animateReaction(ReactionAnimationArgs &&args) override;
|
||||
auto takeSendReactionAnimation()
|
||||
-> std::unique_ptr<Reactions::Animation> override;
|
||||
auto takeReactionAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>> override;
|
||||
|
||||
protected:
|
||||
void refreshDataIdHook() override;
|
||||
|
|
|
@ -27,11 +27,10 @@ Animation::Animation(
|
|||
Fn<void()> repaint,
|
||||
int size)
|
||||
: _owner(owner)
|
||||
, _emoji(args.emoji)
|
||||
, _repaint(std::move(repaint))
|
||||
, _flyFrom(args.flyFrom) {
|
||||
const auto &list = owner->list(::Data::Reactions::Type::All);
|
||||
const auto i = ranges::find(list, _emoji, &::Data::Reaction::emoji);
|
||||
const auto i = ranges::find(list, args.emoji, &::Data::Reaction::emoji);
|
||||
if (i == end(list) || !i->centerIcon) {
|
||||
return;
|
||||
}
|
||||
|
@ -172,10 +171,9 @@ float64 Animation::flyingProgress() const {
|
|||
return _fly.value(1.);
|
||||
}
|
||||
|
||||
QString Animation::playingAroundEmoji() const {
|
||||
const auto finished = !_valid
|
||||
bool Animation::finished() const {
|
||||
return !_valid
|
||||
|| (!_flyIcon && !_center->animating() && !_effect->animating());
|
||||
return finished ? QString() : _emoji;
|
||||
}
|
||||
|
||||
} // namespace HistoryView::Reactions
|
||||
|
|
|
@ -35,9 +35,9 @@ public:
|
|||
void setRepaintCallback(Fn<void()> repaint);
|
||||
QRect paintGetArea(QPainter &p, QPoint origin, QRect target) const;
|
||||
|
||||
[[nodiscard]] QString playingAroundEmoji() const;
|
||||
[[nodiscard]] bool flying() const;
|
||||
[[nodiscard]] float64 flyingProgress() const;
|
||||
[[nodiscard]] bool finished() const;
|
||||
|
||||
private:
|
||||
void flyCallback();
|
||||
|
@ -46,7 +46,6 @@ private:
|
|||
int computeParabolicTop(int from, int to, float64 progress) const;
|
||||
|
||||
const not_null<::Data::Reactions*> _owner;
|
||||
const QString _emoji;
|
||||
Fn<void()> _repaint;
|
||||
std::shared_ptr<Lottie::Icon> _flyIcon;
|
||||
std::unique_ptr<Lottie::Icon> _center;
|
||||
|
|
|
@ -268,29 +268,34 @@ void InlineList::paint(
|
|||
const PaintContext &context,
|
||||
int outerWidth,
|
||||
const QRect &clip) const {
|
||||
struct SingleAnimation {
|
||||
not_null<Reactions::Animation*> animation;
|
||||
QRect target;
|
||||
};
|
||||
std::vector<SingleAnimation> animations;
|
||||
|
||||
const auto st = context.st;
|
||||
const auto stm = context.messageStyle();
|
||||
const auto padding = st::reactionInlinePadding;
|
||||
const auto size = st::reactionInlineSize;
|
||||
const auto skip = (size - st::reactionInlineImage) / 2;
|
||||
const auto inbubble = (_data.flags & InlineListData::Flag::InBubble);
|
||||
const auto animated = (_animation && context.reactionInfo)
|
||||
? _animation->playingAroundEmoji()
|
||||
: QString();
|
||||
const auto flipped = (_data.flags & Data::Flag::Flipped);
|
||||
if (_animation && context.reactionInfo && animated.isEmpty()) {
|
||||
_animation = nullptr;
|
||||
}
|
||||
p.setFont(st::semiboldFont);
|
||||
for (const auto &button : _buttons) {
|
||||
if (context.reactionInfo
|
||||
&& button.animation
|
||||
&& button.animation->finished()) {
|
||||
button.animation = nullptr;
|
||||
}
|
||||
const auto animating = (button.animation != nullptr);
|
||||
const auto &geometry = button.geometry;
|
||||
const auto mine = (_data.chosenReaction == button.emoji);
|
||||
const auto withoutMine = button.count - (mine ? 1 : 0);
|
||||
const auto animating = (animated == button.emoji);
|
||||
const auto skipImage = animating
|
||||
&& (withoutMine < 1 || !_animation->flying());
|
||||
&& (withoutMine < 1 || !button.animation->flying());
|
||||
const auto bubbleProgress = skipImage
|
||||
? _animation->flyingProgress()
|
||||
? button.animation->flyingProgress()
|
||||
: 1.;
|
||||
const auto bubbleReady = (bubbleProgress == 1.);
|
||||
const auto bubbleSkip = anim::interpolate(
|
||||
|
@ -299,7 +304,7 @@ void InlineList::paint(
|
|||
bubbleProgress);
|
||||
const auto inner = geometry.marginsRemoved(padding);
|
||||
const auto chosen = mine
|
||||
&& (!animating || !_animation->flying() || skipImage);
|
||||
&& (!animating || !button.animation->flying() || skipImage);
|
||||
if (bubbleProgress > 0.) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
|
@ -342,9 +347,10 @@ void InlineList::paint(
|
|||
p.drawImage(image.topLeft(), button.image);
|
||||
}
|
||||
if (animating) {
|
||||
context.reactionInfo->effectPaint = [=](QPainter &p) {
|
||||
return _animation->paintGetArea(p, QPoint(), image);
|
||||
};
|
||||
animations.push_back({
|
||||
.animation = button.animation.get(),
|
||||
.target = image,
|
||||
});
|
||||
}
|
||||
if (bubbleProgress == 0.) {
|
||||
continue;
|
||||
|
@ -381,6 +387,19 @@ void InlineList::paint(
|
|||
p.setOpacity(1.);
|
||||
}
|
||||
}
|
||||
if (!animations.empty()) {
|
||||
context.reactionInfo->effectPaint = [=](QPainter &p) {
|
||||
auto result = QRect();
|
||||
for (const auto &single : animations) {
|
||||
const auto area = single.animation->paintGetArea(
|
||||
p,
|
||||
QPoint(),
|
||||
single.target);
|
||||
result = result.isEmpty() ? area : result.united(area);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool InlineList::getState(
|
||||
|
@ -411,7 +430,11 @@ bool InlineList::getState(
|
|||
void InlineList::animate(
|
||||
ReactionAnimationArgs &&args,
|
||||
Fn<void()> repaint) {
|
||||
_animation = std::make_unique<Reactions::Animation>(
|
||||
const auto i = ranges::find(_buttons, args.emoji, &Button::emoji);
|
||||
if (i == end(_buttons)) {
|
||||
return;
|
||||
}
|
||||
i->animation = std::make_unique<Reactions::Animation>(
|
||||
_owner,
|
||||
std::move(args),
|
||||
std::move(repaint),
|
||||
|
@ -447,13 +470,28 @@ void InlineList::resolveUserpicsImage(const Button &button) const {
|
|||
kMaxRecentUserpics);
|
||||
}
|
||||
|
||||
std::unique_ptr<Animation> InlineList::takeSendAnimation() {
|
||||
return std::move(_animation);
|
||||
auto InlineList::takeAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>> {
|
||||
auto result = base::flat_map<
|
||||
QString,
|
||||
std::unique_ptr<Reactions::Animation>>();
|
||||
for (auto &button : _buttons) {
|
||||
if (button.animation) {
|
||||
result.emplace(button.emoji, std::move(button.animation));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void InlineList::continueSendAnimation(
|
||||
std::unique_ptr<Animation> animation) {
|
||||
_animation = std::move(animation);
|
||||
void InlineList::continueAnimations(base::flat_map<
|
||||
QString,
|
||||
std::unique_ptr<Reactions::Animation>> animations) {
|
||||
for (auto &[emoji, animation] : animations) {
|
||||
const auto i = ranges::find(_buttons, emoji, &Button::emoji);
|
||||
if (i != end(_buttons)) {
|
||||
i->animation = std::move(animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
||||
|
|
|
@ -75,8 +75,11 @@ public:
|
|||
void animate(
|
||||
ReactionAnimationArgs &&args,
|
||||
Fn<void()> repaint);
|
||||
[[nodiscard]] std::unique_ptr<Animation> takeSendAnimation();
|
||||
void continueSendAnimation(std::unique_ptr<Animation> animation);
|
||||
[[nodiscard]] auto takeAnimations()
|
||||
-> base::flat_map<QString, std::unique_ptr<Reactions::Animation>>;
|
||||
void continueAnimations(base::flat_map<
|
||||
QString,
|
||||
std::unique_ptr<Reactions::Animation>> animations);
|
||||
|
||||
private:
|
||||
struct Userpics {
|
||||
|
@ -86,6 +89,7 @@ private:
|
|||
};
|
||||
struct Button {
|
||||
QRect geometry;
|
||||
mutable std::unique_ptr<Animation> animation;
|
||||
mutable QImage image;
|
||||
mutable ClickHandlerPtr link;
|
||||
std::unique_ptr<Userpics> userpics;
|
||||
|
@ -113,8 +117,6 @@ private:
|
|||
std::vector<Button> _buttons;
|
||||
QSize _skipBlock;
|
||||
|
||||
mutable std::unique_ptr<Animation> _animation;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] InlineListData InlineListDataFromMessage(
|
||||
|
|
Loading…
Add table
Reference in a new issue