mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 22:55:11 +00:00
Implement custom reactions in stories.
This commit is contained in:
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user