Added ministar particles to premium bubbles with Credits type.

This commit is contained in:
23rd
2025-11-06 13:05:59 +03:00
parent f1ceb1c95a
commit 0c701d9d95
2 changed files with 142 additions and 50 deletions
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/object_ptr.h"
#include "lang/lang_keys.h"
#include "ui/effects/gradient.h"
#include "ui/effects/ministar_particles.h"
#include "ui/effects/premium_graphics.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/vertical_layout.h"
@@ -151,53 +152,72 @@ void Bubble::setFlipHorizontal(bool value) {
_flipHorizontal = value;
}
QRect Bubble::bubbleGeometry(const QRect &r) const {
const auto penWidth = _st.penWidth;
const auto penWidthHalf = penWidth / 2;
return r - style::margins(
penWidthHalf,
penWidthHalf,
penWidthHalf,
_st.tailSize.height() + penWidthHalf);
}
QPainterPath Bubble::bubblePath(const QRect &r) const {
const auto bubbleRect = bubbleGeometry(r);
const auto radius = bubbleRadius();
auto pathTail = QPainterPath();
const auto tailWHalf = _st.tailSize.width() / 2.;
const auto progress = _tailEdge;
const auto tailTop = bubbleRect.y() + bubbleRect.height();
const auto tailLeftFull = bubbleRect.x()
+ (bubbleRect.width() * 0.5)
- tailWHalf;
const auto tailLeft = bubbleRect.x()
+ (bubbleRect.width() * 0.5 * (progress + 1.))
- tailWHalf;
const auto tailCenter = tailLeft + tailWHalf;
const auto tailRight = [&] {
const auto max = bubbleRect.x() + bubbleRect.width();
const auto right = tailLeft + _st.tailSize.width();
const auto bottomMax = max - radius;
return (right > bottomMax)
? std::max(float64(tailCenter), float64(bottomMax))
: right;
}();
if (_hasTail) {
pathTail.moveTo(tailLeftFull, tailTop);
pathTail.lineTo(tailLeft, tailTop);
pathTail.lineTo(tailCenter, tailTop + _st.tailSize.height());
pathTail.lineTo(tailRight, tailTop);
pathTail.lineTo(tailRight, tailTop - radius);
pathTail.moveTo(tailLeftFull, tailTop);
}
auto pathBubble = QPainterPath();
pathBubble.setFillRule(Qt::WindingFill);
pathBubble.addRoundedRect(bubbleRect, radius, radius);
auto result = pathTail + pathBubble;
if (_flipHorizontal) {
auto m = QTransform();
const auto center = QRectF(bubbleRect).center();
m.translate(center.x(), center.y());
m.scale(-1., 1.);
m.translate(-center.x(), -center.y());
return m.map(result);
}
return result;
}
void Bubble::paintBubble(QPainter &p, const QRect &r, const QBrush &brush) {
if (!_counter.has_value()) {
return;
}
const auto bubbleRect = bubbleGeometry(r);
const auto penWidth = _st.penWidth;
const auto penWidthHalf = penWidth / 2;
const auto bubbleRect = r - style::margins(
penWidthHalf,
penWidthHalf,
penWidthHalf,
_st.tailSize.height() + penWidthHalf);
{
const auto radius = bubbleRadius();
auto pathTail = QPainterPath();
const auto tailWHalf = _st.tailSize.width() / 2.;
const auto progress = _tailEdge;
const auto tailTop = bubbleRect.y() + bubbleRect.height();
const auto tailLeftFull = bubbleRect.x()
+ (bubbleRect.width() * 0.5)
- tailWHalf;
const auto tailLeft = bubbleRect.x()
+ (bubbleRect.width() * 0.5 * (progress + 1.))
- tailWHalf;
const auto tailCenter = tailLeft + tailWHalf;
const auto tailRight = [&] {
const auto max = bubbleRect.x() + bubbleRect.width();
const auto right = tailLeft + _st.tailSize.width();
const auto bottomMax = max - radius;
return (right > bottomMax)
? std::max(float64(tailCenter), float64(bottomMax))
: right;
}();
if (_hasTail) {
pathTail.moveTo(tailLeftFull, tailTop);
pathTail.lineTo(tailLeft, tailTop);
pathTail.lineTo(tailCenter, tailTop + _st.tailSize.height());
pathTail.lineTo(tailRight, tailTop);
pathTail.lineTo(tailRight, tailTop - radius);
pathTail.moveTo(tailLeftFull, tailTop);
}
auto pathBubble = QPainterPath();
pathBubble.setFillRule(Qt::WindingFill);
pathBubble.addRoundedRect(bubbleRect, radius, radius);
auto hq = PainterHighQualityEnabler(p);
p.setPen(QPen(
brush,
@@ -206,16 +226,7 @@ void Bubble::paintBubble(QPainter &p, const QRect &r, const QBrush &brush) {
Qt::RoundCap,
Qt::RoundJoin));
p.setBrush(brush);
if (_flipHorizontal) {
auto m = QTransform();
const auto center = QRectF(bubbleRect).center();
m.translate(center.x(), center.y());
m.scale(-1., 1.);
m.translate(-center.x(), -center.y());
p.drawPath(m.map(pathTail + pathBubble));
} else {
p.drawPath(pathTail + pathBubble);
}
p.drawPath(bubblePath(r));
}
p.setPen(st::activeButtonFg);
p.setFont(_st.font);
@@ -268,6 +279,9 @@ BubbleWidget::BubbleWidget(
, _deflection(kDeflection)
, _stepBeforeDeflection(kStepBeforeDeflection)
, _stepAfterDeflection(kStepAfterDeflection) {
if (_type == BubbleType::Credits) {
setupParticles(parent);
}
const auto resizeTo = [=](int w, int h) {
_deflection = (w > _st.widthLimit)
? kDeflectionSmall
@@ -303,6 +317,65 @@ BubbleWidget::BubbleWidget(
}, lifetime());
}
void BubbleWidget::setupParticles(not_null<Ui::RpWidget*> parent) {
_particles.emplace(StarParticles::Type::Radial, 50, st::lineWidth * 4);
_particles->setSpeed(0.1);
_particlesWidget = Ui::CreateChild<Ui::RpWidget>(parent);
_particlesWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
_particlesWidget->show();
_particlesWidget->raise();
_particlesAnimation.init([=] { _particlesWidget->update(); });
_particlesWidget->paintRequest() | rpl::start_with_next([=] {
if (!_particlesAnimation.animating()) {
_particlesAnimation.start();
}
auto p = QPainter(_particlesWidget);
auto hq = PainterHighQualityEnabler(p);
const auto offset = QPoint(
(_particlesWidget->width() - width()) / 2,
(_particlesWidget->height() - height()) / 2);
const auto bubbleRect = rect().translated(offset)
- QMargins(
_spaceForDeflection.width(),
_spaceForDeflection.height(),
_spaceForDeflection.width(),
_spaceForDeflection.height());
p.save();
p.translate(offset);
const auto bubblePath = _bubble.bubblePath(bubbleRect);
p.restore();
auto fullRect = QPainterPath();
fullRect.addRect(QRectF(_particlesWidget->rect()));
p.setClipPath(bubblePath);
_particles->setColor(st::premiumButtonFg->c);
_particles->paint(p, _particlesWidget->rect(), crl::now());
p.setClipping(false);
p.setClipPath(fullRect.subtracted(bubblePath));
_particles->setColor(st::creditsBg3->c);
_particles->paint(p, _particlesWidget->rect(), crl::now());
}, _particlesWidget->lifetime());
geometryValue() | rpl::start_with_next([=](QRect geometry) {
const auto particlesSize = QSize(
int(geometry.width() * 1.5),
int(geometry.height() * 1.5));
const auto center = geometry.center();
_particlesWidget->setGeometry(
center.x() - particlesSize.width() / 2,
center.y() - particlesSize.height() / 2,
particlesSize.width(),
particlesSize.height());
}, lifetime());
}
void BubbleWidget::animateTo(BubbleRowState state) {
const auto targetWidth = _bubble.countTargetWidth(state.counter);
const auto parent = parentWidget();
@@ -399,6 +472,11 @@ void BubbleWidget::animateTo(BubbleRowState state) {
+ counterProgress * (state.counter - _animatingFrom.counter);
_bubble.setCounter(int(base::SafeRound(now)));
if (_particles) {
const auto progress = now / float(state.counter);
_particles->setSpeed(0.01 + progress * 0.25);
}
_bubble.setFlipHorizontal(nowBubbleEdge < 0);
_bubble.setTailEdge(std::abs(nowBubbleEdge));
update();
@@ -479,6 +557,10 @@ void BubbleWidget::paintEvent(QPaintEvent *e) {
}());
}
void BubbleWidget::resizeEvent(QResizeEvent *e) {
RpWidget::resizeEvent(e);
}
void AddBubbleRow(
not_null<Ui::VerticalLayout*> parent,
const style::PremiumBubble &st,
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "ui/effects/numbers_animation.h"
#include "ui/effects/ministar_particles.h"
#include "ui/rp_widget.h"
enum lngtag_count : int;
@@ -56,11 +57,13 @@ public:
[[nodiscard]] int bubbleRadius() const;
[[nodiscard]] int countMaxWidth(int maxPossibleCounter) const;
[[nodiscard]] int countTargetWidth(int targetCounter) const;
[[nodiscard]] QRect bubbleGeometry(const QRect &r) const;
void setCounter(int value);
void setTailEdge(EdgeProgress edge);
void setFlipHorizontal(bool value);
void paintBubble(QPainter &p, const QRect &r, const QBrush &brush);
[[nodiscard]] QPainterPath bubblePath(const QRect &r) const;
[[nodiscard]] rpl::producer<> widthChanges() const;
@@ -117,8 +120,11 @@ public:
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void setupParticles(not_null<Ui::RpWidget*> parent);
struct GradientParams {
int left = 0;
int width = 0;
@@ -151,6 +157,10 @@ private:
float64 _stepBeforeDeflection;
float64 _stepAfterDeflection;
RpWidget *_particlesWidget = nullptr;
std::optional<StarParticles> _particles;
Ui::Animations::Basic _particlesAnimation;
};
void AddBubbleRow(