diff --git a/Telegram/SourceFiles/ui/effects/premium_bubble.cpp b/Telegram/SourceFiles/ui/effects/premium_bubble.cpp index 929db929a2..7e5025cc70 100644 --- a/Telegram/SourceFiles/ui/effects/premium_bubble.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_bubble.cpp @@ -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 parent) { + _particles.emplace(StarParticles::Type::Radial, 50, st::lineWidth * 4); + _particles->setSpeed(0.1); + + _particlesWidget = Ui::CreateChild(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 parent, const style::PremiumBubble &st, diff --git a/Telegram/SourceFiles/ui/effects/premium_bubble.h b/Telegram/SourceFiles/ui/effects/premium_bubble.h index be9c1525f7..ec3e83a7ec 100644 --- a/Telegram/SourceFiles/ui/effects/premium_bubble.h +++ b/Telegram/SourceFiles/ui/effects/premium_bubble.h @@ -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 parent); + struct GradientParams { int left = 0; int width = 0; @@ -151,6 +157,10 @@ private: float64 _stepBeforeDeflection; float64 _stepAfterDeflection; + RpWidget *_particlesWidget = nullptr; + std::optional _particles; + Ui::Animations::Basic _particlesAnimation; + }; void AddBubbleRow(