mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Improve stars in collectible emoji status.
This commit is contained in:
parent
6d7abd1718
commit
e3517aceab
2 changed files with 221 additions and 73 deletions
|
@ -2387,8 +2387,6 @@ not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
|
||||||
EmojiStatusId id,
|
EmojiStatusId id,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
uint64 setId) {
|
uint64 setId) {
|
||||||
Expects(document->sticker() != nullptr);
|
|
||||||
|
|
||||||
const auto documentId = document->id;
|
const auto documentId = document->id;
|
||||||
const auto i = _customEmoji.find(id);
|
const auto i = _customEmoji.find(id);
|
||||||
const auto recentOnly = (i != end(_customEmoji)) && i->second.recentOnly;
|
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 type = Data::EmojiStatuses::Type::Collectibles;
|
||||||
const auto &list = session().data().emojiStatuses().list(type);
|
const auto &list = session().data().emojiStatuses().list(type);
|
||||||
if (list.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto setId = Data::Stickers::CollectibleSetId;
|
const auto setId = Data::Stickers::CollectibleSetId;
|
||||||
auto set = std::vector<CustomOne>();
|
auto set = std::vector<CustomOne>();
|
||||||
set.reserve(list.size());
|
set.reserve(list.size());
|
||||||
|
@ -2480,14 +2475,16 @@ void EmojiListWidget::refreshEmojiStatusCollectibles() {
|
||||||
? status.collectible->documentId
|
? status.collectible->documentId
|
||||||
: status.documentId;
|
: status.documentId;
|
||||||
const auto document = session().data().document(documentId);
|
const auto document = session().data().document(documentId);
|
||||||
if (const auto sticker = document->sticker()) {
|
const auto sticker = document->sticker();
|
||||||
set.push_back({
|
set.push_back({
|
||||||
.collectible = status.collectible,
|
.collectible = status.collectible,
|
||||||
.custom = resolveCustomEmoji(status, document, setId),
|
.custom = resolveCustomEmoji(status, document, setId),
|
||||||
.document = document,
|
.document = document,
|
||||||
.emoji = Ui::Emoji::Find(sticker->alt),
|
.emoji = sticker ? Ui::Emoji::Find(sticker->alt) : nullptr,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (set.empty()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const auto collectibles = session().data().stickers().collectibleSet();
|
const auto collectibles = session().data().stickers().collectibleSet();
|
||||||
_custom.push_back({
|
_custom.push_back({
|
||||||
|
|
|
@ -7,11 +7,221 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "ui/effects/premium_stars_colored.h"
|
#include "ui/effects/premium_stars_colored.h"
|
||||||
|
|
||||||
|
#include "base/random.h"
|
||||||
#include "ui/effects/premium_graphics.h" // GiftGradientStops.
|
#include "ui/effects/premium_graphics.h" // GiftGradientStops.
|
||||||
#include "ui/text/text_custom_emoji.h"
|
#include "ui/text/text_custom_emoji.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
namespace Ui::Premium {
|
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(
|
ColoredMiniStars::ColoredMiniStars(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
@ -113,66 +323,7 @@ std::unique_ptr<Text::CustomEmoji> MakeCollectibleEmoji(
|
||||||
std::unique_ptr<Text::CustomEmoji> inner,
|
std::unique_ptr<Text::CustomEmoji> inner,
|
||||||
Fn<void()> update,
|
Fn<void()> update,
|
||||||
int size) {
|
int size) {
|
||||||
class Emoji final : public Text::CustomEmoji {
|
return std::make_unique<CollectibleEmoji>(
|
||||||
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>(
|
|
||||||
entityData,
|
entityData,
|
||||||
centerColor,
|
centerColor,
|
||||||
edgeColor,
|
edgeColor,
|
||||||
|
|
Loading…
Add table
Reference in a new issue