2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-31 14:45:14 +00:00

Implement custom reactions in stories.

This commit is contained in:
John Preston
2023-08-08 10:55:12 +02:00
parent 066dbfe8fc
commit 13f67d68c4
21 changed files with 744 additions and 192 deletions

View File

@@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/emoji_fly_animation.h"
#include "data/stickers/data_custom_emoji.h"
#include "ui/text/text_custom_emoji.h"
#include "ui/animated_icon.h"
#include "styles/style_info.h"
#include "styles/style_chat.h"
@@ -100,4 +102,10 @@ bool EmojiFlyAnimation::paintBadgeFrame(not_null<QWidget*> widget) {
return !_fly.finished();
}
ReactionFlyCenter EmojiFlyAnimation::grabBadgeCenter() {
auto result = _fly.takeCenter();
result.size = _flySize;
return result;
}
} // namespace Ui

View File

@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
struct ReactionFlyCenter;
class EmojiFlyAnimation {
public:
EmojiFlyAnimation(
@@ -26,6 +28,7 @@ public:
void repaint();
bool paintBadgeFrame(not_null<QWidget*> widget);
[[nodiscard]] ReactionFlyCenter grabBadgeCenter();
private:
const int _flySize = 0;

View File

@@ -68,7 +68,8 @@ ReactionFlyAnimation::ReactionFlyAnimation(
: _owner(owner)
, _repaint(std::move(repaint))
, _flyFrom(args.flyFrom)
, _scaleOutDuration(args.scaleOutDuration) {
, _scaleOutDuration(args.scaleOutDuration)
, _scaleOutTarget(args.scaleOutTarget) {
const auto &list = owner->list(::Data::Reactions::Type::All);
auto centerIcon = (DocumentData*)nullptr;
auto aroundAnimation = (DocumentData*)nullptr;
@@ -86,12 +87,14 @@ ReactionFlyAnimation::ReactionFlyAnimation(
aroundAnimation = owner->chooseGenericAnimation(document);
} else {
const auto i = ranges::find(list, args.id, &::Data::Reaction::id);
if (i == end(list) || !i->centerIcon) {
if (i == end(list)/* || !i->centerIcon*/) {
return;
}
centerIcon = i->centerIcon;
centerIcon = i->centerIcon
? not_null(i->centerIcon)
: i->selectAnimation;
aroundAnimation = i->aroundAnimation;
_centerSizeMultiplier = 1.;
_centerSizeMultiplier = i->centerIcon ? 1. : 0.5;
}
const auto resolve = [&](
std::unique_ptr<AnimatedIcon> &icon,
@@ -139,21 +142,31 @@ QRect ReactionFlyAnimation::paintGetArea(
QRect clip,
crl::time now) const {
const auto scale = [&] {
const auto rate = _effect ? _effect->frameRate() : 0.;
if (!_scaleOutDuration || !rate) {
if (!_scaleOutDuration
|| (!_effect && !_noEffectScaleStarted)) {
return 1.;
}
const auto left = _effect->framesCount() - _effect->frameIndex();
const auto duration = left * 1000. / rate;
return (duration < _scaleOutDuration)
? (duration / double(_scaleOutDuration))
: 1.;
auto progress = _noEffectScaleAnimation.value(0.);
if (_effect) {
const auto rate = _effect->frameRate();
if (!rate) {
return 1.;
}
const auto left = _effect->framesCount() - _effect->frameIndex();
const auto duration = left * 1000. / rate;
progress = (duration < _scaleOutDuration)
? (duration / double(_scaleOutDuration))
: 1.;
}
return (1. * progress + _scaleOutTarget * (1. - progress));
}();
auto hq = std::optional<PainterHighQualityEnabler>();
if (scale < 1.) {
const auto delta = ((1. - scale) / 2.) * target.size();
target = QRect(
target.topLeft() + QPoint(delta.width(), delta.height()),
target.size() * scale);
hq.emplace(p);
const auto shift = QRectF(target).center();
p.translate(shift);
p.scale(scale, scale);
p.translate(-shift);
}
if (!_valid) {
return QRect();
@@ -169,8 +182,10 @@ QRect ReactionFlyAnimation::paintGetArea(
if (clip.isEmpty() || area.intersects(clip)) {
paintCenterFrame(p, target, colored, now);
if (const auto effect = _effect.get()) {
// Must not be colored to text.
p.drawImage(wide, effect->frame(QColor()));
if (effect->animating()) {
// Must not be colored to text.
p.drawImage(wide, effect->frame(QColor()));
}
}
paintMiniCopies(p, target.center(), colored, now);
}
@@ -359,6 +374,9 @@ void ReactionFlyAnimation::startAnimations() {
}
if (const auto effect = _effect.get()) {
_effect->animate(callback());
} else if (_scaleOutDuration > 0) {
_noEffectScaleStarted = true;
_noEffectScaleAnimation.start(callback(), 1, 0, _scaleOutDuration);
}
if (!_miniCopies.empty()) {
_minis.start(callback(), 0., 1., kMiniCopiesDurationMax);
@@ -382,7 +400,19 @@ bool ReactionFlyAnimation::finished() const {
|| (_flyIcon.isNull()
&& (!_center || !_center->animating())
&& (!_effect || !_effect->animating())
&& !_noEffectScaleAnimation.animating()
&& !_minis.animating());
}
ReactionFlyCenter ReactionFlyAnimation::takeCenter() {
_valid = false;
return {
.custom = std::move(_custom),
.icon = std::move(_center),
.scale = (_scaleOutDuration > 0) ? _scaleOutTarget : 1.,
.centerSizeMultiplier = _centerSizeMultiplier,
.customSize = _customSize,
};
}
} // namespace HistoryView::Reactions

View File

@@ -28,11 +28,21 @@ struct ReactionFlyAnimationArgs {
QImage flyIcon;
QRect flyFrom;
crl::time scaleOutDuration = 0;
float64 scaleOutTarget = 0.;
bool effectOnly = false;
[[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const;
};
struct ReactionFlyCenter {
std::unique_ptr<Text::CustomEmoji> custom;
std::unique_ptr<AnimatedIcon> icon;
float64 scale = 0.;
float64 centerSizeMultiplier = 0.;
int customSize = 0;
int size = 0;
};
class ReactionFlyAnimation final {
public:
ReactionFlyAnimation(
@@ -56,6 +66,8 @@ public:
[[nodiscard]] float64 flyingProgress() const;
[[nodiscard]] bool finished() const;
[[nodiscard]] ReactionFlyCenter takeCenter();
private:
struct Parabolic {
float64 a = 0.;
@@ -98,6 +110,7 @@ private:
std::unique_ptr<Text::CustomEmoji> _custom;
std::unique_ptr<AnimatedIcon> _center;
std::unique_ptr<AnimatedIcon> _effect;
Animations::Simple _noEffectScaleAnimation;
std::vector<MiniCopy> _miniCopies;
Animations::Simple _fly;
Animations::Simple _minis;
@@ -105,6 +118,8 @@ private:
float64 _centerSizeMultiplier = 0.;
int _customSize = 0;
crl::time _scaleOutDuration = 0;
float64 _scaleOutTarget = 0.;
bool _noEffectScaleStarted = false;
bool _valid = false;
mutable Parabolic _cached;