diff --git a/Telegram/Resources/icons/settings/starmini.svg b/Telegram/Resources/icons/settings/starmini.svg new file mode 100644 index 000000000..13e53a95d --- /dev/null +++ b/Telegram/Resources/icons/settings/starmini.svg @@ -0,0 +1,7 @@ + + +ministar + + + + diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index 75cc1293a..bfcf00c99 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -27,6 +27,7 @@ ../../art/recording/recording_info_video_portrait.svg ../../icons/settings/dino.svg ../../icons/settings/star.svg + ../../icons/settings/starmini.svg ../../icons/calls/hands.lottie diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index 8d87457d4..51330b1cb 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_premium.h" +#include "base/random.h" #include "core/application.h" #include "info/info_wrap_widget.h" // Info::Wrap. #include "info/settings/info_settings_widget.h" // SectionCustomTopBarData. @@ -16,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_premium.h" #include "ui/abstract_button.h" #include "ui/basic_click_handlers.h" +#include "ui/effects/animation_value_f.h" #include "ui/effects/gradient.h" #include "ui/effects/premium_graphics.h" #include "ui/text/text_utilities.h" @@ -209,6 +211,140 @@ void SendScreenAccept(not_null controller) { MTP_jsonNull()); } +class MiniStars final { +public: + MiniStars(Fn updateCallback); + + void paint(Painter &p, const QRectF &rect); + +private: + void createStar(crl::time now); + int angle() const; + + struct MiniStar { + crl::time birthTime = 0; + crl::time deathTime = 0; + int angle = 0; + float64 size = 0.; + float64 alpha = 0.; + }; + + struct Interval { + int from; + int length; + }; + const std::vector _availableAngles; + const Interval _lifeLength; + const Interval _deathTime; + const Interval _size; + const Interval _alpha; + + const float64 _appearProgressTill; + const float64 _disappearProgressAfter; + const float64 _distanceProgressStart; + + QSvgRenderer _sprite; + + Ui::Animations::Basic _animation; + + std::vector _ministars; + + crl::time _nextBirthTime = 0; + +}; + +MiniStars::MiniStars(Fn updateCallback) +: _availableAngles({ + Interval{ -10, 40 }, + Interval{ 180 + 10 - 40, 40 }, + Interval{ 180 + 15, 50 }, + Interval{ -15 - 50, 50 }, +}) +, _lifeLength({ 150, 200 }) +, _deathTime({ 1500, 2000 }) +, _size({ 10, 20 }) +, _alpha({ 40, 60 }) +, _appearProgressTill(0.2) +, _disappearProgressAfter(0.8) +, _distanceProgressStart(0.5) +, _sprite(u":/gui/icons/settings/starmini.svg"_q) +, _animation([=](crl::time now) { + if (now > _nextBirthTime) { + createStar(now); + _nextBirthTime = now + + _lifeLength.from + + base::RandomIndex(_lifeLength.length); + } + updateCallback(); +}) { + _animation.start(); +} + +void MiniStars::paint(Painter &p, const QRectF &rect) { + const auto center = rect.center(); + const auto opacity = p.opacity(); + for (const auto &ministar : _ministars) { + const auto progress = (crl::now() - ministar.birthTime) + / float64(ministar.deathTime - ministar.birthTime); + if (progress > 1.) { + continue; + } + const auto appearProgress = std::clamp( + progress / _appearProgressTill, + 0., + 1.); + const auto rsin = float(std::sin(ministar.angle * M_PI / 180.)); + const auto rcos = float(std::cos(ministar.angle * M_PI / 180.)); + const auto end = QPointF( + rect.width() / 1.5 * rcos, + rect.height() / 1.5 * rsin); + + const auto alphaProgress = 1. + - (std::clamp(progress - _disappearProgressAfter, 0., 1.) + / (1. - _disappearProgressAfter)); + p.setOpacity(ministar.alpha * alphaProgress * appearProgress); + + const auto distanceProgress = _distanceProgressStart + progress; + const auto starSize = ministar.size * appearProgress; + _sprite.render(&p, QRectF( + center.x() + + anim::interpolateF(0, end.x(), distanceProgress) + - starSize / 2., + center.y() + + anim::interpolateF(0, end.y(), distanceProgress) + - starSize / 2., + starSize, + starSize)); + } + p.setOpacity(opacity); +} + +int MiniStars::angle() const { + const auto &interval = _availableAngles[ + base::RandomIndex(_availableAngles.size())]; + return base::RandomIndex(interval.length) + interval.from; +} + +void MiniStars::createStar(crl::time now) { + auto ministar = MiniStar{ + .birthTime = now, + .deathTime = now + + _deathTime.from + + base::RandomIndex(_deathTime.length), + .angle = angle(), + .size = float64(_size.from + base::RandomIndex(_size.length)), + .alpha = float64(_alpha.from + base::RandomIndex(_alpha.length)) + / 100., + }; + for (auto i = 0; i < _ministars.size(); i++) { + if (ministar.birthTime > _ministars[i].deathTime) { + _ministars[i] = ministar; + return; + } + } + _ministars.push_back(ministar); +} + class TopBar final : public Ui::RpWidget { public: TopBar(not_null parent); @@ -220,6 +356,7 @@ protected: void paintEvent(QPaintEvent *e) override; private: + MiniStars _ministars; QSvgRenderer _star; Ui::Text::String _title; Ui::Text::String _about; @@ -231,6 +368,7 @@ private: TopBar::TopBar(not_null parent) : Ui::RpWidget(parent) +, _ministars([=] { update(); }) , _star(u":/gui/icons/settings/star.svg"_q) , _title(st::boxTitle.style, tr::lng_premium_summary_title(tr::now)) { _about.setMarkedText( @@ -296,6 +434,13 @@ void TopBar::paintEvent(QPaintEvent *e) { starSize); }; const auto currentStarRect = starRect(topProgress, bodyProgress); + + p.translate(currentStarRect.center()); + p.scale(bodyProgress, bodyProgress); + p.translate(-currentStarRect.center()); + _ministars.paint(p, starRect(topProgress, 1.)); + p.resetTransform(); + _star.render(&p, currentStarRect); p.setPen(st::premiumButtonFg);