diff --git a/Telegram/Resources/icons/passport_ready.png b/Telegram/Resources/icons/passport_ready.png new file mode 100644 index 000000000..2ba5de37a Binary files /dev/null and b/Telegram/Resources/icons/passport_ready.png differ diff --git a/Telegram/Resources/icons/passport_ready@2x.png b/Telegram/Resources/icons/passport_ready@2x.png new file mode 100644 index 000000000..f7a62efd5 Binary files /dev/null and b/Telegram/Resources/icons/passport_ready@2x.png differ diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index a1c5f3e0a..06bbcfcc0 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -720,7 +720,7 @@ proxyRowSkip: 2px; proxyRowRipple: RippleAnimation(defaultRippleAnimation) { color: windowBgOver; } -proxyRowSelectedIcon: icon {{ "mediaview_save_check", windowActiveTextFg }}; +proxyRowSelectedIcon: icon {{ "passport_ready", windowActiveTextFg }}; proxyRowTitleFg: windowFg; proxyRowTitlePalette: TextPalette(defaultTextPalette) { linkFg: windowSubTextFg; @@ -765,3 +765,10 @@ proxyEmptyListLabel: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; } proxyEmptyListPadding: margins(22px, 48px, 22px, 0px); +proxyCheckingPosition: point(2px, 5px); +proxyCheckingSkip: 6px; +proxyCheckingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) { + color: windowSubTextFg; + thickness: 1px; + size: size(8px, 8px); +} diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 66d6557cb..07a86f5f0 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/toast/toast.h" +#include "ui/effects/radial_animation.h" #include "ui/text_options.h" #include "history/history_location_manager.h" #include "application.h" @@ -59,6 +60,7 @@ protected: private: void setupControls(View &&view); int countAvailableWidth() const; + void step_radial(TimeMs ms, bool timer); View _view; @@ -66,6 +68,7 @@ private: object_ptr> _edit; object_ptr> _delete; object_ptr> _restore; + std::unique_ptr _progress; int _skipLeft = 0; int _skipRight = 0; @@ -191,11 +194,33 @@ void ProxyRow::updateFields(View &&view) { _edit->toggle(!_view.deleted, anim::type::instant); _restore->toggle(_view.deleted, anim::type::instant); + const auto state = _view.state; + if (state == State::Connecting || state == State::Checking) { + if (!_progress) { + _progress = std::make_unique( + animation(this, &ProxyRow::step_radial)); + _progress->start(); + } + } else { + _progress = nullptr; + } + setPointerCursor(!_view.deleted); update(); } +void ProxyRow::step_radial(TimeMs ms, bool timer) { + if (timer) { + update(); + } else if (_progress) { + _progress->update(false, ms); + if (!_progress->animating()) { + _progress = nullptr; + } + } +} + int ProxyRow::resizeGetHeight(int newWidth) { const auto result = st::proxyRowPadding.top() + st::semiboldFont->height @@ -226,8 +251,8 @@ int ProxyRow::resizeGetHeight(int newWidth) { void ProxyRow::paintEvent(QPaintEvent *e) { Painter p(this); + const auto ms = getms(); if (!_view.deleted) { - const auto ms = getms(); paintRipple(p, 0, 0, ms); } @@ -282,9 +307,25 @@ void ProxyRow::paintEvent(QPaintEvent *e) { }(); p.setPen(statusFg); p.setFont(st::normalFont); - p.drawTextLeft(left, top, width(), status); - top += st::normalFont->height + st::proxyRowPadding.bottom(); + auto statusLeft = left; + if (_progress) { + _progress->step(ms); + if (_progress) { + _progress->draw( + p, + { + st::proxyCheckingPosition.x() + statusLeft, + st::proxyCheckingPosition.y() + top }, + width(), + st::proxyCheckingAnimation); + statusLeft += st::proxyCheckingPosition.x() + + st::proxyCheckingAnimation.size.width() + + st::proxyCheckingSkip; + } + } + p.drawTextLeft(statusLeft, top, width(), status); + top += st::normalFont->height + st::proxyRowPadding.bottom(); } ProxiesBox::ProxiesBox( diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.cpp b/Telegram/SourceFiles/ui/effects/radial_animation.cpp index 34ebeab5c..fbf4cb5ee 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/radial_animation.cpp @@ -7,11 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/effects/radial_animation.h" +#include "styles/style_widgets.h" + namespace Ui { RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks) - : a_arcStart(0, FullArcLength) - , _animation(std::move(callbacks)) { +: a_arcStart(0, FullArcLength) +, _animation(std::move(callbacks)) { } void RadialAnimation::start(float64 prg) { @@ -82,4 +84,96 @@ void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, styl p.setOpacity(o); } +InfiniteRadialAnimation::InfiniteRadialAnimation(AnimationCallbacks &&callbacks) +: _animation(std::move(callbacks)) { +} + +void InfiniteRadialAnimation::start() { + _start = _changed = getms(); + _finished = false; + _animation.start(); +} + +void InfiniteRadialAnimation::update(bool finished, TimeMs ms) { + if (_finished != finished) { + _finished = finished; + _changed = ms; + } + + auto dt = float64(ms - _changed); + auto fulldt = float64(ms - _start); + _opacity = qMin(fulldt / st::radialDuration, 1.); + if (!finished) { + } else if (dt >= st::radialDuration) { + stop(); + } else { + auto r = dt / st::radialDuration; + _opacity *= 1 - r; + } +} + +void InfiniteRadialAnimation::stop() { + _start = _changed = 0; + _animation.stop(); +} + +void InfiniteRadialAnimation::step(TimeMs ms) { + _animation.step(ms); +} + +void InfiniteRadialAnimation::draw( + Painter &p, + QPoint position, + int outerWidth, + const style::InfiniteRadialAnimation &st) { + auto o = p.opacity(); + p.setOpacity(o * _opacity); + + auto pen = st.color->p; + auto was = p.pen(); + pen.setWidth(st.thickness); + pen.setCapStyle(Qt::RoundCap); + p.setPen(pen); + + const auto time = (getms() - _start); + const auto linear = (time * FullArcLength) / st.linearPeriod; + const auto frontPeriods = time / st.sinePeriod; + const auto frontCurrent = time % st.sinePeriod; + const auto frontProgress = anim::sineInOut( + st.arcMax - st.arcMin, + std::min(frontCurrent, TimeMs(st.sineDuration)) + / float64(st.sineDuration)); + const auto backTime = std::max(time - st.sineShift, 0LL); + const auto backPeriods = backTime / st.sinePeriod; + const auto backCurrent = backTime % st.sinePeriod; + const auto backProgress = anim::sineInOut( + st.arcMax - st.arcMin, + std::min(backCurrent, TimeMs(st.sineDuration)) + / float64(st.sineDuration)); + const auto front = linear + std::round((st.arcMin + frontProgress + frontPeriods * (st.arcMax - st.arcMin)) * FullArcLength); + const auto from = linear + std::round((backProgress + backPeriods * (st.arcMax - st.arcMin)) * FullArcLength); + const auto len = (front - from); + + //if (rtl()) { + // from = QuarterArcLength - (from - QuarterArcLength) - len; + // if (from < 0) from += FullArcLength; + //} + + { + PainterHighQualityEnabler hq(p); + p.drawArc( + rtlrect( + position.x(), + position.y(), + st.size.width(), + st.size.height(), + outerWidth), + from, + len); + } + + p.setPen(was); + p.setOpacity(o); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/radial_animation.h b/Telegram/SourceFiles/ui/effects/radial_animation.h index ac255aaae..b3c0f28bd 100644 --- a/Telegram/SourceFiles/ui/effects/radial_animation.h +++ b/Telegram/SourceFiles/ui/effects/radial_animation.h @@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace style { +struct InfiniteRadialAnimation; +} // namespace style + namespace Ui { class RadialAnimation { @@ -42,4 +46,39 @@ private: }; +class InfiniteRadialAnimation { +public: + InfiniteRadialAnimation(AnimationCallbacks &&callbacks); + + float64 opacity() const { + return _opacity; + } + bool animating() const { + return _animation.animating(); + } + + void start(); + void update(bool finished, TimeMs ms); + void stop(); + + void step(TimeMs ms); + void step() { + step(getms()); + } + + void draw( + Painter &p, + QPoint position, + int outerWidth, + const style::InfiniteRadialAnimation &st); + +private: + float64 _opacity = 0.; + TimeMs _start = 0; + TimeMs _changed = 0; + bool _finished = false; + BasicAnimation _animation; + +}; + } // namespace Ui diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index c60cdec77..48f2bd794 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -37,6 +37,27 @@ RippleAnimation { hideDuration: int; } +InfiniteRadialAnimation { + color: color; + thickness: pixels; + size: size; + linearPeriod: int; + sinePeriod: int; + sineDuration: int; + sineShift: int; + arcMin: double; + arcMax: double; +} + +defaultInfiniteRadialAnimation: InfiniteRadialAnimation { + linearPeriod: 1000; + sinePeriod: 3000; + sineDuration: 1000; + sineShift: 1500; + arcMin: 0.0625; + arcMax: 0.75; +} + FlatButton { color: color; overColor: color;