Improve stars in collectible emoji status.

This commit is contained in:
John Preston 2025-01-14 13:54:58 +04:00
parent 6d7abd1718
commit e3517aceab
2 changed files with 221 additions and 73 deletions

View file

@ -2387,8 +2387,6 @@ not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
EmojiStatusId id,
not_null<DocumentData*> document,
uint64 setId) {
Expects(document->sticker() != nullptr);
const auto documentId = document->id;
const auto i = _customEmoji.find(id);
const auto recentOnly = (i != end(_customEmoji)) && i->second.recentOnly;
@ -2469,9 +2467,6 @@ void EmojiListWidget::refreshEmojiStatusCollectibles() {
}
const auto type = Data::EmojiStatuses::Type::Collectibles;
const auto &list = session().data().emojiStatuses().list(type);
if (list.empty()) {
return;
}
const auto setId = Data::Stickers::CollectibleSetId;
auto set = std::vector<CustomOne>();
set.reserve(list.size());
@ -2480,14 +2475,16 @@ void EmojiListWidget::refreshEmojiStatusCollectibles() {
? status.collectible->documentId
: status.documentId;
const auto document = session().data().document(documentId);
if (const auto sticker = document->sticker()) {
set.push_back({
.collectible = status.collectible,
.custom = resolveCustomEmoji(status, document, setId),
.document = document,
.emoji = Ui::Emoji::Find(sticker->alt),
});
}
const auto sticker = document->sticker();
set.push_back({
.collectible = status.collectible,
.custom = resolveCustomEmoji(status, document, setId),
.document = document,
.emoji = sticker ? Ui::Emoji::Find(sticker->alt) : nullptr,
});
}
if (set.empty()) {
return;
}
const auto collectibles = session().data().stickers().collectibleSet();
_custom.push_back({

View file

@ -7,11 +7,221 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/effects/premium_stars_colored.h"
#include "base/random.h"
#include "ui/effects/premium_graphics.h" // GiftGradientStops.
#include "ui/text/text_custom_emoji.h"
#include "ui/painter.h"
#include "ui/rp_widget.h"
namespace Ui::Premium {
namespace {
constexpr auto kStarsCount = 16;
constexpr auto kTravelMax = 0.5;
constexpr auto kExcludeRadius = 0.7;
constexpr auto kFading = crl::time(200);
constexpr auto kLifetimeMin = crl::time(1000);
constexpr auto kLifetimeMax = 3 * kLifetimeMin;
constexpr auto kSizeMin = 0.1;
constexpr auto kSizeMax = 0.15;
[[nodiscard]] crl::time ChooseLife(base::BufferedRandom<uint32> &random) {
return kLifetimeMin
+ base::RandomIndex(kLifetimeMax - kLifetimeMin, random);
}
class CollectibleEmoji final : public Text::CustomEmoji {
public:
CollectibleEmoji(
QStringView entityData,
QColor centerColor,
QColor edgeColor,
std::unique_ptr<Ui::Text::CustomEmoji> inner,
Fn<void()> update,
int size);
int width() override;
QString entityData() override;
void paint(QPainter &p, const Context &context) override;
void unload() override;
bool ready() override;
bool readyInDefaultState() override;
private:
struct Star {
QPointF start;
QPointF delta;
float64 size = 0.;
crl::time birthTime = 0;
crl::time deathTime = 0;
};
void fill();
void refill(Star &star, base::BufferedRandom<uint32> &random);
void prepareFrame();
QString _entityData;
QSvgRenderer _svg;
std::vector<Star> _stars;
QColor _centerColor;
QColor _edgeColor;
std::unique_ptr<Text::CustomEmoji> _inner;
Ui::Animations::Basic _animation;
QImage _frame;
int _size = 0;
};
CollectibleEmoji::CollectibleEmoji(
QStringView entityData,
QColor centerColor,
QColor edgeColor,
std::unique_ptr<Ui::Text::CustomEmoji> inner,
Fn<void()> update,
int size)
: _entityData(entityData.toString())
, _svg(u":/gui/icons/settings/starmini.svg"_q)
, _centerColor(centerColor)
, _edgeColor(edgeColor)
, _inner(std::move(inner))
, _animation(std::move(update))
, _size(size) {
fill();
}
void CollectibleEmoji::fill() {
_stars.reserve(kStarsCount);
const auto now = crl::now();
auto random = base::BufferedRandom<uint32>(kStarsCount * 12);
for (auto i = 0; i != kStarsCount; ++i) {
const auto life = ChooseLife(random);
const auto shift = base::RandomIndex(life - kFading, random);
_stars.push_back({
.birthTime = now - crl::time(shift),
.deathTime = now - crl::time(shift) + crl::time(life),
});
refill(_stars.back(), random);
}
}
void CollectibleEmoji::refill(
Star &star,
base::BufferedRandom<uint32> &random) {
const auto take = [&] {
return base::RandomIndex(_size * 16, random) / (_size * 15.);
};
const auto stake = [&] {
return take() * 2. - 1.;
};
const auto exclude = kExcludeRadius * kExcludeRadius;
auto square = 0.;
while (true) {
const auto start = QPointF(stake(), stake());
square = start.x() * start.x() + start.y() * start.y();
if (square > exclude) {
star.start = start * _size;
break;
}
}
square *= _size * _size;
while (true) {
const auto delta = QPointF(stake(), stake()) * kTravelMax * _size;
const auto end = star.start + delta;
if (end.x() * end.x() + end.y() * end.y() > square) {
star.delta = delta;
break;
}
}
star.start = (star.start + QPointF(_size, _size)) / 2.;
star.size = (kSizeMin + (kSizeMax - kSizeMin) * take()) * _size;
}
int CollectibleEmoji::width() {
return _inner->width();
}
QString CollectibleEmoji::entityData() {
return _entityData;
}
void CollectibleEmoji::prepareFrame() {
const auto clip = QSize(_size, _size);
if (_frame.isNull()) {
const auto ratio = style::DevicePixelRatio();
_frame = QImage(clip * ratio, QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(ratio);
}
_frame.fill(Qt::transparent);
auto p = QPainter(&_frame);
auto hq = PainterHighQualityEnabler(p);
auto random = std::optional<base::BufferedRandom<uint32>>();
const auto now = crl::now();
for (auto &star : _stars) {
if (star.deathTime <= now) {
if (!random) {
random.emplace(kStarsCount * 10);
}
const auto life = ChooseLife(*random);
star.birthTime = now;
star.deathTime = now + crl::time(life);
refill(star, *random);
}
Assert(star.birthTime <= now && now <= star.deathTime);
const auto time = (now - star.birthTime)
/ float64(star.deathTime - star.birthTime);
const auto position = star.start + star.delta * time;
const auto scale = ((now - star.birthTime) < kFading)
? ((now - star.birthTime) / float64(kFading))
: ((star.deathTime - now) < kFading)
? ((star.deathTime - now) / float64(kFading))
: 1.;
if (scale > 0.) {
const auto size = star.size * scale;
const auto rect = QRectF(
position - QPointF(size, size),
QSizeF(size, size) * 2);
_svg.render(&p, rect);
}
}
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
auto gradient = QRadialGradient(
QRect(QPoint(), clip).center(),
clip.height() / 2);
gradient.setStops({
{ 0., _centerColor },
{ 1., _edgeColor },
});
p.setBrush(gradient);
p.setPen(Qt::NoPen);
p.drawRect(QRect(QPoint(), clip));
}
void CollectibleEmoji::paint(QPainter &p, const Context &context) {
prepareFrame();
p.drawImage(context.position, _frame);
if (context.paused) {
_animation.stop();
} else if (!_animation.animating()) {
_animation.start();
}
_inner->paint(p, context);
}
void CollectibleEmoji::unload() {
_inner->unload();
}
bool CollectibleEmoji::ready() {
return _inner->ready();
}
bool CollectibleEmoji::readyInDefaultState() {
return _inner->readyInDefaultState();
}
} // namespace
ColoredMiniStars::ColoredMiniStars(
not_null<Ui::RpWidget*> parent,
@ -113,66 +323,7 @@ std::unique_ptr<Text::CustomEmoji> MakeCollectibleEmoji(
std::unique_ptr<Text::CustomEmoji> inner,
Fn<void()> update,
int size) {
class Emoji final : public Text::CustomEmoji {
public:
Emoji(
QStringView entityData,
QColor centerColor,
QColor edgeColor,
std::unique_ptr<Ui::Text::CustomEmoji> inner,
Fn<void()> update,
int size)
: _entityData(entityData.toString())
, _stars([=](QRect) { update(); }, MiniStars::Type::SlowStars)
, _centerColor(centerColor)
, _edgeColor(edgeColor)
, _inner(std::move(inner))
, _size(size) {
_stars.setColorOverride(QGradientStops{
{ 0., edgeColor },
{ 1., centerColor },
});
_stars.setSize(QSize(size, size));
}
int width() override {
return _inner->width();
}
QString entityData() override {
return _entityData;
}
void paint(QPainter &p, const Context &context) override {
_stars.setPosition(context.position);
_stars.paint(p);
_inner->paint(p, context);
}
void unload() override {
_inner->unload();
}
bool ready() override {
return _inner->ready();
}
bool readyInDefaultState() override {
return _inner->readyInDefaultState();
}
private:
QString _entityData;
ColoredMiniStars _stars;
QColor _centerColor;
QColor _edgeColor;
std::unique_ptr<Text::CustomEmoji> _inner;
int _size = 0;
};
return std::make_unique<Emoji>(
return std::make_unique<CollectibleEmoji>(
entityData,
centerColor,
edgeColor,