From a0152557ecbf4539c7e01ec7b82abd51a1311ea6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 26 Nov 2019 17:27:09 +0300 Subject: [PATCH] Cross-fade login QR. --- Telegram/SourceFiles/intro/intro.style | 1 + Telegram/SourceFiles/intro/intro_qr.cpp | 60 ++++++++++++++------ Telegram/SourceFiles/intro/intro_qr.h | 2 + Telegram/SourceFiles/intro/intro_step.cpp | 14 ++--- Telegram/SourceFiles/intro/intro_step.h | 3 +- Telegram/SourceFiles/intro/introcode.cpp | 5 +- Telegram/SourceFiles/intro/introcode.h | 2 + Telegram/SourceFiles/intro/intropwdcheck.cpp | 5 +- Telegram/SourceFiles/intro/intropwdcheck.h | 2 + Telegram/SourceFiles/intro/introwidget.cpp | 23 ++++---- Telegram/SourceFiles/intro/introwidget.h | 2 +- 11 files changed, 78 insertions(+), 41 deletions(-) diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style index c9d4be939..26426f40d 100644 --- a/Telegram/SourceFiles/intro/intro.style +++ b/Telegram/SourceFiles/intro/intro.style @@ -174,6 +174,7 @@ introQrTitle: FlatLabel(defaultFlatLabel) { linkFontOver: font(20px semibold underline); } } +introQrErrorTop: 336px; introQrTitleTop: 196px; introQrStep: defaultFlatLabel; introQrStepsTop: 232px; diff --git a/Telegram/SourceFiles/intro/intro_qr.cpp b/Telegram/SourceFiles/intro/intro_qr.cpp index 908aedc9a..2eb46ef52 100644 --- a/Telegram/SourceFiles/intro/intro_qr.cpp +++ b/Telegram/SourceFiles/intro/intro_qr.cpp @@ -56,7 +56,14 @@ namespace { if (max > 0 && data.size * pixel > max) { pixel = std::max(max / data.size, 1); } - return TelegramQrExact(data, pixel * style::DevicePixelRatio()); + const auto qr = TelegramQrExact(data, pixel * style::DevicePixelRatio()); + auto result = QImage(qr.size(), QImage::Format_ARGB32_Premultiplied); + result.fill(st::windowBg->c); + { + auto p = QPainter(&result); + p.drawImage(QRect(QPoint(), qr.size()), qr); + } + return result; } [[nodiscard]] not_null PrepareQrWidget( @@ -67,8 +74,10 @@ namespace { : waiting(callback, st::defaultInfiniteRadialAnimation) { } + QImage previous; QImage qr; QImage center; + Ui::Animations::Simple shown; Ui::InfiniteRadialAnimation waiting; }; auto qrs = std::move( @@ -92,9 +101,15 @@ namespace { ) | rpl::map([](const Qr::Data &code, const auto &) { return TelegramQr(code, st::introQrPixel, st::introQrMaxSize); }) | rpl::start_with_next([=](QImage &&image) { + state->previous = std::move(state->qr); state->qr = std::move(image); state->waiting.stop(); - result->update(); + state->shown.stop(); + state->shown.start( + [=] { result->update(); }, + 0., + 1., + st::fadeWrapDuration); }, result->lifetime()); std::move( palettes @@ -106,7 +121,24 @@ namespace { result->paintRequest( ) | rpl::start_with_next([=](QRect clip) { auto p = QPainter(result); - auto rect = QRect( + const auto shown = state->qr.isNull() ? 0. : state->shown.value(1.); + if (!state->qr.isNull()) { + const auto size = state->qr.size() / cIntRetinaFactor(); + const auto qr = QRect( + (result->width() - size.width()) / 2, + (result->height() - size.height()) / 2, + size.width(), + size.height()); + if (shown == 1.) { + state->previous = QImage(); + } else if (!state->previous.isNull()) { + p.drawImage(qr, state->previous); + } + p.setOpacity(shown); + p.drawImage(qr, state->qr); + p.setOpacity(1.); + } + const auto rect = QRect( (result->width() - st::introQrCenterSize) / 2, (result->height() - st::introQrCenterSize) / 2, st::introQrCenterSize, @@ -119,7 +151,7 @@ namespace { auto pen = st::activeButtonBg->p; pen.setWidth(line); pen.setCapStyle(Qt::RoundCap); - p.setOpacity(radial.shown); + p.setOpacity(radial.shown * (1. - shown)); p.setPen(pen); p.drawArc( rect.marginsAdded({ line, line, line, line }), @@ -127,16 +159,6 @@ namespace { radial.arcLength); p.setOpacity(1.); } - if (!state->qr.isNull()) { - const auto size = state->qr.size() / cIntRetinaFactor(); - p.drawImage( - QRect( - (result->width() - size.width()) / 2, - (result->height() - size.height()) / 2, - size.width(), - size.height()), - state->qr); - } }, result->lifetime()); return result; } @@ -148,7 +170,7 @@ QrWidget::QrWidget( not_null account, not_null data) : Step(parent, account, data) - , _refreshTimer([=] { refreshCode(); }) { +, _refreshTimer([=] { refreshCode(); }) { setTitleText(rpl::single(QString())); setDescriptionText(rpl::single(QString())); setErrorCentered(true); @@ -163,6 +185,10 @@ QrWidget::QrWidget( refreshCode(); } +int QrWidget::errorTop() const { + return contentTop() + st::introQrErrorTop; +} + void QrWidget::checkForTokenUpdate(const MTPUpdates &updates) { updates.match([&](const MTPDupdateShort &data) { checkForTokenUpdate(data.vupdate()); @@ -256,9 +282,7 @@ void QrWidget::setupControls() { contentTop() + st::introQrSkipTop); }, skip->lifetime()); - skip->setClickedCallback([=] { - goNext(); - }); + skip->setClickedCallback([=] { submit(); }); } void QrWidget::refreshCode() { diff --git a/Telegram/SourceFiles/intro/intro_qr.h b/Telegram/SourceFiles/intro/intro_qr.h index fbcf1f5a9..0a97d263d 100644 --- a/Telegram/SourceFiles/intro/intro_qr.h +++ b/Telegram/SourceFiles/intro/intro_qr.h @@ -40,6 +40,8 @@ public: } private: + int errorTop() const override; + void setupControls(); void refreshCode(); void checkForTokenUpdate(const MTPUpdates &updates); diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp index 6afa818dc..0a5f3e2d3 100644 --- a/Telegram/SourceFiles/intro/intro_step.cpp +++ b/Telegram/SourceFiles/intro/intro_step.cpp @@ -179,11 +179,14 @@ void Step::updateLabelsPosition() { } Ui::SendPendingMoveResizeEvents(_error->entity()); auto errorLeft = _errorCentered ? 0 : (contentLeft() + st::buttonRadius); - auto errorTop = contentTop() + (_errorBelowLink ? st::introErrorBelowLinkTop : st::introErrorTop); - _error->moveToLeft(errorLeft, errorTop); + _error->moveToLeft(errorLeft, errorTop()); } } +int Step::errorTop() const { + return contentTop() + st::introErrorTop; +} + void Step::setTitleText(rpl::producer titleText) { _titleText = std::move(titleText); } @@ -364,13 +367,6 @@ void Step::setErrorCentered(bool centered) { _error.destroy(); } -void Step::setErrorBelowLink(bool below) { - _errorBelowLink = below; - if (_error) { - updateLabelsPosition(); - } -} - void Step::showError(rpl::producer text) { _errorText = std::move(text); } diff --git a/Telegram/SourceFiles/intro/intro_step.h b/Telegram/SourceFiles/intro/intro_step.h index 81ed97259..67e2ad225 100644 --- a/Telegram/SourceFiles/intro/intro_step.h +++ b/Telegram/SourceFiles/intro/intro_step.h @@ -78,7 +78,6 @@ public: [[nodiscard]] int contentTop() const; void setErrorCentered(bool centered); - void setErrorBelowLink(bool below); void showError(rpl::producer text); void hideError() { showError(rpl::single(QString())); @@ -128,6 +127,8 @@ protected: } } + virtual int errorTop() const; + private: struct CoverAnimation { CoverAnimation() = default; diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index d8d6fce51..c8ba9ab8a 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -95,7 +95,6 @@ CodeWidget::CodeWidget( _noTelegramCode->addClickHandler([=] { onNoTelegramCode(); }); _code->setDigitsCountMax(getData()->codeLength); - setErrorBelowLink(true); setTitleText(rpl::single(App::formatPhone(getData()->phone))); updateDescText(); @@ -107,6 +106,10 @@ void CodeWidget::refreshLang() { updateControlsGeometry(); } +int CodeWidget::errorTop() const { + return contentTop() + st::introErrorBelowLinkTop; +} + void CodeWidget::updateDescText() { const auto byTelegram = getData()->codeByTelegram; setDescriptionText( diff --git a/Telegram/SourceFiles/intro/introcode.h b/Telegram/SourceFiles/intro/introcode.h index 5aaaeb352..d02ed7d82 100644 --- a/Telegram/SourceFiles/intro/introcode.h +++ b/Telegram/SourceFiles/intro/introcode.h @@ -69,6 +69,8 @@ private slots: void onCheckRequest(); private: + int errorTop() const override; + void updateCallText(); void refreshLang(); void updateControlsGeometry(); diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index db2e807b2..0e5bad618 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -50,7 +50,6 @@ PwdCheckWidget::PwdCheckWidget( setTitleText(tr::lng_signin_title()); updateDescriptionText(); - setErrorBelowLink(true); if (_hint.isEmpty()) { _pwdHint->hide(); @@ -79,6 +78,10 @@ void PwdCheckWidget::refreshLang() { updateControlsGeometry(); } +int PwdCheckWidget::errorTop() const { + return contentTop() + st::introErrorBelowLinkTop; +} + void PwdCheckWidget::resizeEvent(QResizeEvent *e) { Step::resizeEvent(e); updateControlsGeometry(); diff --git a/Telegram/SourceFiles/intro/intropwdcheck.h b/Telegram/SourceFiles/intro/intropwdcheck.h index 5113781b5..02e392eae 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.h +++ b/Telegram/SourceFiles/intro/intropwdcheck.h @@ -46,6 +46,8 @@ private slots: void onCheckRequest(); private: + int errorTop() const override; + void showReset(); void refreshLang(); void updateControlsGeometry(); diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 98b6d5524..493c8913a 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -122,7 +122,7 @@ void Widget::createLanguageLink() { Lang::CurrentCloudManager().switchToLanguage(languageId); }); _changeLanguage->toggle( - !_resetAccount && !_terms, + !_resetAccount && !_terms && _nextShown, anim::type::normal); updateControlsGeometry(); }; @@ -230,11 +230,6 @@ void Widget::historyMove(Direction direction) { setupNextButton(); if (_resetAccount) _resetAccount->show(anim::type::normal); if (_terms) _terms->show(anim::type::normal); - if (_changeLanguage) { - _changeLanguage->toggle( - !_resetAccount && !_terms, - anim::type::normal); - } getStep()->showAnimated(direction); fixOrder(); } @@ -253,6 +248,7 @@ void Widget::hideAndDestroy(object_ptr> widget) { void Widget::fixOrder() { _next->raise(); if (_update) _update->raise(); + if (_changeLanguage) _changeLanguage->raise(); _settings->raise(); _back->raise(); _connecting->raise(); @@ -333,7 +329,7 @@ void Widget::showTerms() { } if (_changeLanguage) { _changeLanguage->toggle( - !_terms && !_resetAccount, + !_terms && !_resetAccount && _nextShown, anim::type::normal); } } @@ -491,7 +487,7 @@ void Widget::showControls() { } if (_changeLanguage) { _changeLanguage->toggle( - !_resetAccount && !_terms, + !_resetAccount && !_terms && _nextShown, anim::type::instant); } if (_terms) { @@ -508,12 +504,19 @@ void Widget::setupNextButton() { auto visible = getStep()->nextButtonText( ) | rpl::map([](const QString &text) { return !text.isEmpty(); - }) | rpl::distinct_until_changed(); + }); std::move( visible - ) | rpl::start_with_next([=](bool visible) { + ) | rpl::filter([=](bool visible) { + return visible != _nextShown; + }) | rpl::start_with_next([=](bool visible) { _next->toggle(visible, anim::type::normal); _nextShown = visible; + if (_changeLanguage) { + _changeLanguage->toggle( + !_resetAccount && !_terms && _nextShown, + anim::type::normal); + } _nextShownAnimation.start( [=] { updateControlsGeometry(); }, _nextShown ? 0. : 1., diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index f56f984df..e9a84f541 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -152,7 +152,7 @@ private: std::unique_ptr _connecting; - bool _nextShown = false; + bool _nextShown = true; Ui::Animations::Simple _nextShownAnimation; mtpRequestId _resetRequest = 0;