Intro redesign done.

This commit is contained in:
John Preston 2016-11-24 22:28:23 +03:00
parent 6e0394dd42
commit 3da0533339
71 changed files with 2150 additions and 1776 deletions

View File

@ -125,8 +125,10 @@ notificationSampleTextFg: #d7d7d7 | windowSubTextFg;
notificationSampleNameFg: #939393 | windowSubTextFg;
// intro
introHeaderFg: windowFg;
introErrorFg: windowFg;
introBg: windowBg;
introTitleFg: windowBoldFg;
introDescriptionFg: windowSubTextFg;
introErrorFg: windowSubTextFg;
// dialogs
dialogsMenuIconFg: menuIconFg;

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -138,7 +138,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_pinned_unpin" = "Unpin";
"lng_pinned_notify" = "Notify all members";
"lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b].";
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
"lng_start_msgs" = "START MESSAGING";
"lng_intro_next" = "NEXT";
@ -150,7 +150,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_phone_ph" = "Your phone number";
"lng_phone_title" = "Your Phone";
"lng_phone_desc" = "Please confirm your country code and\nenter your phone number.";
"lng_phone_notreg" = "Note: if you don't have a Telegram account yet,\nplease [b]sign up[/b] with your [a href=\"https://telegram.org/\"]iOS / Android[/a] or {signup_start}here »{signup_end}";
"lng_phone_notreg" = "If you don't have a Telegram account yet,\nplease [b]sign up[/b] with {link_start}Android / iPhone{link_end} or {signup_start}here{signup_end}";
"lng_country_code" = "Country Code";
"lng_bad_country_code" = "Invalid Country Code";
"lng_country_ph" = "Search";
@ -160,7 +160,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_code_ph" = "Your code";
"lng_code_desc" = "We have sent you a message with activation\ncode to your phone. Please enter it below.";
"lng_code_telegram" = "Please enter the code you've just\nreceived in your previous [b]Telegram[/b] app.";
"lng_code_telegram" = "Please enter the code you've just received\nin your previous [b]Telegram[/b] app.";
"lng_code_no_telegram" = "Send code via SMS";
"lng_code_call" = "Telegram will dial your number in {minutes}:{seconds}";
"lng_code_calling" = "Requesting a call from Telegram...";
@ -174,7 +174,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_signin_title" = "Cloud password check";
"lng_signin_desc" = "Please enter your cloud password.";
"lng_signin_recover_desc" = "Please enter the code from the e-mail.";
"lng_signin_recover_desc" = "Please enter the code from the e-mail\n{email}";
"lng_signin_password" = "Your cloud password";
"lng_signin_code" = "Code from e-mail";
"lng_signin_recover" = "Forgot password?";
@ -196,7 +196,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_signin_reset_in_minutes" = "{count_minutes:0 minutes|# minute|# minutes}";
"lng_signin_reset_cancelled" = "Your recent attempts to reset this account have been cancelled by its active user. Please try again in 7 days.";
"lng_signup_title" = "Information and photo";
"lng_signup_title" = "Your Info";
"lng_signup_desc" = "Please enter your name and\nupload a photo.";
"lng_signup_firstname" = "First Name";

View File

@ -102,8 +102,10 @@ notificationSampleUserpicFg: windowBgActive;
notificationSampleCloseFg: #d7d7d7; // windowSubTextFg;
notificationSampleTextFg: #d7d7d7; // windowSubTextFg;
notificationSampleNameFg: #939393; // windowSubTextFg;
introHeaderFg: windowFg;
introErrorFg: windowFg;
introBg: windowBg;
introTitleFg: windowBoldFg;
introDescriptionFg: windowSubTextFg;
introErrorFg: windowSubTextFg;
dialogsMenuIconFg: menuIconFg;
dialogsMenuIconFgOver: menuIconFgOver;
dialogsBg: windowBg;

View File

@ -2546,7 +2546,7 @@ namespace {
QImage readImage(const QString &file, QByteArray *format, bool opaque, bool *animated, QByteArray *content) {
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) {
if (f.size() > MediaViewImageSizeLimit || !f.open(QIODevice::ReadOnly)) {
if (animated) *animated = false;
return QImage();
}

View File

@ -252,7 +252,7 @@ void AddContactBox::onRetry() {
GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : AbstractBox()
, _creating(creating)
, _photo(this, st::newGroupPhotoSize)
, _photo(this, st::newGroupPhotoSize, st::newGroupPhotoIconPosition)
, _title(this, st::defaultInputField, lang(_creating == CreatingGroupChannel ? lng_dlg_new_channel_name : lng_dlg_new_group_name))
, _description(this, st::newGroupDescription, lang(lng_create_group_description))
, _next(this, lang(_creating == CreatingGroupChannel ? lng_create_group_create : lng_create_group_next), st::defaultBoxButton)
@ -276,15 +276,14 @@ GroupInfoBox::GroupInfoBox(CreatingGroupType creating, bool fromTypeChoose) : Ab
connect(_next, SIGNAL(clicked()), this, SLOT(onNext()));
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
notifyFileQueryUpdated(update);
});
_photo->setClickedCallback([this] {
auto imgExtensions = cImgExtensions();
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter();
_setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
});
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
notifyFileQueryUpdated(update);
});
prepare();
}
@ -418,7 +417,7 @@ void GroupInfoBox::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update)
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
return;
}
PhotoCropBox *box = new PhotoCropBox(img, (_creating == CreatingGroupChannel) ? peerFromChannel(0) : peerFromChat(0));
auto box = new PhotoCropBox(img, (_creating == CreatingGroupChannel) ? peerFromChannel(0) : peerFromChat(0));
connect(box, SIGNAL(ready(const QImage&)), this, SLOT(onPhotoReady(const QImage&)));
Ui::showLayer(box, KeepOtherLayers);
}

View File

@ -401,8 +401,7 @@ sessionTerminateAllButton: LinkButton(boxLinkButton) {
passcodeHeaderFont: font(19px);
passcodeHeaderHeight: 80px;
passcodeInput: FlatInput(introPhone) {
}
passcodeInput: introPhone;
passcodeSubmit: RoundButton(introNextButton) {
width: 225px;
}
@ -420,8 +419,6 @@ newGroupLinkTop: 3px;
newGroupLinkFont: font(16px);
newGroupPhotoSize: 76px;
newGroupPhotoBg: #4eb5f0;
newGroupPhotoBgOver: #3fa9e7;
newGroupPhotoIcon: icon {{ "new_chat_photo", #ffffff }};
newGroupPhotoIconPosition: point(23px, 25px);
newGroupPhotoDuration: 150;

View File

@ -65,6 +65,10 @@ void PhotoCropBox::init(const QImage &img, PeerData *peer) {
int32 s = st::boxWideWidth - st::boxPhotoPadding.left() - st::boxPhotoPadding.right();
_thumb = App::pixmapFromImageInPlace(img.scaled(s * cIntRetinaFactor(), s * cIntRetinaFactor(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
_thumb.setDevicePixelRatio(cRetinaFactor());
_mask = QImage(_thumb.size(), QImage::Format_ARGB32_Premultiplied);
_mask.setDevicePixelRatio(cRetinaFactor());
_fade = QImage(_thumb.size(), QImage::Format_ARGB32_Premultiplied);
_fade.setDevicePixelRatio(cRetinaFactor());
_thumbw = _thumb.width() / cIntRetinaFactor();
_thumbh = _thumb.height() / cIntRetinaFactor();
if (_thumbw > _thumbh) {
@ -238,18 +242,16 @@ void PhotoCropBox::paintEvent(QPaintEvent *e) {
p.translate(_thumbx, _thumby);
p.drawPixmap(0, 0, _thumb);
if (_cropy > 0) {
p.fillRect(QRect(0, 0, _cropx + _cropw, _cropy), st::photoCropFadeBg);
}
if (_cropx + _cropw < _thumbw) {
p.fillRect(QRect(_cropx + _cropw, 0, _thumbw - _cropx - _cropw, _cropy + _cropw), st::photoCropFadeBg);
}
if (_cropy + _cropw < _thumbh) {
p.fillRect(QRect(_cropx, _cropy + _cropw, _thumbw - _cropx, _thumbh - _cropy - _cropw), st::photoCropFadeBg);
}
if (_cropx > 0) {
p.fillRect(QRect(0, _cropy, _cropx, _thumbh - _cropy), st::photoCropFadeBg);
_mask.fill(Qt::white);
{
Painter p(&_mask);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.drawEllipse(_cropx, _cropy, _cropw, _cropw);
}
style::colorizeImage(_mask, st::photoCropFadeBg->c, &_fade);
p.drawImage(0, 0, _fade);
int delta = st::cropPointSize;
int mdelta = -delta / 2;

View File

@ -62,6 +62,7 @@ private:
ChildWidget<Ui::RoundButton> _cancel;
QImage _img;
QPixmap _thumb;
QImage _mask, _fade;
PeerId _peerId;
};

View File

@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/slide_animation.h"
#include "ui/widgets/discrete_sliders.h"
namespace {
@ -338,37 +339,12 @@ void StickersBox::paintEvent(QPaintEvent *e) {
_about.draw(p, st::stickersReorderPadding.top(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center);
}
if (!_leftCache.isNull()) {
auto slide = _a_slide.current(getms(), _slideLeft ? 0. : 1.);
if (!_a_slide.animating()) {
_leftCache = _rightCache = QPixmap();
if (_slideAnimation) {
_slideAnimation->paintFrame(p, scrollArea()->x(), scrollArea()->y() - titleHeight(), width(), getms());
if (!_slideAnimation->animating()) {
_slideAnimation.reset();
scrollArea()->show();
update();
} else {
auto easeOut = anim::easeOutCirc(1., slide);
auto easeIn = anim::easeInCirc(1., slide);
auto cacheWidth = (_leftCache.width() / cIntRetinaFactor());
auto arrivingCoord = anim::interpolate(cacheWidth, 0, easeOut);
auto departingCoord = anim::interpolate(0, cacheWidth, easeIn);
auto arrivingAlpha = easeIn;
auto departingAlpha = 1. - easeOut;
auto leftCoord = (_slideLeft ? arrivingCoord : departingCoord) * -1;
auto leftAlpha = (_slideLeft ? arrivingAlpha : departingAlpha);
auto rightCoord = (_slideLeft ? departingCoord : arrivingCoord);
auto rightAlpha = (_slideLeft ? departingAlpha : arrivingAlpha);
auto x = scrollArea()->x();
auto y = scrollArea()->y() - titleHeight();
auto leftWidth = (cacheWidth + leftCoord);
if (leftWidth > 0) {
p.setOpacity(leftAlpha);
p.drawPixmap(x, y, leftWidth, _leftCache.height() / cIntRetinaFactor(), _leftCache, (_leftCache.width() - leftWidth * cIntRetinaFactor()), 0, leftWidth * cIntRetinaFactor(), _leftCache.height());
}
auto rightWidth = cacheWidth - rightCoord;
if (rightWidth > 0) {
p.setOpacity(rightAlpha);
p.drawPixmap(x + rightCoord, y, _rightCache, 0, 0, rightWidth * cIntRetinaFactor(), _rightCache.height());
}
}
}
}
@ -430,14 +406,11 @@ void StickersBox::switchTab() {
auto nowCache = grabContentCache();
auto nowIndex = _tab->index;
_leftCache = std_::move(wasCache);
_rightCache = std_::move(nowCache);
_slideLeft = (wasIndex > nowIndex);
if (_slideLeft) {
std_::swap_moveable(_leftCache, _rightCache);
}
_slideAnimation = std_::make_unique<Ui::SlideAnimation>();
_slideAnimation->setSnapshots(std_::move(wasCache), std_::move(nowCache));
auto slideLeft = wasIndex > nowIndex;
_slideAnimation->start(slideLeft, [this] { update(); }, st::slideDuration);
scrollArea()->hide();
_a_slide.start([this] { update(); }, 0., 1., st::slideDuration);
update();
}
}
@ -602,6 +575,8 @@ void StickersBox::closePressed() {
}
}
StickersBox::~StickersBox() = default;
StickersBox::Inner::Inner(QWidget *parent, StickersBox::Section section) : TWidget(parent)
, _section(section)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())

View File

@ -33,6 +33,7 @@ class PlainShadow;
class RoundButton;
class RippleAnimation;
class SettingsSlider;
class SlideAnimation;
} // namespace Ui
class StickersBox : public ItemListBox, public RPCSender {
@ -48,6 +49,8 @@ public:
StickersBox(Section section = Section::Installed);
StickersBox(const Stickers::Order &archivedIds);
~StickersBox();
public slots:
void onStickersUpdated();
@ -111,9 +114,7 @@ private:
ChildWidget<Ui::RoundButton> _done = { nullptr };
ChildWidget<ScrollableBoxShadow> _bottomShadow = { nullptr };
FloatAnimation _a_slide;
bool _slideLeft = false;
QPixmap _leftCache, _rightCache;
std_::unique_ptr<Ui::SlideAnimation> _slideAnimation;
QTimer _scrollTimer;
int32 _scrollDelta = 0;

View File

@ -516,8 +516,8 @@ void start() {
SandboxData->LangSystemISO = psCurrentLanguage();
if (SandboxData->LangSystemISO.isEmpty()) SandboxData->LangSystemISO = qstr("en");
QByteArray l = LangSystemISO().toLatin1();
for (int32 i = 0; i < languageCount; ++i) {
auto l = LangSystemISO().toLatin1();
for (auto i = 0; i < languageCount; ++i) {
if (l.at(0) == LanguageCodes[i][0] && l.at(1) == LanguageCodes[i][1]) {
SandboxData->LangSystem = i;
break;

View File

@ -21,119 +21,135 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
using "basic.style";
using "ui/widgets/widgets.style";
countryInput {
width: pixels;
height: pixels;
top: pixels;
bgColor: color;
ptrSize: size;
textMrg: margins;
font: font;
align: align;
}
introCoverHeight: 208px;
introCoverTopBg: #0f89d0;
introCoverBottomBg: #39b0f0;
introCoverIconsFg: #5ec6ff;
introCoverMaxWidth: 880px;
introCoverIconsMinSkip: 120px;
introCoverLeft: icon {{ "intro_left", introCoverIconsFg }};
introCoverRight: icon {{ "intro_right", introCoverIconsFg }};
introCoverIcon: icon {
{ "intro_plane_trace", #5ec6ff69 },
{ "intro_plane_inner", #c6d8e8 },
{ "intro_plane_outer", #a1bed4 },
{ "intro_plane_top", #ffffff },
};
introCoverIconLeft: 50px;
introCoverIconTop: 46px;
introCountry: countryInput {
width: 300px;
height: 41px;
top: 33px;
bgColor: windowBgOver;
ptrSize: size(15px, 8px);
textMrg: margins(16px, 5px, 16px, 15px);
font: defaultInputFont;
align: align(left);
}
introSettingsSkip: 10px;
introIcon: icon {{ "intro_logo", #008ed5 }};
introPhotoSize: 76px;
introPhotoIconPosition: point(23px, 25px);
introPhotoTop: 10px;
introResetLink: LinkButton(defaultLinkButton) {
color: #d15948;
overColor: #d15948;
downColor: #db6352;
}
introBtnTop: 288px;
introSkip: 25px;
introFinishSkip: 15px;
introPhotoSize: 98px;
introHeaderFont: font(24px);
introHeaderSkip: 14px;
introIconSkip: 50px;
introFont: font(16px);
introLink: LinkButton(defaultLinkButton) {
font: introFont;
overFont: font(16px underline);
}
introLabel: FlatLabel(defaultFlatLabel) {
font: introFont;
introCoverTitle: FlatLabel(defaultFlatLabel) {
font: font(22px semibold);
textFg: introTitleFg;
align: align(center);
}
introCoverTitleTop: 126px;
introCoverDescription: FlatLabel(defaultFlatLabel) {
font: font(15px);
textFg: introDescriptionFg;
align: align(center);
}
introCoverDescriptionTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 24px;
}
introCoverDescriptionTop: 164px;
introTitle: FlatLabel(defaultFlatLabel) {
font: font(17px semibold);
textFg: introTitleFg;
}
introTitleTop: 11px;
introDescription: FlatLabel(defaultFlatLabel) {
font: normalFont;
textFg: introDescriptionFg;
}
introDescriptionTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 20px;
}
introDescriptionTop: 44px;
introStepSize: size(400px, 200px);
introSize: size(400px, 460px);
introSlideShift: 500px; // intro hiding animation
introLink: defaultLinkButton;
introPlaneWidth: 48px;
introPlaneHeight: 38px;
introHeight: 396px;
introStepTopMin: 86px;
introStepWidth: 380px;
introStepHeight: 256px;
introStepHeightAdd: 30px;
introStepHeightFull: 580px;
introSlideDuration: 200;
introSlideDelta: 0; // between hide start and show start
introTextTop: 22px;
introTextSize: size(400px, 93px);
introCallSkip: 15px;
introPwdTextSize: size(400px, 73px);
introCoverDuration: 200;
introNextButton: RoundButton(defaultActiveButton) {
width: 300px;
height: 56px;
textTop: 16px;
font: font(17px);
textTop: 17px;
font: font(17px semibold);
}
introPhoneTop: 8px;
introCountryCode: FlatInput(defaultFlatInput) {
width: 70px;
introStepFieldTop: 116px;
introPhoneTop: 16px;
introLinkTop: 21px;
introCountry: InputField(defaultInputField) {
textMargins: margins(3px, 7px, 3px, 6px);
font: font(16px);
width: 300px;
height: 41px;
align: align(center);
}
introPhone: FlatInput(defaultFlatInput) {
textMrg: margins(12px, 5px, 12px, 6px);
introCountryCode: InputField(introCountry) {
width: 64px;
height: 41px;
textAlign: align(top);
}
introPhone: InputField(introCountry) {
textMargins: margins(12px, 7px, 12px, 6px);
width: 225px;
height: 41px;
}
introCode: FlatInput(defaultFlatInput) {
textMrg: margins(12px, 5px, 12px, 6px);
width: 106px;
height: 41px;
align: align(center);
introCode: introCountry;
introName: introCountry;
introPassword: introCountry;
introPasswordTop: 94px;
introPasswordHintTop: 146px;
phPos: point(0px, 0px);
phAlign: align(center);
phShift: 0px;
introPasswordHint: FlatLabel(introDescription) {
textFg: windowFg;
}
introName: FlatInput(introPhone) {
width: 192px;
}
introPassword: FlatInput(introPhone) {
width: 300px;
introPasswordHintTextStyle: introDescriptionTextStyle;
introResetButton: RoundButton(defaultLightButton) {
textFg: attentionButtonFg;
textFgOver: attentionButtonFgOver;
textBgOver: attentionButtonBgOver;
ripple: RippleAnimation(defaultRippleAnimation) {
color: attentionButtonBgRipple;
}
}
introResetBottom: 20px;
introCountryIcon: icon {{ "intro_country_dropdown", menuIconFg }};
introCountryIconPosition: point(8px, 17px);
introSelectDelta: 30px;
introErrorWidth: 450px;
introErrorTop: 225px;
introErrorBelowLinkTop: 213px;
introErrorDuration: 200;
introErrorTop: 15px;
introErrorHeight: 40px;
introErrorFont: font(16px);
introLabelTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 30px;
}
introErrorLabelTextStyle: TextStyle(defaultTextStyle) {
lineHeight: 27px;
}
introErrorLabel: FlatLabel(defaultFlatLabel) {
font: introErrorFont;
introError: introDescription;
introErrorCentered: FlatLabel(introError) {
align: align(center);
}
introErrorTextStyle: introDescriptionTextStyle;
introBackButton: IconButton(defaultIconButton) {
width: 56px;
height: 56px;

View File

@ -26,21 +26,28 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "intro/introsignup.h"
#include "intro/intropwdcheck.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "styles/style_intro.h"
CodeInput::CodeInput(QWidget *parent, const style::FlatInput &st, const QString &ph) : Ui::FlatInput(parent, st, ph) {
namespace Intro {
CodeInput::CodeInput(QWidget *parent, const style::InputField &st, const QString &ph) : Ui::MaskedInputField(parent, st, ph) {
}
void CodeInput::correctValue(const QString &was, QString &now) {
void CodeInput::setDigitsCountMax(int digitsCount) {
_digitsCountMax = digitsCount;
}
void CodeInput::correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) {
QString newText;
int oldPos(cursorPosition()), newPos(-1), oldLen(now.length()), digitCount = 0;
int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = 0;
for (int i = 0; i < oldLen; ++i) {
if (now[i].isDigit()) {
++digitCount;
}
}
if (digitCount > 5) digitCount = 5;
bool strict = (digitCount == 5);
accumulate_min(digitCount, _digitsCountMax);
auto strict = (digitCount == _digitsCountMax);
newText.reserve(oldLen);
for (int i = 0; i < oldLen; ++i) {
@ -58,177 +65,131 @@ void CodeInput::correctValue(const QString &was, QString &now) {
newPos = newText.length();
}
}
if (newPos < 0) {
newPos = newText.length();
if (newPos < 0 || newPos > newText.size()) {
newPos = newText.size();
}
if (newText != now) {
now = newText;
setText(now);
updatePlaceholder();
if (newPos != oldPos) {
setCursorPosition(newPos);
}
}
if (newPos != nowCursor) {
nowCursor = newPos;
setCursorPosition(nowCursor);
}
if (strict) emit codeEntered();
}
IntroCode::IntroCode(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, _a_error(animation(this, &IntroCode::step_error))
, _next(this, lang(lng_intro_next), st::introNextButton)
, _desc(st::introTextSize.width())
CodeWidget::CodeWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink)
, _noTelegramCodeRequestId(0)
, _code(this, st::introCode, lang(lng_code_ph))
, _callTimer(this)
, _callStatus(intro()->getCallStatus())
, _callStatus(getData()->callStatus)
, _callTimeout(getData()->callTimeout)
, _callLabel(this, st::introDescription, st::introDescriptionTextStyle)
, _checkRequest(this) {
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitCode()));
connect(_code, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_callTimer, SIGNAL(timeout()), this, SLOT(onSendCall()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
connect(_noTelegramCode, SIGNAL(clicked()), this, SLOT(onNoTelegramCode()));
updateDescText();
_code->setDigitsCountMax(getData()->codeLength);
setErrorBelowLink(true);
if (!intro()->codeByTelegram()) {
if (_callStatus.type == IntroWidget::CallWaiting) {
_callTimer->start(1000);
}
}
setTitleText(App::formatPhone(getData()->phone));
updateDescText();
}
void IntroCode::updateDescText() {
_desc.setRichText(st::introFont, lang(intro()->codeByTelegram() ? lng_code_telegram : lng_code_desc));
if (intro()->codeByTelegram()) {
void CodeWidget::updateDescText() {
setDescriptionText(lang(getData()->codeByTelegram ? lng_code_telegram : lng_code_desc));
if (getData()->codeByTelegram) {
_noTelegramCode->show();
_callTimer->stop();
} else {
_noTelegramCode->hide();
_callStatus = intro()->getCallStatus();
if (_callStatus.type == IntroWidget::CallWaiting && !_callTimer->isActive()) {
_callStatus = getData()->callStatus;
_callTimeout = getData()->callTimeout;
if (_callStatus == Widget::Data::CallStatus::Waiting && !_callTimer->isActive()) {
_callTimer->start(1000);
}
}
update();
updateCallText();
}
void IntroCode::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
bool codeByTelegram = intro()->codeByTelegram();
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, intro()->getPhone(), style::al_top);
p.setFont(st::introFont->f);
_desc.draw(p, _textRect.x(), _textRect.y() + _textRect.height() - 2 * st::introFont->height, _textRect.width(), style::al_top);
}
if (codeByTelegram) {
} else {
QString callText;
switch (_callStatus.type) {
case IntroWidget::CallWaiting: {
if (_callStatus.timeout >= 3600) {
callText = lng_code_call(lt_minutes, qsl("%1:%2").arg(_callStatus.timeout / 3600).arg((_callStatus.timeout / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(_callStatus.timeout % 60, 2, 10, QChar('0')));
void CodeWidget::updateCallText() {
auto text = ([this]() -> QString {
if (getData()->codeByTelegram) {
return QString();
}
switch (_callStatus) {
case Widget::Data::CallStatus::Waiting: {
if (_callTimeout >= 3600) {
return lng_code_call(lt_minutes, qsl("%1:%2").arg(_callTimeout / 3600).arg((_callTimeout / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(_callTimeout % 60, 2, 10, QChar('0')));
} else {
callText = lng_code_call(lt_minutes, QString::number(_callStatus.timeout / 60), lt_seconds, qsl("%1").arg(_callStatus.timeout % 60, 2, 10, QChar('0')));
return lng_code_call(lt_minutes, QString::number(_callTimeout / 60), lt_seconds, qsl("%1").arg(_callTimeout % 60, 2, 10, QChar('0')));
}
} break;
case IntroWidget::CallCalling: {
callText = lang(lng_code_calling);
} break;
case IntroWidget::CallCalled: {
callText = lang(lng_code_called);
} break;
case Widget::Data::CallStatus::Calling: return lang(lng_code_calling);
case Widget::Data::CallStatus::Called: return lang(lng_code_called);
}
if (!callText.isEmpty()) {
p.drawText(QRect(_textRect.left(), _code->y() + _code->height() + st::introCallSkip, st::introTextSize.width(), st::introErrorHeight), callText, style::al_center);
}
}
if (_a_error.animating() || _error.length()) {
p.setOpacity(a_errorAlpha.current());
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(QRect(_textRect.left(), _next->y() + _next->height() + st::introErrorTop, st::introTextSize.width(), st::introErrorHeight), _error, style::al_center);
}
return QString();
})();
_callLabel->setText(text);
_callLabel->setVisible(!text.isEmpty() && !animating());
}
void IntroCode::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_code->move((width() - _code->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
}
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
_noTelegramCode->move(_textRect.left() + (st::introTextSize.width() - _noTelegramCode->width()) / 2, _code->y() + _code->height() + st::introCallSkip + (st::introErrorHeight - _noTelegramCode->height()) / 2);
void CodeWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_code->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
auto linkTop = _code->y() + _code->height() + st::introLinkTop;
_noTelegramCode->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
_callLabel->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
}
void IntroCode::showError(const QString &error) {
if (!error.isEmpty()) _code->notaBene();
if (!_a_error.animating() && error == _error) return;
if (error.length()) {
_error = error;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_a_error.start();
void CodeWidget::showCodeError(const QString &text) {
if (!text.isEmpty()) _code->showError();
showError(text);
}
void IntroCode::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
_error.clear();
}
} else {
a_errorAlpha.update(dt, anim::linear);
}
if (timer) update();
}
void IntroCode::activate() {
IntroStep::activate();
void CodeWidget::setInnerFocus() {
_code->setFocus();
}
void IntroCode::finished() {
IntroStep::finished();
_error.clear();
a_errorAlpha = anim::fvalue(0);
_sentCode.clear();
_code->setDisabled(false);
_callTimer->stop();
_code->setText(QString());
rpcClear();
}
void IntroCode::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
void CodeWidget::activate() {
Step::activate();
_code->show();
if (getData()->codeByTelegram) {
_noTelegramCode->show();
} else {
_callLabel->show();
}
MTP::send(MTPauth_CancelCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())));
setInnerFocus();
}
void IntroCode::stopCheck() {
void CodeWidget::finished() {
Step::finished();
_checkRequest->stop();
_callTimer->stop();
rpcClear();
cancelled();
_sentCode.clear();
_code->setText(QString());
_code->setDisabled(false);
}
void CodeWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
MTP::cancel(base::take(_callRequestId));
MTP::send(MTPauth_CancelCode(MTP_string(getData()->phone), MTP_string(getData()->phoneHash)));
}
void CodeWidget::stopCheck() {
_checkRequest->stop();
}
void IntroCode::onCheckRequest() {
void CodeWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@ -248,26 +209,25 @@ void IntroCode::onCheckRequest() {
}
}
void IntroCode::codeSubmitDone(const MTPauth_Authorization &result) {
void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) {
stopCheck();
_sentRequest = 0;
_code->setDisabled(false);
const auto &d(result.c_auth_authorization());
auto &d = result.c_auth_authorization();
if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf?
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
return;
}
cSetLoggedPhoneNumber(intro()->getPhone());
intro()->finish(d.vuser);
cSetLoggedPhoneNumber(getData()->phone);
finish(d.vuser);
}
bool IntroCode::codeSubmitFail(const RPCError &error) {
bool CodeWidget::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
stopCheck();
_sentRequest = 0;
showError(lang(lng_flood_error));
_code->setDisabled(false);
_code->setFocus();
showCodeError(lang(lng_flood_error));
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
@ -277,138 +237,138 @@ bool IntroCode::codeSubmitFail(const RPCError &error) {
_code->setDisabled(false);
const QString &err = error.type();
if (err == qstr("PHONE_NUMBER_INVALID") || err == qstr("PHONE_CODE_EXPIRED")) { // show error
intro()->onBack();
goBack();
return true;
} else if (err == qstr("PHONE_CODE_EMPTY") || err == qstr("PHONE_CODE_INVALID")) {
showError(lang(lng_bad_code));
_code->notaBene();
showCodeError(lang(lng_bad_code));
return true;
} else if (err == qstr("PHONE_NUMBER_UNOCCUPIED")) { // success, need to signUp
intro()->setCode(_sentCode);
intro()->replaceStep(new IntroSignup(intro()));
getData()->code = _sentCode;
goReplace(new Intro::SignupWidget(parentWidget(), getData()));
return true;
} else if (err == qstr("SESSION_PASSWORD_NEEDED")) {
intro()->setCode(_sentCode);
getData()->code = _sentCode;
_code->setDisabled(false);
_checkRequest->start(1000);
_sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&IntroCode::gotPassword), rpcFail(&IntroCode::codeSubmitFail));
_sentRequest = MTP::send(MTPaccount_GetPassword(), rpcDone(&CodeWidget::gotPassword), rpcFail(&CodeWidget::codeSubmitFail));
return true;
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
showCodeError(err + ": " + error.description());
} else {
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
}
_code->setFocus();
return false;
}
void IntroCode::onInputChange() {
showError(QString());
if (_code->text().length() == 5) onSubmitCode();
void CodeWidget::onInputChange() {
hideError();
if (_code->getLastText().length() == getData()->codeLength) {
submit();
}
}
void IntroCode::onSendCall() {
if (_callStatus.type == IntroWidget::CallWaiting) {
if (--_callStatus.timeout <= 0) {
_callStatus.type = IntroWidget::CallCalling;
void CodeWidget::onSendCall() {
if (_callStatus == Widget::Data::CallStatus::Waiting) {
if (--_callTimeout <= 0) {
_callStatus = Widget::Data::CallStatus::Calling;
_callTimer->stop();
MTP::send(MTPauth_ResendCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())), rpcDone(&IntroCode::callDone));
_callRequestId = MTP::send(MTPauth_ResendCode(MTP_string(getData()->phone), MTP_string(getData()->phoneHash)), rpcDone(&CodeWidget::callDone));
} else {
intro()->setCallStatus(_callStatus);
getData()->callStatus = _callStatus;
getData()->callTimeout = _callTimeout;
}
}
update();
}
void IntroCode::callDone(const MTPauth_SentCode &v) {
if (_callStatus.type == IntroWidget::CallCalling) {
_callStatus.type = IntroWidget::CallCalled;
intro()->setCallStatus(_callStatus);
update();
updateCallText();
}
}
void IntroCode::gotPassword(const MTPaccount_Password &result) {
void CodeWidget::callDone(const MTPauth_SentCode &v) {
if (v.type() == mtpc_auth_sentCode) {
fillSentCodeData(v.c_auth_sentCode().vtype);
_code->setDigitsCountMax(getData()->codeLength);
}
if (_callStatus == Widget::Data::CallStatus::Calling) {
_callStatus = Widget::Data::CallStatus::Called;
getData()->callStatus = _callStatus;
getData()->callTimeout = _callTimeout;
updateCallText();
}
}
void CodeWidget::gotPassword(const MTPaccount_Password &result) {
stopCheck();
_sentRequest = 0;
_code->setDisabled(false);
switch (result.type()) {
case mtpc_account_noPassword: // should not happen
case mtpc_account_noPassword: { // should not happen
_code->setFocus();
break;
} break;
case mtpc_account_password: {
const auto &d(result.c_account_password());
intro()->setPwdSalt(qba(d.vcurrent_salt));
intro()->setHasRecovery(mtpIsTrue(d.vhas_recovery));
intro()->setPwdHint(qs(d.vhint));
intro()->replaceStep(new IntroPwdCheck(intro()));
auto &d = result.c_account_password();
getData()->pwdSalt = qba(d.vcurrent_salt);
getData()->hasRecovery = mtpIsTrue(d.vhas_recovery);
getData()->pwdHint = qs(d.vhint);
goReplace(new Intro::PwdCheckWidget(parentWidget(), getData()));
} break;
}
}
void IntroCode::onSubmitCode() {
void CodeWidget::submit() {
if (_sentRequest) return;
_code->setDisabled(true);
setFocus();
showError(QString());
hideError();
_checkRequest->start(1000);
_sentCode = _code->text();
intro()->setPwdSalt(QByteArray());
intro()->setHasRecovery(false);
intro()->setPwdHint(QString());
_sentRequest = MTP::send(MTPauth_SignIn(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(_sentCode)), rpcDone(&IntroCode::codeSubmitDone), rpcFail(&IntroCode::codeSubmitFail));
_sentCode = _code->getLastText();
getData()->pwdSalt = QByteArray();
getData()->hasRecovery = false;
getData()->pwdHint = QString();
_sentRequest = MTP::send(MTPauth_SignIn(MTP_string(getData()->phone), MTP_string(getData()->phoneHash), MTP_string(_sentCode)), rpcDone(&CodeWidget::codeSubmitDone), rpcFail(&CodeWidget::codeSubmitFail));
}
void IntroCode::onNoTelegramCode() {
void CodeWidget::onNoTelegramCode() {
if (_noTelegramCodeRequestId) return;
_noTelegramCodeRequestId = MTP::send(MTPauth_ResendCode(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash())), rpcDone(&IntroCode::noTelegramCodeDone), rpcFail(&IntroCode::noTelegramCodeFail));
_noTelegramCodeRequestId = MTP::send(MTPauth_ResendCode(MTP_string(getData()->phone), MTP_string(getData()->phoneHash)), rpcDone(&CodeWidget::noTelegramCodeDone), rpcFail(&CodeWidget::noTelegramCodeFail));
}
void IntroCode::noTelegramCodeDone(const MTPauth_SentCode &result) {
void CodeWidget::noTelegramCodeDone(const MTPauth_SentCode &result) {
if (result.type() != mtpc_auth_sentCode) {
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
return;
}
const auto &d(result.c_auth_sentCode());
switch (d.vtype.type()) {
case mtpc_auth_sentCodeTypeApp: intro()->setCodeByTelegram(true);
case mtpc_auth_sentCodeTypeSms:
case mtpc_auth_sentCodeTypeCall: intro()->setCodeByTelegram(false);
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
auto &d = result.c_auth_sentCode();
fillSentCodeData(d.vtype);
_code->setDigitsCountMax(getData()->codeLength);
if (d.has_next_type() && d.vnext_type.type() == mtpc_auth_codeTypeCall) {
intro()->setCallStatus({ IntroWidget::CallWaiting, d.has_timeout() ? d.vtimeout.v : 60 });
getData()->callStatus = Widget::Data::CallStatus::Waiting;
getData()->callTimeout = d.has_timeout() ? d.vtimeout.v : 60;
} else {
intro()->setCallStatus({ IntroWidget::CallDisabled, 0 });
getData()->callStatus = Widget::Data::CallStatus::Disabled;
getData()->callTimeout = 0;
}
intro()->setCodeByTelegram(false);
getData()->codeByTelegram = false;
updateDescText();
}
bool IntroCode::noTelegramCodeFail(const RPCError &error) {
bool CodeWidget::noTelegramCodeFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
showError(lang(lng_flood_error));
_code->setFocus();
showCodeError(lang(lng_flood_error));
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
if (cDebug()) { // internal server error
showError(error.type() + ": " + error.description());
showCodeError(error.type() + ": " + error.description());
} else {
showError(lang(lng_server_error));
showCodeError(lang(lng_server_error));
}
_code->setFocus();
return false;
}
void IntroCode::onSubmit() {
onSubmitCode();
}
} // namespace Intro

View File

@ -26,55 +26,63 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class RoundButton;
class LinkButton;
class FlatLabel;
} // namespace Ui
class CodeInput final : public Ui::FlatInput {
namespace Intro {
class CodeInput final : public Ui::MaskedInputField {
Q_OBJECT
public:
CodeInput(QWidget *parent, const style::FlatInput &st, const QString &ph);
CodeInput(QWidget *parent, const style::InputField &st, const QString &ph);
void setDigitsCountMax(int digitsCount);
signals:
void codeEntered();
protected:
void correctValue(const QString &was, QString &now);
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
private:
int _digitsCountMax = 5;
};
class IntroCode final : public IntroStep {
class CodeWidget : public Widget::Step {
Q_OBJECT
public:
IntroCode(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void step_error(float64 ms, bool timer);
CodeWidget(QWidget *parent, Widget::Data *data);
bool hasBack() const override {
return true;
}
void setInnerFocus() override;
void activate() override;
void finished() override;
void cancelled() override;
void onSubmit() override;
void codeSubmitDone(const MTPauth_Authorization &result);
bool codeSubmitFail(const RPCError &error);
void submit() override;
void updateDescText();
public slots:
void onSubmitCode();
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onNoTelegramCode();
void onInputChange();
void onSendCall();
void onCheckRequest();
private:
void showError(const QString &err);
void updateCallText();
void codeSubmitDone(const MTPauth_Authorization &result);
bool codeSubmitFail(const RPCError &error);
void showCodeError(const QString &text);
void callDone(const MTPauth_SentCode &v);
void gotPassword(const MTPaccount_Password &result);
@ -83,23 +91,21 @@ private:
void stopCheck();
QString _error;
anim::fvalue a_errorAlpha;
Animation _a_error;
ChildWidget<Ui::RoundButton> _next;
Text _desc;
ChildWidget<Ui::LinkButton> _noTelegramCode;
mtpRequestId _noTelegramCodeRequestId;
QRect _textRect;
mtpRequestId _noTelegramCodeRequestId = 0;
ChildWidget<CodeInput> _code;
QString _sentCode;
mtpRequestId _sentRequest = 0;
ChildObject<QTimer> _callTimer;
IntroWidget::CallStatus _callStatus;
Widget::Data::CallStatus _callStatus;
int _callTimeout;
mtpRequestId _callRequestId = 0;
ChildWidget<Ui::FlatLabel> _callLabel;
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@ -27,20 +27,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_intro.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/effects/widget_fade_wrap.h"
#include "core/click_handler_types.h"
IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, _a_error(animation(this, &IntroPhone::step_error))
, _next(this, lang(lng_intro_next), st::introNextButton)
namespace Intro {
PhoneWidget::PhoneWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _country(this, st::introCountry)
, _phone(this, st::introPhone)
, _code(this, st::introCountryCode)
, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), Ui::FlatLabel::InitType::Rich, st::introErrorLabel, st::introErrorLabelTextStyle)
, _phone(this, st::introPhone)
, _checkRequest(this) {
setVisible(false);
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPhone()));
connect(_phone, SIGNAL(voidBackspace(QKeyEvent*)), _code, SLOT(startErasing(QKeyEvent*)));
connect(_country, SIGNAL(codeChanged(const QString &)), _code, SLOT(codeSelected(const QString &)));
connect(_code, SIGNAL(codeChanged(const QString &)), _country, SLOT(onChooseCode(const QString &)));
@ -49,146 +45,112 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent)
connect(_code, SIGNAL(addedToNumber(const QString &)), _phone, SLOT(addedToNumber(const QString &)));
connect(_phone, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_code, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(intro(), SIGNAL(countryChanged()), this, SLOT(countryChanged()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
_signup->setLink(1, MakeShared<LambdaClickHandler>([this] {
toSignUp();
}));
_signup->hide();
setTitleText(lang(lng_phone_title));
setDescriptionText(lang(lng_phone_desc));
subscribe(getData()->updated, [this] { countryChanged(); });
setErrorCentered(true);
_signupCache = myGrab(_signup);
if (!_country->onChooseCountry(intro()->currentCountry())) {
if (!_country->onChooseCountry(getData()->country)) {
_country->onChooseCountry(qsl("US"));
}
_changed = false;
}
void IntroPhone::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
void PhoneWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_country->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
auto phoneTop = _country->y() + _country->height() + st::introPhoneTop;
_code->moveToLeft(contentLeft(), phoneTop);
_phone->moveToLeft(contentLeft() + _country->width() - st::introPhone.width, phoneTop);
updateSignupGeometry();
}
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, lang(lng_phone_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(_textRect, lang(lng_phone_desc), style::al_bottom);
}
if (_a_error.animating() || _error.length()) {
int32 errorY = _showSignup ? ((_phone->y() + _phone->height() + _next->y() - st::introErrorFont->height) / 2) : (_next->y() + _next->height() + st::introErrorTop);
p.setOpacity(a_errorAlpha.current());
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(QRect(_textRect.x(), errorY, _textRect.width(), st::introErrorFont->height), _error, style::al_top);
if (_signup->isHidden() && _showSignup) {
p.drawPixmap(_signup->x(), _signup->y(), _signupCache);
}
void PhoneWidget::updateSignupGeometry() {
if (_signup) {
_signup->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introDescriptionTop);
}
}
void IntroPhone::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_country->move((width() - _country->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
int phoneTop = _country->y() + _country->height() + st::introPhoneTop;
_phone->move((width() - _country->width()) / 2 + _country->width() - st::introPhone.width, phoneTop);
_code->move((width() - _country->width()) / 2, phoneTop);
}
_signup->move((width() - _signup->width()) / 2, _next->y() + _next->height() + st::introErrorTop - ((st::introErrorLabelTextStyle.lineHeight - st::introErrorFont->height) / 2));
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
void PhoneWidget::showPhoneError(const QString &text) {
_phone->showError();
showError(text);
}
void IntroPhone::showError(const QString &error, bool signUp) {
if (!error.isEmpty()) {
_phone->notaBene();
_showSignup = signUp;
void PhoneWidget::hidePhoneError() {
hideError();
if (_signup) {
_signup->fadeOut();
showDescription();
}
if (!_a_error.animating() && error == _error) return;
if (error.length()) {
_error = error;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_signup->hide();
_a_error.start();
}
void IntroPhone::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
_error.clear();
_signup->hide();
} else if (!_error.isEmpty() && _showSignup) {
_signup->show();
}
} else {
a_errorAlpha.update(dt, anim::linear);
void PhoneWidget::showSignup() {
showPhoneError(lang(lng_bad_phone_noreg));
if (!_signup) {
auto signupText = lng_phone_notreg(lt_link_start, textcmdStartLink(1), lt_link_end, textcmdStopLink(), lt_signup_start, textcmdStartLink(2), lt_signup_end, textcmdStopLink());
auto inner = new Ui::FlatLabel(this, signupText, Ui::FlatLabel::InitType::Rich, st::introDescription, st::introDescriptionTextStyle);
_signup.create(this, inner, base::lambda<void()>(), st::introErrorDuration);
_signup->entity()->setLink(1, MakeShared<UrlClickHandler>(qsl("https://telegram.org"), false));
_signup->entity()->setLink(2, MakeShared<LambdaClickHandler>([this] {
toSignUp();
}));
_signup->hideFast();
updateSignupGeometry();
}
if (timer) update();
_signup->fadeIn();
hideDescription();
}
void IntroPhone::countryChanged() {
void PhoneWidget::countryChanged() {
if (!_changed) {
selectCountry(intro()->currentCountry());
selectCountry(getData()->country);
}
}
void IntroPhone::onInputChange() {
void PhoneWidget::onInputChange() {
_changed = true;
showError(QString());
hidePhoneError();
}
void IntroPhone::disableAll() {
_next->setDisabled(true);
void PhoneWidget::disableAll() {
_phone->setDisabled(true);
_country->setDisabled(true);
_code->setDisabled(true);
setFocus();
}
void IntroPhone::enableAll(bool failed) {
_next->setDisabled(false);
void PhoneWidget::enableAll(bool failed) {
_phone->setDisabled(false);
_country->setDisabled(false);
_code->setDisabled(false);
if (failed) _phone->setFocus();
}
void IntroPhone::onSubmitPhone() {
void PhoneWidget::submit() {
if (_sentRequest || isHidden()) return;
if (!App::isValidPhone(fullNumber())) {
showError(lang(lng_bad_phone));
showPhoneError(lang(lng_bad_phone));
_phone->setFocus();
return;
}
disableAll();
showError(QString());
hidePhoneError();
_checkRequest->start(1000);
_sentPhone = fullNumber();
_sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&IntroPhone::phoneCheckDone), rpcFail(&IntroPhone::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_CheckPhone(MTP_string(_sentPhone)), rpcDone(&PhoneWidget::phoneCheckDone), rpcFail(&PhoneWidget::phoneSubmitFail));
}
void IntroPhone::stopCheck() {
void PhoneWidget::stopCheck() {
_checkRequest->stop();
}
void IntroPhone::onCheckRequest() {
void PhoneWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@ -202,66 +164,65 @@ void IntroPhone::onCheckRequest() {
}
}
void IntroPhone::phoneCheckDone(const MTPauth_CheckedPhone &result) {
void PhoneWidget::phoneCheckDone(const MTPauth_CheckedPhone &result) {
stopCheck();
const auto &d(result.c_auth_checkedPhone());
auto &d = result.c_auth_checkedPhone();
if (mtpIsTrue(d.vphone_registered)) {
disableAll();
showError(QString());
hidePhoneError();
_checkRequest->start(1000);
MTPauth_SendCode::Flags flags = 0;
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&PhoneWidget::phoneSubmitDone), rpcFail(&PhoneWidget::phoneSubmitFail));
} else {
showError(lang(lng_bad_phone_noreg), true);
showSignup();
enableAll(true);
_sentRequest = 0;
}
}
void IntroPhone::phoneSubmitDone(const MTPauth_SentCode &result) {
void PhoneWidget::phoneSubmitDone(const MTPauth_SentCode &result) {
stopCheck();
_sentRequest = 0;
enableAll(true);
if (result.type() != mtpc_auth_sentCode) {
showError(lang(lng_server_error));
showPhoneError(lang(lng_server_error));
return;
}
const auto &d(result.c_auth_sentCode());
switch (d.vtype.type()) {
case mtpc_auth_sentCodeTypeApp: intro()->setCodeByTelegram(true); break;
case mtpc_auth_sentCodeTypeSms:
case mtpc_auth_sentCodeTypeCall: intro()->setCodeByTelegram(false); break;
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
intro()->setPhone(_sentPhone, d.vphone_code_hash.c_string().v.c_str(), d.is_phone_registered());
auto &d = result.c_auth_sentCode();
fillSentCodeData(d.vtype);
getData()->phone = _sentPhone;
getData()->phoneHash = d.vphone_code_hash.c_string().v.c_str();
getData()->phoneIsRegistered = d.is_phone_registered();
if (d.has_next_type() && d.vnext_type.type() == mtpc_auth_codeTypeCall) {
intro()->setCallStatus({ IntroWidget::CallWaiting, d.has_timeout() ? d.vtimeout.v : 60 });
getData()->callStatus = Widget::Data::CallStatus::Waiting;
getData()->callTimeout = d.has_timeout() ? d.vtimeout.v : 60;
} else {
intro()->setCallStatus({ IntroWidget::CallDisabled, 0 });
getData()->callStatus = Widget::Data::CallStatus::Disabled;
getData()->callTimeout = 0;
}
intro()->nextStep(new IntroCode(intro()));
goNext(new Intro::CodeWidget(parentWidget(), getData()));
}
void IntroPhone::toSignUp() {
void PhoneWidget::toSignUp() {
disableAll();
showError(QString());
hideError(); // Hide error, but leave the signup label visible.
_checkRequest->start(1000);
MTPauth_SendCode::Flags flags = 0;
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
_sentRequest = MTP::send(MTPauth_SendCode(MTP_flags(flags), MTP_string(_sentPhone), MTPBool(), MTP_int(ApiId), MTP_string(ApiHash)), rpcDone(&PhoneWidget::phoneSubmitDone), rpcFail(&PhoneWidget::phoneSubmitFail));
}
bool IntroPhone::phoneSubmitFail(const RPCError &error) {
bool PhoneWidget::phoneSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
stopCheck();
_sentRequest = 0;
showError(lang(lng_flood_error));
showPhoneError(lang(lng_flood_error));
enableAll(true);
return true;
}
@ -271,48 +232,50 @@ bool IntroPhone::phoneSubmitFail(const RPCError &error) {
_sentRequest = 0;
const QString &err = error.type();
if (err == qstr("PHONE_NUMBER_INVALID")) { // show error
showError(lang(lng_bad_phone));
showPhoneError(lang(lng_bad_phone));
enableAll(true);
return true;
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
showPhoneError(err + ": " + error.description());
} else {
showError(lang(lng_server_error));
showPhoneError(lang(lng_server_error));
}
enableAll(true);
return false;
}
QString IntroPhone::fullNumber() const {
return _code->text() + _phone->text();
QString PhoneWidget::fullNumber() const {
return _code->getLastText() + _phone->getLastText();
}
void IntroPhone::selectCountry(const QString &c) {
void PhoneWidget::selectCountry(const QString &c) {
_country->onChooseCountry(c);
}
void IntroPhone::activate() {
IntroStep::activate();
void PhoneWidget::setInnerFocus() {
_phone->setFocus();
}
void IntroPhone::finished() {
IntroStep::finished();
void PhoneWidget::activate() {
Step::activate();
_country->show();
_phone->show();
_code->show();
setInnerFocus();
}
void PhoneWidget::finished() {
Step::finished();
_checkRequest->stop();
rpcClear();
_error.clear();
a_errorAlpha = anim::fvalue(0);
cancelled();
enableAll(true);
}
void IntroPhone::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
void PhoneWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
}
void IntroPhone::onSubmit() {
onSubmitPhone();
}
} // namespace Intro

View File

@ -30,35 +30,41 @@ class RoundButton;
class FlatLabel;
} // namespace Ui
class IntroPhone final : public IntroStep {
namespace Intro {
class PhoneWidget : public Widget::Step, private base::Subscriber {
Q_OBJECT
public:
IntroPhone(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void step_error(float64 ms, bool timer);
PhoneWidget(QWidget *parent, Widget::Data *data);
void selectCountry(const QString &country);
void setInnerFocus() override;
void activate() override;
void finished() override;
void cancelled() override;
void onSubmit() override;
void submit() override;
bool hasBack() const override {
return true;
}
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onInputChange();
void onCheckRequest();
private:
void updateSignupGeometry();
void countryChanged();
void phoneCheckDone(const MTPauth_CheckedPhone &result);
void phoneSubmitDone(const MTPauth_SentCode &result);
bool phoneSubmitFail(const RPCError &error);
public slots:
void countryChanged();
void onInputChange();
void onSubmitPhone();
void onCheckRequest();
private:
void toSignUp();
QString fullNumber() const;
@ -66,24 +72,17 @@ private:
void enableAll(bool failed);
void stopCheck();
void showError(const QString &err, bool signUp = false);
QString _error;
anim::fvalue a_errorAlpha;
Animation _a_error;
void showPhoneError(const QString &text);
void hidePhoneError();
void showSignup();
bool _changed = false;
ChildWidget<Ui::RoundButton> _next;
QRect _textRect;
ChildWidget<CountryInput> _country;
ChildWidget<Ui::PhonePartInput> _phone;
ChildWidget<Ui::CountryCodeInput> _code;
ChildWidget<Ui::PhonePartInput> _phone;
ChildWidget<Ui::FlatLabel> _signup;
QPixmap _signupCache;
bool _showSignup = false;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _signup = { nullptr };
QString _sentPhone;
mtpRequestId _sentRequest = 0;
@ -91,3 +90,5 @@ private:
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@ -30,117 +30,52 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "intro/introsignup.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
IntroPwdCheck::IntroPwdCheck(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, _a_error(animation(this, &IntroPwdCheck::step_error))
, _next(this, lang(lng_intro_submit), st::introNextButton)
, _salt(parent->getPwdSalt())
, _hasRecovery(parent->getHasRecovery())
, _hint(parent->getPwdHint())
namespace Intro {
PwdCheckWidget::PwdCheckWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _salt(getData()->pwdSalt)
, _hasRecovery(getData()->hasRecovery)
, _hint(getData()->pwdHint)
, _pwdField(this, st::introPassword, lang(lng_signin_password))
, _pwdHint(this, st::introPasswordHint, st::introPasswordHintTextStyle)
, _codeField(this, st::introPassword, lang(lng_signin_code))
, _toRecover(this, lang(lng_signin_recover))
, _toPassword(this, lang(lng_signin_try_password))
, _reset(this, lang(lng_signin_reset_account), st::introResetLink)
, _checkRequest(this) {
setVisible(false);
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitPwd()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
connect(_toRecover, SIGNAL(clicked()), this, SLOT(onToRecover()));
connect(_toPassword, SIGNAL(clicked()), this, SLOT(onToPassword()));
connect(_pwdField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_codeField, SIGNAL(changed()), this, SLOT(onInputChange()));
connect(_reset, SIGNAL(clicked()), this, SLOT(onReset()));
_pwdField->setEchoMode(QLineEdit::Password);
setTitleText(lang(lng_signin_title));
updateDescriptionText();
setErrorBelowLink(true);
if (!_hint.isEmpty()) {
_hintText.setText(st::introFont, lng_signin_hint(lt_password_hint, _hint));
if (_hint.isEmpty()) {
_pwdHint->hide();
} else {
_pwdHint->setText(lng_signin_hint(lt_password_hint, _hint));
}
_codeField->hide();
_toPassword->hide();
_toRecover->show();
_reset->hide();
setMouseTracking(true);
}
void IntroPwdCheck::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, lang(lng_signin_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(_textRect, lang(_pwdField->isHidden() ? lng_signin_recover_desc : lng_signin_desc), style::al_bottom);
}
if (_pwdField->isHidden()) {
if (!_emailPattern.isEmpty()) {
p.drawText(QRect(_textRect.x(), _pwdField->y() + _pwdField->height() + st::introFinishSkip, _textRect.width(), st::introFont->height), _emailPattern, style::al_top);
}
} else if (!_hint.isEmpty()) {
_hintText.drawElided(p, _pwdField->x(), _pwdField->y() + _pwdField->height() + st::introFinishSkip, _pwdField->width(), 1, style::al_top);
}
if (_a_error.animating() || _error.length()) {
p.setOpacity(a_errorAlpha.current());
QRect errRect((width() - st::introErrorWidth) / 2, (_pwdField->y() + _pwdField->height() + st::introFinishSkip + st::introFont->height + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight);
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(errRect, _error, QTextOption(style::al_center));
p.setOpacity(1);
}
void PwdCheckWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
_pwdField->moveToLeft(contentLeft(), contentTop() + st::introPasswordTop);
_pwdHint->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introPasswordHintTop);
_codeField->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
auto linkTop = _codeField->y() + _codeField->height() + st::introLinkTop;
_toRecover->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
_toPassword->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
}
void IntroPwdCheck::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_pwdField->move((width() - _pwdField->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_codeField->move((width() - _codeField->width()) / 2, st::introTextTop + st::introTextSize.height() + st::introCountry.top);
_toRecover->move(_next->x() + (_pwdField->width() - _toRecover->width()) / 2, _next->y() + _next->height() + st::introFinishSkip);
_toPassword->move(_next->x() + (_pwdField->width() - _toPassword->width()) / 2, _next->y() + _next->height() + st::introFinishSkip);
_reset->move((width() - _reset->width()) / 2, _toRecover->y() + _toRecover->height() + st::introFinishSkip);
}
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
}
void IntroPwdCheck::showError(const QString &error) {
if (!_a_error.animating() && error == _error) return;
if (error.length()) {
_error = error;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_a_error.start();
}
void IntroPwdCheck::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
_error.clear();
}
} else {
a_errorAlpha.update(dt, anim::linear);
}
if (timer) update();
}
void IntroPwdCheck::activate() {
IntroStep::activate();
void PwdCheckWidget::setInnerFocus() {
if (_pwdField->isHidden()) {
_codeField->setFocus();
} else {
@ -148,17 +83,25 @@ void IntroPwdCheck::activate() {
}
}
void IntroPwdCheck::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
void PwdCheckWidget::activate() {
if (_pwdField->isHidden() && _codeField->isHidden()) {
Step::activate();
_pwdField->show();
_pwdHint->show();
_toRecover->show();
}
setInnerFocus();
}
void IntroPwdCheck::stopCheck() {
void PwdCheckWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
}
void PwdCheckWidget::stopCheck() {
_checkRequest->stop();
}
void IntroPwdCheck::onCheckRequest() {
void PwdCheckWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@ -176,7 +119,7 @@ void IntroPwdCheck::onCheckRequest() {
}
}
void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &result) {
_sentRequest = 0;
stopCheck();
if (recover) {
@ -184,22 +127,22 @@ void IntroPwdCheck::pwdSubmitDone(bool recover, const MTPauth_Authorization &res
}
_pwdField->setDisabled(false);
_codeField->setDisabled(false);
const auto &d(result.c_auth_authorization());
auto &d = result.c_auth_authorization();
if (d.vuser.type() != mtpc_user || !d.vuser.c_user().is_self()) { // wtf?
showError(lang(lng_server_error));
return;
}
intro()->finish(d.vuser);
finish(d.vuser);
}
bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
_sentRequest = 0;
stopCheck();
_codeField->setDisabled(false);
showError(lang(lng_flood_error));
_pwdField->setDisabled(false);
_pwdField->notaBene();
_pwdField->showError();
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
@ -212,10 +155,10 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
if (err == qstr("PASSWORD_HASH_INVALID")) {
showError(lang(lng_signin_bad_password));
_pwdField->selectAll();
_pwdField->notaBene();
_pwdField->showError();
return true;
} else if (err == qstr("PASSWORD_EMPTY")) {
intro()->onBack();
goBack();
}
if (cDebug()) { // internal server error
showError(err + ": " + error.description());
@ -226,10 +169,10 @@ bool IntroPwdCheck::pwdSubmitFail(const RPCError &error) {
return false;
}
bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
bool PwdCheckWidget::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
showError(lang(lng_flood_error));
_codeField->notaBene();
_codeField->showError();
return true;
}
if (MTP::isDefaultHandledError(error)) return false;
@ -240,7 +183,7 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
_codeField->setDisabled(false);
const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) {
intro()->onBack();
goBack();
return true;
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
recoverStartFail(error);
@ -252,7 +195,7 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
} else if (err == qstr("CODE_INVALID")) {
showError(lang(lng_signin_wrong_code));
_codeField->selectAll();
_codeField->notaBene();
_codeField->showError();
return true;
}
if (cDebug()) { // internal server error
@ -264,39 +207,42 @@ bool IntroPwdCheck::codeSubmitFail(const RPCError &error) {
return false;
}
void IntroPwdCheck::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = st::introFont->elided(lng_signin_recover_hint(lt_recover_email, qs(result.c_auth_passwordRecovery().vemail_pattern)), _textRect.width());
update();
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
_emailPattern = qs(result.c_auth_passwordRecovery().vemail_pattern);
updateDescriptionText();
}
bool IntroPwdCheck::recoverStartFail(const RPCError &error) {
bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
stopCheck();
_pwdField->setDisabled(false);
_codeField->setDisabled(false);
_pwdField->show();
_pwdHint->show();
_codeField->hide();
_pwdField->setFocus();
updateDescriptionText();
update();
showError(QString());
hideError();
return true;
}
void IntroPwdCheck::onToRecover() {
void PwdCheckWidget::onToRecover() {
if (_hasRecovery) {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
showError(QString());
hideError();
_toRecover->hide();
_toPassword->show();
_pwdField->hide();
_pwdHint->hide();
_pwdField->setText(QString());
_codeField->show();
_codeField->setFocus();
updateDescriptionText();
if (_emailPattern.isEmpty()) {
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&IntroPwdCheck::recoverStarted), rpcFail(&IntroPwdCheck::recoverStartFail));
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PwdCheckWidget::recoverStarted), rpcFail(&PwdCheckWidget::recoverStartFail));
}
update();
} else {
ConfirmBox *box = new InformBox(lang(lng_signin_no_email_forgot));
Ui::showLayer(box);
@ -304,101 +250,63 @@ void IntroPwdCheck::onToRecover() {
}
}
void IntroPwdCheck::onToPassword() {
void PwdCheckWidget::onToPassword() {
ConfirmBox *box = new InformBox(lang(lng_signin_cant_email_forgot));
Ui::showLayer(box);
connect(box, SIGNAL(destroyed(QObject*)), this, SLOT(onToReset()));
}
void IntroPwdCheck::onToReset() {
void PwdCheckWidget::onToReset() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
_toRecover->show();
_toPassword->hide();
_pwdField->show();
_pwdHint->show();
_codeField->hide();
_codeField->setText(QString());
_pwdField->setFocus();
_reset->show();
showResetButton();
updateDescriptionText();
update();
}
void IntroPwdCheck::onReset() {
if (_sentRequest) return;
ConfirmBox *box = new ConfirmBox(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton);
connect(box, SIGNAL(confirmed()), this, SLOT(onResetSure()));
Ui::showLayer(box);
void PwdCheckWidget::updateDescriptionText() {
setDescriptionText(_pwdField->isHidden() ? lng_signin_recover_desc(lt_email, _emailPattern) : lang(lng_signin_desc));
}
void IntroPwdCheck::onResetSure() {
if (_sentRequest) return;
_sentRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&IntroPwdCheck::deleteDone), rpcFail(&IntroPwdCheck::deleteFail));
void PwdCheckWidget::onInputChange() {
hideError();
}
bool IntroPwdCheck::deleteFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_sentRequest = 0;
auto type = error.type();
if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) {
int seconds = type.mid(qstr("2FA_CONFIRM_WAIT_").size()).toInt();
int days = (seconds + 59) / 86400;
int hours = ((seconds + 59) % 86400) / 3600;
int minutes = ((seconds + 59) % 3600) / 60;
QString when;
if (days > 0) {
when = lng_signin_reset_in_days(lt_count_days, days, lt_count_hours, hours, lt_count_minutes, minutes);
} else if (hours > 0) {
when = lng_signin_reset_in_hours(lt_count_hours, hours, lt_count_minutes, minutes);
} else {
when = lng_signin_reset_in_minutes(lt_count_minutes, minutes);
}
Ui::showLayer(new InformBox(lng_signin_reset_wait(lt_phone_number, App::formatPhone(intro()->getPhone()), lt_when, when)));
} else if (type == qstr("2FA_RECENT_CONFIRM")) {
Ui::showLayer(new InformBox(lang(lng_signin_reset_cancelled)));
} else {
Ui::hideLayer();
showError(lang(lng_server_error));
}
return true;
}
void IntroPwdCheck::deleteDone(const MTPBool &v) {
Ui::hideLayer();
intro()->replaceStep(new IntroSignup(intro()));
}
void IntroPwdCheck::onInputChange() {
showError(QString());
}
void IntroPwdCheck::onSubmitPwd(bool force) {
void PwdCheckWidget::submit() {
if (_sentRequest) return;
if (_pwdField->isHidden()) {
if (!force && !_codeField->isEnabled()) return;
QString code = _codeField->text().trimmed();
if (!_codeField->isEnabled()) return;
auto code = _codeField->getLastText().trimmed();
if (code.isEmpty()) {
_codeField->notaBene();
_codeField->showError();
return;
}
_sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&IntroPwdCheck::pwdSubmitDone, true), rpcFail(&IntroPwdCheck::codeSubmitFail));
_sentRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&PwdCheckWidget::pwdSubmitDone, true), rpcFail(&PwdCheckWidget::codeSubmitFail));
} else {
if (!force && !_pwdField->isEnabled()) return;
if (!_pwdField->isEnabled()) return;
_pwdField->setDisabled(true);
setFocus();
showError(QString());
hideError();
QByteArray pwdData = _salt + _pwdField->text().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized);
QByteArray pwdData = _salt + _pwdField->getLastText().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized);
hashSha256(pwdData.constData(), pwdData.size(), pwdHash.data());
_sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&IntroPwdCheck::pwdSubmitDone, false), rpcFail(&IntroPwdCheck::pwdSubmitFail));
_sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&PwdCheckWidget::pwdSubmitDone, false), rpcFail(&PwdCheckWidget::pwdSubmitFail));
}
}
void IntroPwdCheck::onSubmit() {
onSubmitPwd();
QString PwdCheckWidget::nextButtonText() const {
return lang(lng_intro_submit);
}
} // namespace Intro

View File

@ -23,26 +23,37 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "intro/introwidget.h"
namespace Ui {
class FlatInput;
class InputField;
class PasswordInput;
class RoundButton;
class LinkButton;
} // namespace Ui
class IntroPwdCheck final : public IntroStep {
namespace Intro {
class PwdCheckWidget : public Widget::Step {
Q_OBJECT
public:
IntroPwdCheck(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void step_error(float64 ms, bool timer);
PwdCheckWidget(QWidget *parent, Widget::Data *data);
void setInnerFocus() override;
void activate() override;
void cancelled() override;
void onSubmit() override;
void submit() override;
QString nextButtonText() const override;
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onToRecover();
void onToPassword();
void onInputChange();
void onCheckRequest();
void onToReset();
private:
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
bool pwdSubmitFail(const RPCError &error);
bool codeSubmitFail(const RPCError &error);
@ -50,46 +61,24 @@ public:
void recoverStarted(const MTPauth_PasswordRecovery &result);
public slots:
void onSubmitPwd(bool force = false);
void onToRecover();
void onToPassword();
void onInputChange();
void onCheckRequest();
void onToReset();
void onReset();
void onResetSure();
private:
void showError(const QString &error);
void updateDescriptionText();
void stopCheck();
void deleteDone(const MTPBool &result);
bool deleteFail(const RPCError &error);
QString _error;
anim::fvalue a_errorAlpha;
Animation _a_error;
ChildWidget<Ui::RoundButton> _next;
QRect _textRect;
QByteArray _salt;
bool _hasRecovery;
QString _hint, _emailPattern;
ChildWidget<Ui::FlatInput> _pwdField;
ChildWidget<Ui::FlatInput> _codeField;
ChildWidget<Ui::PasswordInput> _pwdField;
ChildWidget<Ui::FlatLabel> _pwdHint;
ChildWidget<Ui::InputField> _codeField;
ChildWidget<Ui::LinkButton> _toRecover;
ChildWidget<Ui::LinkButton> _toPassword;
ChildWidget<Ui::LinkButton> _reset;
mtpRequestId _sentRequest = 0;
Text _hintText;
QByteArray _pwdSalt;
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@ -29,200 +29,106 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/buttons/peer_avatar_button.h"
IntroSignup::IntroSignup(IntroWidget *parent) : IntroStep(parent)
, a_errorAlpha(0)
, a_photoOver(0)
, _a_error(animation(this, &IntroSignup::step_error))
, _a_photo(animation(this, &IntroSignup::step_photo))
, _next(this, lang(lng_intro_finish), st::introNextButton)
namespace Intro {
SignupWidget::SignupWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _photo(this, st::introPhotoSize, st::introPhotoIconPosition)
, _first(this, st::introName, lang(lng_signup_firstname))
, _last(this, st::introName, lang(lng_signup_lastname))
, _invertOrder(langFirstNameGoesSecond())
, _checkRequest(this) {
setVisible(false);
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), this, SLOT(onSubmitName()));
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
_photo->setClickedCallback([this] {
auto imgExtensions = cImgExtensions();
auto filter = qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter();
_readPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
});
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
notifyFileQueryUpdated(update);
});
if (_invertOrder) {
setTabOrder(_last, _first);
}
setErrorCentered(true);
setTitleText(lang(lng_signup_title));
setDescriptionText(lang(lng_signup_desc));
setMouseTracking(true);
}
void IntroSignup::mouseMoveEvent(QMouseEvent *e) {
bool photoOver = QRect(_phLeft, _phTop, st::introPhotoSize, st::introPhotoSize).contains(e->pos());
if (photoOver != _photoOver) {
_photoOver = photoOver;
if (_photoSmall.isNull()) {
a_photoOver.start(_photoOver ? 1 : 0);
_a_photo.start();
}
void SignupWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) {
if (_readPhotoFileQueryId != update.queryId) {
return;
}
_readPhotoFileQueryId = 0;
if (update.remoteContent.isEmpty() && update.filePaths.isEmpty()) {
return;
}
setCursor(_photoOver ? style::cur_pointer : style::cur_default);
}
void IntroSignup::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (QRect(_phLeft, _phTop, st::introPhotoSize, st::introPhotoSize).contains(e->pos())) {
QStringList imgExtensions(cImgExtensions());
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter());
QImage img;
QString file;
QByteArray remoteContent;
if (filedialogGetOpenFile(file, remoteContent, lang(lng_choose_images), filter)) {
if (!remoteContent.isEmpty()) {
img = App::readImage(remoteContent);
} else {
if (!file.isEmpty()) {
img = App::readImage(file);
}
}
} else {
return;
}
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
showError(lang(lng_bad_photo));
return;
}
PhotoCropBox *box = new PhotoCropBox(img, PeerId(0));
connect(box, SIGNAL(ready(const QImage &)), this, SLOT(onPhotoReady(const QImage &)));
Ui::showLayer(box);
}
}
void IntroSignup::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
Painter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
if (trivial || e->rect().intersects(_textRect)) {
p.setFont(st::introHeaderFont->f);
p.drawText(_textRect, lang(lng_signup_title), style::al_top);
p.setFont(st::introFont->f);
p.drawText(_textRect, lang(lng_signup_desc), style::al_bottom);
}
if (_a_error.animating() || error.length()) {
p.setOpacity(a_errorAlpha.current());
QRect errRect;
if (_invertOrder) {
errRect = QRect((width() - st::introErrorWidth) / 2, (_first->y() + _first->height() + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight);
} else {
errRect = QRect((width() - st::introErrorWidth) / 2, (_last->y() + _last->height() + _next->y() - st::introErrorHeight) / 2, st::introErrorWidth, st::introErrorHeight);
}
p.setFont(st::introErrorFont);
p.setPen(st::introErrorFg);
p.drawText(errRect, error, QTextOption(style::al_center));
p.setOpacity(1);
}
if (_photoSmall.isNull()) {
float64 o = a_photoOver.current();
QRect phRect(_phLeft, _phTop, st::introPhotoSize, st::introPhotoSize);
if (o > 0) {
if (o < 1) {
QColor c;
c.setRedF(st::newGroupPhotoBg->c.redF() * (1. - o) + st::newGroupPhotoBgOver->c.redF() * o);
c.setGreenF(st::newGroupPhotoBg->c.greenF() * (1. - o) + st::newGroupPhotoBgOver->c.greenF() * o);
c.setBlueF(st::newGroupPhotoBg->c.blueF() * (1. - o) + st::newGroupPhotoBgOver->c.blueF() * o);
p.fillRect(phRect, c);
} else {
p.fillRect(phRect, st::newGroupPhotoBgOver);
}
} else {
p.fillRect(phRect, st::newGroupPhotoBg);
}
st::newGroupPhotoIcon.paintInCenter(p, phRect);
QImage img;
if (!update.remoteContent.isEmpty()) {
img = App::readImage(update.remoteContent);
} else {
p.drawPixmap(_phLeft, _phTop, _photoSmall);
img = App::readImage(update.filePaths.front());
}
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
showError(lang(lng_bad_photo));
return;
}
auto box = new PhotoCropBox(img, PeerId(0));
connect(box, SIGNAL(ready(const QImage &)), this, SLOT(onPhotoReady(const QImage &)));
Ui::showLayer(box);
}
void IntroSignup::resizeEvent(QResizeEvent *e) {
_phLeft = (width() - _next->width()) / 2;
_phTop = st::introTextTop + st::introTextSize.height() + st::introCountry.top;
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
if (_invertOrder) {
_last->move((width() - _next->width()) / 2 + _next->width() - _last->width(), _phTop);
_first->move((width() - _next->width()) / 2 + _next->width() - _first->width(), _last->y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop);
} else {
_first->move((width() - _next->width()) / 2 + _next->width() - _first->width(), _phTop);
_last->move((width() - _next->width()) / 2 + _next->width() - _last->width(), _first->y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop);
}
}
_textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height());
}
void SignupWidget::resizeEvent(QResizeEvent *e) {
Step::resizeEvent(e);
void IntroSignup::showError(const QString &err) {
if (!_a_error.animating() && err == error) return;
auto photoRight = contentLeft() + st::introNextButton.width;
auto photoTop = contentTop() + st::introPhotoTop;
_photo->moveToLeft(photoRight - _photo->width(), photoTop);
if (err.length()) {
error = err;
a_errorAlpha.start(1);
} else {
a_errorAlpha.start(0);
}
_a_error.start();
}
void IntroSignup::step_error(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_error.stop();
a_errorAlpha.finish();
if (!a_errorAlpha.current()) {
error.clear();
}
} else {
a_errorAlpha.update(dt, anim::linear);
}
if (timer) update();
}
void IntroSignup::step_photo(float64 ms, bool timer) {
float64 dt = ms / st::introErrorDuration;
if (dt >= 1) {
_a_photo.stop();
a_photoOver.finish();
} else {
a_photoOver.update(dt, anim::linear);
}
if (timer) update();
}
void IntroSignup::activate() {
IntroStep::activate();
auto firstTop = contentTop() + st::introStepFieldTop;
auto secondTop = firstTop + st::introName.height + st::introPhoneTop;
if (_invertOrder) {
_last->moveToLeft(contentLeft(), firstTop);
_first->moveToLeft(contentLeft(), secondTop);
} else {
_first->moveToLeft(contentLeft(), firstTop);
_last->moveToLeft(contentLeft(), secondTop);
}
}
void SignupWidget::setInnerFocus() {
if (_invertOrder || _last->hasFocus()) {
_last->setFocus();
} else {
_first->setFocus();
}
}
void IntroSignup::cancelled() {
if (_sentRequest) {
MTP::cancel(base::take(_sentRequest));
}
void SignupWidget::activate() {
Step::activate();
_first->show();
_last->show();
_photo->show();
setInnerFocus();
}
void IntroSignup::stopCheck() {
void SignupWidget::cancelled() {
MTP::cancel(base::take(_sentRequest));
}
void SignupWidget::stopCheck() {
_checkRequest->stop();
}
void IntroSignup::onCheckRequest() {
void SignupWidget::onCheckRequest() {
int32 status = MTP::state(_sentRequest);
if (status < 0) {
int32 leftms = -status;
@ -244,13 +150,12 @@ void IntroSignup::onCheckRequest() {
}
}
void IntroSignup::onPhotoReady(const QImage &img) {
_photoBig = img;
_photoSmall = App::pixmapFromImageInPlace(img.scaled(st::introPhotoSize * cIntRetinaFactor(), st::introPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
_photoSmall.setDevicePixelRatio(cRetinaFactor());
void SignupWidget::onPhotoReady(const QImage &img) {
_photoImage = img;
_photo->setImage(_photoImage);
}
void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) {
void SignupWidget::nameSubmitDone(const MTPauth_Authorization &result) {
stopCheck();
_first->setDisabled(false);
_last->setDisabled(false);
@ -259,10 +164,10 @@ void IntroSignup::nameSubmitDone(const MTPauth_Authorization &result) {
showError(lang(lng_server_error));
return;
}
intro()->finish(d.vuser, _photoBig);
finish(d.vuser, _photoImage);
}
bool IntroSignup::nameSubmitFail(const RPCError &error) {
bool SignupWidget::nameSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
stopCheck();
_first->setDisabled(false);
@ -284,7 +189,7 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) {
if (err == qstr("PHONE_NUMBER_INVALID") || err == qstr("PHONE_CODE_EXPIRED") ||
err == qstr("PHONE_CODE_EMPTY") || err == qstr("PHONE_CODE_INVALID") ||
err == qstr("PHONE_NUMBER_OCCUPIED")) {
intro()->onBack();
goBack();
return true;
} else if (err == "FIRSTNAME_INVALID") {
showError(lang(lng_bad_name));
@ -308,29 +213,29 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) {
return false;
}
void IntroSignup::onInputChange() {
void SignupWidget::onInputChange() {
showError(QString());
}
void IntroSignup::onSubmitName(bool force) {
void SignupWidget::submit() {
if (_invertOrder) {
if ((_last->hasFocus() || _last->text().trimmed().length()) && !_first->text().trimmed().length()) {
if ((_last->hasFocus() || _last->getLastText().trimmed().length()) && !_first->getLastText().trimmed().length()) {
_first->setFocus();
return;
} else if (!_last->text().trimmed().length()) {
} else if (!_last->getLastText().trimmed().length()) {
_last->setFocus();
return;
}
} else {
if ((_first->hasFocus() || _first->text().trimmed().length()) && !_last->text().trimmed().length()) {
if ((_first->hasFocus() || _first->getLastText().trimmed().length()) && !_last->getLastText().trimmed().length()) {
_last->setFocus();
return;
} else if (!_first->text().trimmed().length()) {
} else if (!_first->getLastText().trimmed().length()) {
_first->setFocus();
return;
}
}
if (!force && !_first->isEnabled()) return;
if (!_first->isEnabled()) return;
_first->setDisabled(true);
_last->setDisabled(true);
@ -338,11 +243,13 @@ void IntroSignup::onSubmitName(bool force) {
showError(QString());
_firstName = _first->text().trimmed();
_lastName = _last->text().trimmed();
_sentRequest = MTP::send(MTPauth_SignUp(MTP_string(intro()->getPhone()), MTP_string(intro()->getPhoneHash()), MTP_string(intro()->getCode()), MTP_string(_firstName), MTP_string(_lastName)), rpcDone(&IntroSignup::nameSubmitDone), rpcFail(&IntroSignup::nameSubmitFail));
_firstName = _first->getLastText().trimmed();
_lastName = _last->getLastText().trimmed();
_sentRequest = MTP::send(MTPauth_SignUp(MTP_string(getData()->phone), MTP_string(getData()->phoneHash), MTP_string(getData()->code), MTP_string(_firstName), MTP_string(_lastName)), rpcDone(&SignupWidget::nameSubmitDone), rpcFail(&SignupWidget::nameSubmitFail));
}
void IntroSignup::onSubmit() {
onSubmitName();
QString SignupWidget::nextButtonText() const {
return lang(lng_intro_finish);
}
} // namespace Intro

View File

@ -21,64 +21,58 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "intro/introwidget.h"
#include "ui/filedialog.h"
namespace Ui {
class RoundButton;
class FlatInput;
class InputField;
class NewAvatarButton;
} // namespace Ui
class IntroSignup final : public IntroStep {
namespace Intro {
class SignupWidget : public Widget::Step, private base::Subscriber {
Q_OBJECT
public:
IntroSignup(IntroWidget *parent);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void step_error(float64 ms, bool timer);
void step_photo(float64 ms, bool timer);
SignupWidget(QWidget *parent, Widget::Data *data);
void setInnerFocus() override;
void activate() override;
void cancelled() override;
void onSubmit() override;
void submit() override;
QString nextButtonText() const override;
void nameSubmitDone(const MTPauth_Authorization &result);
bool nameSubmitFail(const RPCError &error);
protected:
void resizeEvent(QResizeEvent *e) override;
public slots:
void onSubmitName(bool force = false);
private slots:
void onInputChange();
void onCheckRequest();
void onPhotoReady(const QImage &img);
private:
void showError(const QString &err);
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
void nameSubmitDone(const MTPauth_Authorization &result);
bool nameSubmitFail(const RPCError &error);
void stopCheck();
QString error;
anim::fvalue a_errorAlpha, a_photoOver;
Animation _a_error;
Animation _a_photo;
QImage _photoImage;
ChildWidget<Ui::RoundButton> _next;
QRect _textRect;
bool _photoOver = false;
QImage _photoBig;
QPixmap _photoSmall;
int32 _phLeft, _phTop;
ChildWidget<Ui::FlatInput> _first;
ChildWidget<Ui::FlatInput> _last;
ChildWidget<Ui::NewAvatarButton> _photo;
ChildWidget<Ui::InputField> _first;
ChildWidget<Ui::InputField> _last;
QString _firstName, _lastName;
mtpRequestId _sentRequest = 0;
FileDialog::QueryId _readPhotoFileQueryId = 0;
bool _invertOrder = false;
ChildObject<QTimer> _checkRequest;
};
} // namespace Intro

View File

@ -24,67 +24,24 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "application.h"
#include "intro/introphone.h"
#include "langloaderplain.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent)
, _intro(this, lang(lng_intro), Ui::FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle)
, _changeLang(this, QString())
, _next(this, lang(lng_start_msgs), st::introNextButton) {
_changeLang->hide();
if (cLang() == languageDefault) {
int32 l = Sandbox::LangSystem();
if (l != languageDefault) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this));
QString text = loader.found().value(lng_switch_to_this);
if (!text.isEmpty()) {
_changeLang->setText(text);
parent->langChangeTo(l);
_changeLang->show();
}
}
} else {
_changeLang->setText(langOriginal(lng_switch_to_this));
parent->langChangeTo(languageDefault);
_changeLang->show();
}
_headerWidth = st::introHeaderFont->width(qsl("Telegram Desktop"));
setGeometry(parent->innerRect());
connect(_next, SIGNAL(clicked()), parent, SLOT(onStepSubmit()));
connect(_changeLang, SIGNAL(clicked()), parent, SLOT(onChangeLang()));
namespace Intro {
StartWidget::StartWidget(QWidget *parent, Widget::Data *data) : Step(parent, data, true) {
setMouseTracking(true);
setTitleText(qsl("Telegram Desktop"));
setDescriptionText(lang(lng_intro_about));
show();
}
void IntroStart::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
Painter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
int32 hy = _intro->y() - st::introHeaderFont->height - st::introHeaderSkip + st::introHeaderFont->ascent;
p.setFont(st::introHeaderFont);
p.setPen(st::introHeaderFg);
p.drawText((width() - _headerWidth) / 2, hy, qsl("Telegram Desktop"));
st::introIcon.paint(p, QPoint((width() - st::introIcon.width()) / 2, hy - st::introIconSkip - st::introIcon.height()), width());
void StartWidget::submit() {
goNext(new Intro::PhoneWidget(parentWidget(), getData()));
}
void IntroStart::resizeEvent(QResizeEvent *e) {
if (e->oldSize().width() != width()) {
_next->move((width() - _next->width()) / 2, st::introBtnTop);
_intro->move((width() - _intro->width()) / 2, _next->y() - _intro->height() - st::introSkip);
_changeLang->move((width() - _changeLang->width()) / 2, _next->y() + _next->height() + _changeLang->height());
}
QString StartWidget::nextButtonText() const {
return lang(lng_start_msgs);
}
void IntroStart::onSubmit() {
intro()->nextStep(new IntroPhone(intro()));
}
} // namespace Intro

View File

@ -28,22 +28,15 @@ class LinkButton;
class RoundButton;
} // namespace Ui
class IntroStart final : public IntroStep {
namespace Intro {
class StartWidget : public Widget::Step {
public:
IntroStart(IntroWidget *parent);
StartWidget(QWidget *parent, Widget::Data *data);
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void onSubmit() override;
private:
ChildWidget<Ui::FlatLabel> _intro;
ChildWidget<Ui::LinkButton> _changeLang;
ChildWidget<Ui::RoundButton> _next;
int32 _headerWidth = 0;
void submit() override;
QString nextButtonText() const override;
};
} // namespace Intro

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "localstorage.h"
#include "langloaderplain.h"
#include "intro/introstart.h"
#include "intro/introphone.h"
#include "intro/introcode.h"
@ -31,39 +32,60 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "mainwindow.h"
#include "application.h"
#include "boxes/confirmbox.h"
#include "ui/text/text.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/effects/widget_fade_wrap.h"
#include "styles/style_intro.h"
#include "ui/effects/slide_animation.h"
#include "autoupdater.h"
#include "window/slide_animation.h"
#include "window/window_slide_animation.h"
#include "styles/style_boxes.h"
#include "styles/style_intro.h"
#include "styles/style_window.h"
IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent)
, _a_stage(animation(this, &IntroWidget::step_stage))
, _a_show(animation(this, &IntroWidget::step_show))
namespace Intro {
Widget::Widget(QWidget *parent) : TWidget(parent)
, _a_show(animation(this, &Widget::step_show))
, _back(this, new Ui::IconButton(this, st::introBackButton), base::lambda<void()>(), st::introSlideDuration)
, _settings(this, lang(lng_menu_settings), st::defaultBoxButton) {
_back->entity()->setClickedCallback([this] { onBack(); });
, _settings(this, new Ui::RoundButton(this, lang(lng_menu_settings), st::defaultBoxButton), base::lambda<void()>(), st::introCoverDuration)
, _next(this, QString(), st::introNextButton) {
getData()->country = psCurrentCountry();
_back->entity()->setClickedCallback([this] { historyMove(Direction::Back); });
_back->hideFast();
_settings->setClickedCallback([] { App::wnd()->showSettings(); });
_next->setClickedCallback([this] { getStep()->submit(); });
_countryForReg = psCurrentCountry();
_settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); });
MTP::send(MTPhelp_GetNearestDc(), rpcDone(&IntroWidget::gotNearestDC));
if (cLang() == languageDefault) {
auto systemLangId = Sandbox::LangSystem();
if (systemLangId != languageDefault) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this));
QString text = loader.found().value(lng_switch_to_this);
if (!text.isEmpty()) {
_changeLanguage.create(this, new Ui::LinkButton(this, text), base::lambda<void()>(), st::introCoverDuration);
_changeLanguage->entity()->setClickedCallback([this, systemLangId] { changeLanguage(systemLangId); });
}
}
} else {
_changeLanguage.create(this, new Ui::LinkButton(this, langOriginal(lng_switch_to_this)), base::lambda<void()>(), st::introCoverDuration);
_changeLanguage->entity()->setClickedCallback([this] { changeLanguage(languageDefault); });
}
_stepHistory.push_back(new IntroStart(this));
_back->raise();
_settings->raise();
MTP::send(MTPhelp_GetNearestDc(), rpcDone(&Widget::gotNearestDC));
appendStep(new StartWidget(this, getData()));
fixOrder();
show();
setFocus();
showControls();
getStep()->showFast();
cSetPasswordRecovered(false);
_back->moveToLeft(0, 0);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus()));
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus()));
@ -74,12 +96,12 @@ IntroWidget::IntroWidget(QWidget *parent) : TWidget(parent)
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void IntroWidget::onCheckUpdateStatus() {
void Widget::onCheckUpdateStatus() {
if (Sandbox::updatingState() == Application::UpdatingReady) {
if (_update) return;
_update.create(this, lang(lng_menu_update).toUpper(), st::defaultBoxButton);
_update.create(this, new Ui::RoundButton(this, lang(lng_menu_update).toUpper(), st::defaultBoxButton), base::lambda<void()>(), st::introCoverDuration);
if (!_a_show.animating()) _update->show();
_update->setClickedCallback([] {
_update->entity()->setClickedCallback([] {
checkReadyUpdate();
App::restart();
});
@ -91,123 +113,204 @@ void IntroWidget::onCheckUpdateStatus() {
}
#endif // TDESKTOP_DISABLE_AUTOUPDATE
void IntroWidget::langChangeTo(int32 langId) {
_langChangeTo = langId;
}
void IntroWidget::onChangeLang() {
cSetLang(_langChangeTo);
void Widget::changeLanguage(int32 languageId) {
cSetLang(languageId);
Local::writeSettings();
App::restart();
}
void IntroWidget::onStepSubmit() {
step()->onSubmit();
void Widget::setInnerFocus() {
if (getStep()->animating()) {
setFocus();
} else {
getStep()->setInnerFocus();
}
}
void IntroWidget::onBack() {
historyMove(MoveBack);
}
void IntroWidget::historyMove(MoveType type) {
if (_a_stage.animating()) return;
void Widget::historyMove(Direction direction) {
if (getStep()->animating()) return;
t_assert(_stepHistory.size() > 1);
if (App::app()) App::app()->mtpPause();
switch (type) {
case MoveBack: {
_cacheHide = grabStep();
IntroStep *back = step();
_backFrom = back->hasBack() ? 1 : 0;
auto wasStep = getStep((direction == Direction::Back) ? 0 : 1);
if (direction == Direction::Back) {
_stepHistory.pop_back();
back->cancelled();
delete back;
} break;
case MoveForward: {
_cacheHide = grabStep(1);
_backFrom = step(1)->hasBack() ? 1 : 0;
step(1)->finished();
} break;
case MoveReplace: {
_cacheHide = grabStep(1);
IntroStep *replaced = step(1);
_backFrom = replaced->hasBack() ? 1 : 0;
wasStep->cancelled();
} else if (direction == Direction::Replace) {
_stepHistory.removeAt(_stepHistory.size() - 2);
replaced->finished();
delete replaced;
} break;
}
getStep()->prepareShowAnimated(wasStep);
if (wasStep->hasCover() != getStep()->hasCover()) {
_nextTopFrom = wasStep->contentTop() + st::introStepHeight;
_controlsTopFrom = wasStep->hasCover() ? st::introCoverHeight : 0;
_coverShownAnimation.start([this] { updateControlsGeometry(); }, 0., 1., st::introCoverDuration, anim::easeOutCirc);
}
_cacheShow = grabStep();
_backTo = step()->hasBack() ? 1 : 0;
int32 m = (type == MoveBack) ? -1 : 1;
a_coordHide = anim::ivalue(0, -m * st::introSlideShift);
a_opacityHide = anim::fvalue(1, 0);
a_coordShow = anim::ivalue(m * st::introSlideShift, 0);
a_opacityShow = anim::fvalue(0, 1);
_a_stage.start();
_a_stage.step();
if (_backTo) {
if (direction == Direction::Forward || direction == Direction::Replace) {
wasStep->finished();
}
if (direction == Direction::Back || direction == Direction::Replace) {
delete base::take(wasStep);
}
if (getStep()->hasBack()) {
_back->fadeIn();
} else {
_back->fadeOut();
}
step()->hide();
if (getStep()->hasCover()) {
_settings->fadeOut();
if (_update) _update->fadeOut();
if (_changeLanguage) _changeLanguage->fadeIn();
} else {
_settings->fadeIn();
if (_update) _update->fadeIn();
if (_changeLanguage) _changeLanguage->fadeOut();
}
_next->setText(getStep()->nextButtonText());
if (_resetAccount) _resetAccount->fadeOut();
getStep()->showAnimated(direction);
fixOrder();
}
void IntroWidget::pushStep(IntroStep *step, MoveType type) {
_stepHistory.push_back(step);
void Widget::fixOrder() {
_next->raise();
if (_update) _update->raise();
_settings->raise();
_back->raise();
}
void Widget::moveToStep(Step *step, Direction direction) {
appendStep(step);
_back->raise();
_settings->raise();
if (_update) {
_update->raise();
}
_stepHistory.back()->hide();
historyMove(type);
historyMove(direction);
}
void IntroWidget::gotNearestDC(const MTPNearestDc &result) {
const auto &nearest(result.c_nearestDc());
void Widget::appendStep(Step *step) {
_stepHistory.push_back(step);
step->setGeometry(calculateStepRect());
step->setGoCallback([this](Step *step, Direction direction) {
if (direction == Direction::Back) {
historyMove(direction);
} else {
moveToStep(step, direction);
}
});
step->setShowResetCallback([this] {
showResetButton();
});
}
void Widget::showResetButton() {
if (!_resetAccount) {
_resetAccount.create(this, new Ui::RoundButton(this, lang(lng_signin_reset_account), st::introResetButton), base::lambda<void()>(), st::introErrorDuration);
_resetAccount->hideFast();
_resetAccount->entity()->setClickedCallback([this] { resetAccount(); });
updateControlsGeometry();
}
_resetAccount->fadeIn();
}
void Widget::resetAccount() {
if (_resetRequest) return;
auto box = new ConfirmBox(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton);
box->setConfirmedCallback([this] { resetAccountSure(); });
Ui::showLayer(box);
}
void Widget::resetAccountSure() {
if (_resetRequest) return;
_resetRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&Widget::resetDone), rpcFail(&Widget::resetFail));
}
void Widget::resetDone(const MTPBool &result) {
Ui::hideLayer();
moveToStep(new SignupWidget(this, getData()), Direction::Replace);
}
bool Widget::resetFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_resetRequest = 0;
auto type = error.type();
if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) {
int seconds = type.mid(qstr("2FA_CONFIRM_WAIT_").size()).toInt();
int days = (seconds + 59) / 86400;
int hours = ((seconds + 59) % 86400) / 3600;
int minutes = ((seconds + 59) % 3600) / 60;
QString when;
if (days > 0) {
when = lng_signin_reset_in_days(lt_count_days, days, lt_count_hours, hours, lt_count_minutes, minutes);
} else if (hours > 0) {
when = lng_signin_reset_in_hours(lt_count_hours, hours, lt_count_minutes, minutes);
} else {
when = lng_signin_reset_in_minutes(lt_count_minutes, minutes);
}
Ui::showLayer(new InformBox(lng_signin_reset_wait(lt_phone_number, App::formatPhone(getData()->phone), lt_when, when)));
} else if (type == qstr("2FA_RECENT_CONFIRM")) {
Ui::showLayer(new InformBox(lang(lng_signin_reset_cancelled)));
} else {
Ui::hideLayer();
getStep()->showError(lang(lng_server_error));
}
return true;
}
void Widget::gotNearestDC(const MTPNearestDc &result) {
auto &nearest = result.c_nearestDc();
DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3").arg(nearest.vcountry.c_string().v.c_str()).arg(nearest.vnearest_dc.v).arg(nearest.vthis_dc.v));
MTP::setdc(result.c_nearestDc().vnearest_dc.v, true);
if (_countryForReg != nearest.vcountry.c_string().v.c_str()) {
_countryForReg = nearest.vcountry.c_string().v.c_str();
emit countryChanged();
MTP::setdc(nearest.vnearest_dc.v, true);
auto nearestCountry = qs(nearest.vcountry);
if (getData()->country != nearestCountry) {
getData()->country = nearestCountry;
getData()->updated.notify();
}
}
QPixmap IntroWidget::grabStep(int skip) {
return myGrab(step(skip), QRect(st::introSlideShift, 0, st::introSize.width(), st::introSize.height()));
void Widget::showControls() {
getStep()->show();
_next->show();
_next->setText(getStep()->nextButtonText());
if (getStep()->hasCover()) {
_settings->hideFast();
if (_update) _update->hideFast();
if (_changeLanguage) _changeLanguage->showFast();
} else {
_settings->showFast();
if (_update) _update->showFast();
if (_changeLanguage) _changeLanguage->hideFast();
}
if (getStep()->hasBack()) {
_back->showFast();
} else {
_back->hideFast();
}
}
void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) {
void Widget::hideControls() {
getStep()->hide();
_next->hide();
_settings->hideFast();
if (_update) _update->hideFast();
if (_changeLanguage) _changeLanguage->hideFast();
_back->hideFast();
}
void Widget::animShow(const QPixmap &bgAnimCache, bool back) {
if (App::app()) App::app()->mtpPause();
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
_a_show.stop();
step()->show();
_settings->show();
if (_update) _update->show();
if (step()->hasBack()) {
_back->showFast();
} else {
_back->hideFast();
}
showControls();
(back ? _cacheUnder : _cacheOver) = myGrab(this);
step()->hide();
_back->hideFast();
_settings->hide();
if (_update) _update->hide();
hideControls();
a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift);
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
@ -217,7 +320,7 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) {
show();
}
void IntroWidget::step_show(float64 ms, bool timer) {
void Widget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
if (dt >= 1) {
_a_show.stop();
@ -228,13 +331,9 @@ void IntroWidget::step_show(float64 ms, bool timer) {
_cacheUnder = _cacheOver = QPixmap();
setFocus();
step()->activate();
if (step()->hasBack()) {
_back->showFast();
}
_settings->show();
if (_update) _update->show();
showControls();
getStep()->activate();
if (App::app()) App::app()->mtpUnpause();
} else {
a_coordUnder.update(dt, Window::SlideAnimation::transition());
@ -244,37 +343,14 @@ void IntroWidget::step_show(float64 ms, bool timer) {
if (timer) update();
}
void IntroWidget::stop_show() {
_a_show.stop();
}
void IntroWidget::step_stage(float64 ms, bool timer) {
float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration;
float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0;
if (dt >= 1) {
_a_stage.stop();
a_coordShow.finish();
a_opacityShow.finish();
_cacheHide = _cacheShow = QPixmap();
setFocus();
step()->activate();
if (App::app()) App::app()->mtpUnpause();
} else {
a_coordShow.update(dt2, anim::easeOutCirc);
a_opacityShow.update(dt2, anim::easeInCirc);
a_coordHide.update(dt1, anim::easeInCirc);
a_opacityHide.update(dt1, anim::easeOutCirc);
}
if (timer) update();
}
void IntroWidget::paintEvent(QPaintEvent *e) {
void Widget::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
setMouseTracking(true);
if (_coverShownAnimation.animating()) {
_coverShownAnimation.step(getms());
}
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
@ -290,130 +366,406 @@ void IntroWidget::paintEvent(QPaintEvent *e) {
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
p.setOpacity(a_shadow.current());
st::slideShadow.fill(p, QRect(a_coordOver.current() - st::slideShadow.width(), 0, st::slideShadow.width(), height()));
} else if (_a_stage.animating()) {
p.setOpacity(a_opacityHide.current());
p.drawPixmap(step()->x() + st::introSlideShift + a_coordHide.current(), step()->y(), _cacheHide);
p.setOpacity(a_opacityShow.current());
p.drawPixmap(step()->x() + st::introSlideShift + a_coordShow.current(), step()->y(), _cacheShow);
}
}
QRect IntroWidget::innerRect() const {
int innerWidth = st::introSize.width() + 2 * st::introSlideShift, innerHeight = st::introSize.height();
return QRect((width() - innerWidth) / 2, (height() - innerHeight) / 2, innerWidth, (height() + innerHeight) / 2);
QRect Widget::calculateStepRect() const {
auto stepInnerTop = (height() - st::introHeight) / 2;
accumulate_max(stepInnerTop, st::introStepTopMin);
auto nextTop = stepInnerTop + st::introStepHeight;
auto additionalHeight = st::introStepHeightAdd;
auto stepWidth = width();
auto stepHeight = nextTop + additionalHeight;
return QRect(0, 0, stepWidth, stepHeight);
}
QString IntroWidget::currentCountry() const {
return _countryForReg;
}
void IntroWidget::setPhone(const QString &phone, const QString &phone_hash, bool registered) {
_phone = phone;
_phone_hash = phone_hash;
_registered = registered;
}
void IntroWidget::setCode(const QString &code) {
_code = code;
}
void IntroWidget::setPwdSalt(const QByteArray &salt) {
_pwdSalt = salt;
}
void IntroWidget::setHasRecovery(bool has) {
_hasRecovery = has;
}
void IntroWidget::setPwdHint(const QString &hint) {
_pwdHint = hint;
}
void IntroWidget::setCodeByTelegram(bool byTelegram) {
_codeByTelegram = byTelegram;
}
void IntroWidget::setCallStatus(const CallStatus &status) {
_callStatus = status;
}
const QString &IntroWidget::getPhone() const {
return _phone;
}
const QString &IntroWidget::getPhoneHash() const {
return _phone_hash;
}
const QString &IntroWidget::getCode() const {
return _code;
}
const IntroWidget::CallStatus &IntroWidget::getCallStatus() const {
return _callStatus;
}
const QByteArray &IntroWidget::getPwdSalt() const {
return _pwdSalt;
}
bool IntroWidget::getHasRecovery() const {
return _hasRecovery;
}
const QString &IntroWidget::getPwdHint() const {
return _pwdHint;
}
bool IntroWidget::codeByTelegram() const {
return _codeByTelegram;
}
void IntroWidget::resizeEvent(QResizeEvent *e) {
auto r = innerRect();
for (auto step : _stepHistory) {
step->setGeometry(r);
void Widget::resizeEvent(QResizeEvent *e) {
auto stepRect = calculateStepRect();
for_const (auto step, _stepHistory) {
step->setGeometry(stepRect);
}
updateControlsGeometry();
}
void IntroWidget::updateControlsGeometry() {
_settings->moveToRight(st::boxButtonPadding.right(), st::boxButtonPadding.top());
void Widget::moveControls() {
}
void Widget::updateControlsGeometry() {
auto shown = _coverShownAnimation.current(1.);
auto controlsTopTo = getStep()->hasCover() ? st::introCoverHeight : 0;
auto controlsTop = anim::interpolate(_controlsTopFrom, controlsTopTo, shown);
_settings->moveToRight(st::introSettingsSkip, controlsTop + st::introSettingsSkip);
if (_update) {
_update->moveToRight(st::boxButtonPadding.right() + _settings->width() + st::boxButtonPadding.left(), _settings->y());
_update->moveToRight(st::introSettingsSkip + _settings->width() + st::introSettingsSkip, _settings->y());
}
_back->moveToLeft(0, controlsTop);
auto nextTopTo = getStep()->contentTop() + st::introStepHeight;
auto nextTop = anim::interpolate(_nextTopFrom, nextTopTo, shown);
_next->moveToLeft((width() - _next->width()) / 2, nextTop);
if (_changeLanguage) {
_changeLanguage->moveToLeft((width() - _changeLanguage->width()) / 2, _next->y() + _next->height() + _changeLanguage->height());
}
if (_resetAccount) {
_resetAccount->moveToLeft((width() - _resetAccount->width()) / 2, height() - st::introResetBottom - _resetAccount->height());
}
}
void IntroWidget::finish(const MTPUser &user, const QImage &photo) {
void Widget::keyPressEvent(QKeyEvent *e) {
if (_a_show.animating() || getStep()->animating()) return;
if (e->key() == Qt::Key_Escape) {
if (getStep()->hasBack()) {
historyMove(Direction::Back);
}
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) {
getStep()->submit();
}
}
Widget::~Widget() {
for (auto step : base::take(_stepHistory)) {
delete step;
}
if (App::wnd()) App::wnd()->noIntro(this);
}
QString Widget::Step::nextButtonText() const {
return lang(lng_intro_next);
}
void Widget::Step::finish(const MTPUser &user, QImage photo) {
App::wnd()->setupMain(&user);
// "this" is already deleted here by creating the main widget.
if (!photo.isNull()) {
App::app()->uploadProfilePhoto(photo, MTP::authedId());
}
}
void IntroWidget::keyPressEvent(QKeyEvent *e) {
if (_a_show.animating() || _a_stage.animating()) return;
void Widget::Step::paintEvent(QPaintEvent *e) {
Painter p(this);
paintAnimated(p, e->rect());
}
if (e->key() == Qt::Key_Escape) {
if (step()->hasBack()) {
onBack();
void Widget::Step::resizeEvent(QResizeEvent *e) {
updateLabelsPosition();
}
void Widget::Step::updateLabelsPosition() {
myEnsureResized(_description->entity());
if (hasCover()) {
_title->moveToLeft((width() - _title->width()) / 2, contentTop() + st::introCoverTitleTop);
_description->moveToLeft((width() - _description->width()) / 2, contentTop() + st::introCoverDescriptionTop);
} else {
_title->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introTitleTop);
_description->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introDescriptionTop);
}
if (_error) {
if (_errorCentered) {
_error->entity()->resizeToWidth(width());
}
} else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) {
onStepSubmit();
myEnsureResized(_error->entity());
auto errorLeft = _errorCentered ? 0 : (contentLeft() + st::buttonRadius);
auto errorTop = contentTop() + (_errorBelowLink ? st::introErrorBelowLinkTop : st::introErrorTop);
_error->moveToLeft(errorLeft, errorTop);
}
}
void IntroWidget::rpcClear() {
for (IntroStep *step : _stepHistory) {
step->rpcClear();
void Widget::Step::setTitleText(QString richText) {
_title->setRichText(richText);
updateLabelsPosition();
}
void Widget::Step::setDescriptionText(QString richText) {
_description->entity()->setRichText(richText);
updateLabelsPosition();
}
void Widget::Step::showFinished() {
_a_show.finish();
_coverAnimation = CoverAnimation();
_slideAnimation.reset();
prepareCoverMask();
activate();
if (App::app()) App::app()->mtpUnpause();
}
bool Widget::Step::paintAnimated(Painter &p, QRect clip) {
if (_slideAnimation) {
_slideAnimation->paintFrame(p, (width() - st::introStepWidth) / 2, contentTop(), width(), getms());
if (!_slideAnimation->animating()) {
showFinished();
return false;
}
return true;
}
auto guard = base::scope_guard([this, &p] {
if (hasCover()) paintCover(p, 0);
});
auto dt = _a_show.current(getms(), 1.);
if (!_a_show.animating()) {
if (_coverAnimation.title) {
showFinished();
}
if (!QRect(0, contentTop(), width(), st::introStepHeight).intersects(clip)) {
return true;
}
return false;
}
auto easeOut = anim::easeOutCirc(1., dt);
auto arrivingAlpha = easeOut;
auto departingAlpha = 1. - easeOut;
auto showCoverMethod = easeOut;
auto hideCoverMethod = easeOut;
auto coverTop = (hasCover() ? anim::interpolate(-st::introCoverHeight, 0, showCoverMethod) : anim::interpolate(0, -st::introCoverHeight, hideCoverMethod));
paintCover(p, coverTop);
guard.dismiss();
auto positionReady = hasCover() ? showCoverMethod : hideCoverMethod;
_coverAnimation.title->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
_coverAnimation.description->paintFrame(p, positionReady, departingAlpha, arrivingAlpha);
paintContentSnapshot(p, _coverAnimation.contentSnapshotWas, departingAlpha, showCoverMethod);
paintContentSnapshot(p, _coverAnimation.contentSnapshotNow, arrivingAlpha, 1. - hideCoverMethod);
return true;
}
void Widget::Step::fillSentCodeData(const MTPauth_SentCodeType &type) {
switch (type.type()) {
case mtpc_auth_sentCodeTypeApp: {
getData()->codeByTelegram = true;
getData()->codeLength = type.c_auth_sentCodeTypeApp().vlength.v;
} break;
case mtpc_auth_sentCodeTypeSms: {
getData()->codeByTelegram = false;
getData()->codeLength = type.c_auth_sentCodeTypeSms().vlength.v;
} break;
case mtpc_auth_sentCodeTypeCall: {
getData()->codeByTelegram = false;
getData()->codeLength = type.c_auth_sentCodeTypeCall().vlength.v;
} break;
case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break;
}
}
IntroWidget::~IntroWidget() {
while (!_stepHistory.isEmpty()) {
IntroStep *back = _stepHistory.back();
_stepHistory.pop_back();
delete back;
}
if (App::wnd()) App::wnd()->noIntro(this);
void Widget::Step::showDescription() {
_description->fadeIn();
}
void Widget::Step::hideDescription() {
_description->fadeOut();
}
void Widget::Step::paintContentSnapshot(Painter &p, const QPixmap &snapshot, float64 alpha, float64 howMuchHidden) {
if (!snapshot.isNull()) {
auto contentTop = anim::interpolate(height() - (snapshot.height() / cIntRetinaFactor()), height(), howMuchHidden);
if (contentTop < height()) {
p.setOpacity(alpha);
p.drawPixmap(QPoint(contentLeft(), contentTop), snapshot, QRect(0, 0, snapshot.width(), (height() - contentTop) * cIntRetinaFactor()));
}
}
}
void Widget::Step::prepareCoverMask() {
if (!_coverMask.isNull()) return;
auto maskWidth = cIntRetinaFactor();
auto maskHeight = st::introCoverHeight * cIntRetinaFactor();
auto mask = QImage(maskWidth, maskHeight, QImage::Format_ARGB32_Premultiplied);
auto maskInts = reinterpret_cast<uint32*>(mask.bits());
t_assert(mask.depth() == (sizeof(uint32) << 3));
auto maskIntsPerLineAdded = (mask.bytesPerLine() >> 2) - maskWidth;
t_assert(maskIntsPerLineAdded >= 0);
auto realHeight = static_cast<float64>(maskHeight - 1);
for (auto y = 0; y != maskHeight; ++y) {
auto color = anim::color(st::introCoverTopBg, st::introCoverBottomBg, y / realHeight);
auto colorInt = anim::getPremultiplied(color);
for (auto x = 0; x != maskWidth; ++x) {
*maskInts++ = colorInt;
}
maskInts += maskIntsPerLineAdded;
}
_coverMask = App::pixmapFromImageInPlace(std_::move(mask));
}
void Widget::Step::paintCover(Painter &p, int top) {
auto coverHeight = top + st::introCoverHeight;
if (coverHeight > 0) {
p.drawPixmap(QRect(0, 0, width(), coverHeight), _coverMask, QRect(0, -top * cIntRetinaFactor(), _coverMask.width(), coverHeight * cIntRetinaFactor()));
}
auto left = 0;
auto right = 0;
if (width() < st::introCoverMaxWidth) {
auto iconsMaxSkip = st::introCoverMaxWidth - st::introCoverLeft.width() - st::introCoverRight.width();
auto iconsSkip = st::introCoverIconsMinSkip + (iconsMaxSkip - st::introCoverIconsMinSkip) * (width() - st::introStepWidth) / (st::introCoverMaxWidth - st::introStepWidth);
auto outside = iconsSkip + st::introCoverLeft.width() + st::introCoverRight.width() - width();
left = -outside / 2;
right = -outside - left;
}
if (top < 0) {
auto shown = float64(coverHeight) / st::introCoverHeight;
auto leftShown = qRound(shown * (left + st::introCoverLeft.width()));
left = leftShown - st::introCoverLeft.width();
auto rightShown = qRound(shown * (right + st::introCoverRight.width()));
right = rightShown - st::introCoverRight.width();
}
st::introCoverLeft.paint(p, left, coverHeight - st::introCoverLeft.height(), width());
st::introCoverRight.paint(p, width() - right - st::introCoverRight.width(), coverHeight - st::introCoverRight.height(), width());
auto planeLeft = (width() - st::introCoverIcon.width()) / 2 - st::introCoverIconLeft;
auto planeTop = top + st::introCoverIconTop;
if (top < 0 && !_hasCover) {
auto deltaLeft = -qRound(float64(st::introPlaneWidth / st::introPlaneHeight) * top);
// auto deltaTop = top;
planeLeft += deltaLeft;
// planeTop += top;
}
st::introCoverIcon.paint(p, planeLeft, planeTop, width());
}
int Widget::Step::contentLeft() const {
return (width() - st::introNextButton.width) / 2;
}
int Widget::Step::contentTop() const {
auto result = height() - st::introStepHeight - st::introStepHeightAdd;
if (_hasCover) {
auto added = 1. - snap(float64(height() - st::windowMinHeight) / (st::introStepHeightFull - st::windowMinHeight), 0., 1.);
result += qRound(added * st::introStepHeightAdd);
}
return result;
}
void Widget::Step::setErrorCentered(bool centered) {
_errorCentered = centered;
_error.destroy();
}
void Widget::Step::setErrorBelowLink(bool below) {
_errorBelowLink = below;
if (_error) {
updateLabelsPosition();
}
}
void Widget::Step::showError(const QString &text) {
_errorText = text;
if (_errorText.isEmpty()) {
if (_error) _error->fadeOut();
} else {
if (!_error) {
auto &st = _errorCentered ? st::introErrorCentered : st::introError;
_error.create(this, new Ui::FlatLabel(this, st, st::introErrorTextStyle), base::lambda<void()>(), st::introErrorDuration);
_error->hideFast();
}
_error->entity()->setText(text);
updateLabelsPosition();
_error->fadeIn();
}
}
Widget::Step::Step(QWidget *parent, Data *data, bool hasCover) : TWidget(parent)
, _data(data)
, _hasCover(hasCover)
, _title(this, _hasCover ? st::introCoverTitle : st::introTitle, st::defaultTextStyle)
, _description(this, new Ui::FlatLabel(this, _hasCover ? st::introCoverDescription : st::introDescription, _hasCover ? st::introCoverDescriptionTextStyle : st::introDescriptionTextStyle), base::lambda<void()>(), st::introErrorDuration) {
hide();
}
void Widget::Step::prepareShowAnimated(Step *after) {
if (hasCover() || after->hasCover()) {
_coverAnimation = prepareCoverAnimation(after);
prepareCoverMask();
} else {
auto leftSnapshot = after->prepareSlideAnimation();
auto rightSnapshot = prepareSlideAnimation();
_slideAnimation = std_::make_unique<Ui::SlideAnimation>();
_slideAnimation->setSnapshots(std_::move(leftSnapshot), std_::move(rightSnapshot));
_slideAnimation->setOverflowHidden(false);
}
}
Widget::Step::CoverAnimation Widget::Step::prepareCoverAnimation(Step *after) {
auto result = CoverAnimation();
result.title = Ui::FlatLabel::CrossFade(after->_title, _title, st::introBg);
result.description = Ui::FlatLabel::CrossFade(after->_description->entity(), _description->entity(), st::introBg, after->_description->pos(), _description->pos());
result.contentSnapshotWas = after->prepareContentSnapshot();
result.contentSnapshotNow = prepareContentSnapshot();
return std_::move(result);
}
QPixmap Widget::Step::prepareContentSnapshot() {
auto otherTop = _description->y() + _description->height();
auto otherRect = myrtlrect(contentLeft(), otherTop, st::introStepWidth, height() - otherTop);
return myGrab(this, otherRect);
}
QPixmap Widget::Step::prepareSlideAnimation() {
auto grabLeft = (width() - st::introStepWidth) / 2;
auto grabTop = contentTop();
return myGrab(this, QRect(grabLeft, grabTop, st::introStepWidth, st::introStepHeight));
}
void Widget::Step::showAnimated(Direction direction) {
show();
if (App::app()) App::app()->mtpPause();
hideChildren();
if (_slideAnimation) {
auto slideLeft = (direction == Direction::Back);
_slideAnimation->start(slideLeft, [this] { update(0, contentTop(), width(), st::introStepHeight); }, st::introSlideDuration);
} else {
_a_show.start([this] { update(); }, 0., 1., st::introCoverDuration);
}
}
void Widget::Step::setGoCallback(base::lambda<void(Step *step, Direction direction)> &&callback) {
_goCallback = std_::move(callback);
}
void Widget::Step::setShowResetCallback(base::lambda<void()> &&callback) {
_showResetCallback = std_::move(callback);
}
void Widget::Step::showFast() {
show();
showFinished();
}
bool Widget::Step::animating() const {
return (_slideAnimation && _slideAnimation->animating()) || _a_show.animating();
}
bool Widget::Step::hasCover() const {
return _hasCover;
}
bool Widget::Step::hasBack() const {
return false;
}
void Widget::Step::activate() {
_title->show();
_description->show();
if (!_errorText.isEmpty()) {
_error->showFast();
}
}
void Widget::Step::cancelled() {
}
void Widget::Step::finished() {
hide();
}
} // namespace Intro

View File

@ -23,77 +23,32 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class IconButton;
class RoundButton;
class LinkButton;
class SlideAnimation;
class CrossFadeAnimation;
class FlatLabel;
template <typename Widget>
class WidgetFadeWrap;
} // namespace Ui
class IntroStep;
class IntroWidget : public TWidget, public RPCSender {
namespace Intro {
class Widget : public TWidget, public RPCSender {
Q_OBJECT
public:
IntroWidget(QWidget *window);
Widget(QWidget *parent);
void animShow(const QPixmap &bgAnimCache, bool back = false);
void step_show(float64 ms, bool timer);
void stop_show();
void setInnerFocus();
void step_stage(float64 ms, bool timer);
QRect innerRect() const;
QString currentCountry() const;
enum CallStatusType {
CallWaiting,
CallCalling,
CallCalled,
CallDisabled,
};
struct CallStatus {
CallStatusType type;
int timeout;
};
void setPhone(const QString &phone, const QString &phone_hash, bool registered);
void setCode(const QString &code);
void setCallStatus(const CallStatus &status);
void setPwdSalt(const QByteArray &salt);
void setHasRecovery(bool hasRecovery);
void setPwdHint(const QString &hint);
void setCodeByTelegram(bool byTelegram);
const QString &getPhone() const;
const QString &getPhoneHash() const;
const QString &getCode() const;
const CallStatus &getCallStatus() const;
const QByteArray &getPwdSalt() const;
bool getHasRecovery() const;
const QString &getPwdHint() const;
bool codeByTelegram() const;
void finish(const MTPUser &user, const QImage &photo = QImage());
void rpcClear() override;
void langChangeTo(int32 langId);
void nextStep(IntroStep *step) {
pushStep(step, MoveForward);
}
void replaceStep(IntroStep *step) {
pushStep(step, MoveReplace);
}
~IntroWidget();
~Widget();
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
public slots:
void onStepSubmit();
void onBack();
void onChangeLang();
signals:
void countryChanged();
@ -102,86 +57,199 @@ private slots:
void onCheckUpdateStatus();
#endif // TDESKTOP_DISABLE_AUTOUPDATE
// Internal interface.
public:
struct Data {
QString country;
QString phone;
QString phoneHash;
bool phoneIsRegistered = false;
enum class CallStatus {
Waiting,
Calling,
Called,
Disabled,
};
CallStatus callStatus = CallStatus::Disabled;
int callTimeout = 0;
QString code;
int codeLength = 5;
bool codeByTelegram = false;
QByteArray pwdSalt;
bool hasRecovery = false;
QString pwdHint;
base::Observable<void> updated;
};
enum class Direction {
Back,
Forward,
Replace,
};
class Step : public TWidget, public RPCSender {
public:
Step(QWidget *parent, Data *data, bool hasCover = false);
virtual void setInnerFocus() {
setFocus();
}
void setGoCallback(base::lambda<void(Step *step, Direction direction)> &&callback);
void setShowResetCallback(base::lambda<void()> &&callback);
void prepareShowAnimated(Step *after);
void showAnimated(Direction direction);
void showFast();
bool animating() const;
bool hasCover() const;
virtual bool hasBack() const;
virtual void activate();
virtual void cancelled();
virtual void finished();
virtual void submit() = 0;
virtual QString nextButtonText() const;
int contentLeft() const;
int contentTop() const;
void setErrorCentered(bool centered);
void setErrorBelowLink(bool below);
void showError(const QString &text);
void hideError() {
showError(QString());
}
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void setTitleText(QString richText);
void setDescriptionText(QString richText);
bool paintAnimated(Painter &p, QRect clip);
void fillSentCodeData(const MTPauth_SentCodeType &type);
void showDescription();
void hideDescription();
Data *getData() const {
return _data;
}
void finish(const MTPUser &user, QImage photo = QImage());
void goBack() {
if (_goCallback) _goCallback(nullptr, Direction::Back);
}
void goNext(Step *step) {
if (_goCallback) _goCallback(step, Direction::Forward);
}
void goReplace(Step *step) {
if (_goCallback) _goCallback(step, Direction::Replace);
}
void showResetButton() {
if (_showResetCallback) _showResetCallback();
}
private:
struct CoverAnimation {
std_::unique_ptr<Ui::CrossFadeAnimation> title;
std_::unique_ptr<Ui::CrossFadeAnimation> description;
// From content top till the next button top.
QPixmap contentSnapshotWas;
QPixmap contentSnapshotNow;
};
void updateLabelsPosition();
void paintContentSnapshot(Painter &p, const QPixmap &snapshot, float64 alpha, float64 howMuchHidden);
CoverAnimation prepareCoverAnimation(Step *step);
QPixmap prepareContentSnapshot();
QPixmap prepareSlideAnimation();
void showFinished();
void prepareCoverMask();
void paintCover(Painter &p, int top);
Data *_data = nullptr;
bool _hasCover = false;
base::lambda<void(Step *step, Direction direction)> _goCallback;
base::lambda<void()> _showResetCallback;
ChildWidget<Ui::FlatLabel> _title;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _description;
bool _errorCentered = false;
bool _errorBelowLink = false;
QString _errorText;
ChildWidget<Ui::WidgetFadeWrap<Ui::FlatLabel>> _error = { nullptr };
FloatAnimation _a_show;
CoverAnimation _coverAnimation;
std_::unique_ptr<Ui::SlideAnimation> _slideAnimation;
QPixmap _coverMask;
};
private:
void step_show(float64 ms, bool timer);
void changeLanguage(int32 languageId);
void updateControlsGeometry();
QPixmap grabStep(int skip = 0);
Data *getData() {
return &_data;
}
int _langChangeTo = 0;
void fixOrder();
void showControls();
void hideControls();
void moveControls();
QRect calculateStepRect() const;
Animation _a_stage;
QPixmap _cacheHide, _cacheShow;
int _cacheHideIndex = 0;
int _cacheShowIndex = 0;
anim::ivalue a_coordHide, a_coordShow;
anim::fvalue a_opacityHide, a_opacityShow;
void showResetButton();
void resetAccount();
void resetAccountSure();
void resetDone(const MTPBool &result);
bool resetFail(const RPCError &error);
Animation _a_show;
QPixmap _cacheUnder, _cacheOver;
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_shadow;
QVector<IntroStep*> _stepHistory;
IntroStep *step(int skip = 0) {
QVector<Step*> _stepHistory;
Step *getStep(int skip = 0) {
t_assert(_stepHistory.size() + skip > 0);
return _stepHistory.at(_stepHistory.size() - skip - 1);
}
enum MoveType {
MoveBack,
MoveForward,
MoveReplace,
};
void historyMove(MoveType type);
void pushStep(IntroStep *step, MoveType type);
void historyMove(Direction direction);
void moveToStep(Step *step, Direction direction);
void appendStep(Step *step);
void gotNearestDC(const MTPNearestDc &dc);
QString _countryForReg;
Data _data;
QString _phone, _phone_hash;
CallStatus _callStatus = { CallDisabled, 0 };
bool _registered = false;
QString _code;
QByteArray _pwdSalt;
bool _hasRecovery = false;
bool _codeByTelegram = false;
QString _pwdHint;
QString _firstname, _lastname;
FloatAnimation _coverShownAnimation;
int _nextTopFrom = 0;
int _controlsTopFrom = 0;
ChildWidget<Ui::WidgetFadeWrap<Ui::IconButton>> _back;
ChildWidget<Ui::RoundButton> _settings;
ChildWidget<Ui::RoundButton> _update = { nullptr };
ChildWidget<Ui::WidgetFadeWrap<Ui::RoundButton>> _update = { nullptr };
ChildWidget<Ui::WidgetFadeWrap<Ui::RoundButton>> _settings;
float64 _backFrom = 0.;
float64 _backTo = 0.;
ChildWidget<Ui::RoundButton> _next;
ChildWidget<Ui::WidgetFadeWrap<Ui::LinkButton>> _changeLanguage = { nullptr };
ChildWidget<Ui::WidgetFadeWrap<Ui::RoundButton>> _resetAccount = { nullptr };
mtpRequestId _resetRequest = 0;
};
class IntroStep : public TWidget, public RPCSender {
public:
IntroStep(IntroWidget *parent) : TWidget(parent) {
}
virtual bool hasBack() const {
return false;
}
virtual void activate() {
show();
}
virtual void cancelled() {
}
virtual void finished() {
hide();
}
virtual void onSubmit() = 0;
protected:
IntroWidget *intro() {
IntroWidget *result = qobject_cast<IntroWidget*>(parentWidget());
t_assert(result != nullptr);
return result;
}
};
} // namespace Intro

View File

@ -173,7 +173,6 @@ class Manager : public QObject {
Q_OBJECT
public:
Manager();
void writeMap(bool fast);
@ -182,13 +181,11 @@ public:
void writingLocations();
void finish();
public slots:
public slots:
void mapWriteTimeout();
void locationsWriteTimeout();
private:
QTimer _mapWriteTimer;
QTimer _locationsWriteTimer;

View File

@ -2269,7 +2269,7 @@ void MainWidget::ui_showPeerHistory(quint64 peerId, qint32 showAtMsgId, Ui::Show
_topBar->hide();
_history->hide();
if (!_a_show.animating()) {
if (!animationParams.oldContentCache.isNull()) {
if (!animationParams.oldContentCache.isNull() && !App::passcoded()) {
_dialogs->showAnimated(back ? Window::SlideDirection::FromLeft : Window::SlideDirection::FromRight, animationParams);
} else {
_dialogs->showFast();

View File

@ -213,11 +213,7 @@ void MainWindow::clearWidgets() {
Ui::hideLayer(true);
_passcode.destroyDelayed();
_main.destroy();
if (_intro) {
_intro->stop_show();
_intro->rpcClear();
_intro.destroyDelayed();
}
_intro.destroy();
if (_mediaView) {
hideMediaview();
_mediaView->rpcClear();
@ -229,10 +225,10 @@ QPixmap MainWindow::grabInner() {
QPixmap result;
if (_intro) {
result = myGrab(_intro);
} else if (_main) {
result = myGrab(_main);
} else if (_passcode) {
result = myGrab(_passcode);
} else if (_main) {
result = myGrab(_main);
}
return result;
}
@ -240,10 +236,9 @@ QPixmap MainWindow::grabInner() {
void MainWindow::clearPasscode() {
if (!_passcode) return;
QPixmap bg = grabInner();
auto bg = grabInner();
_passcode->stop_show();
_passcode.destroyDelayed();
_passcode.destroy();
if (_intro) {
_intro->animShow(bg, true);
} else {
@ -260,10 +255,6 @@ void MainWindow::clearPasscode() {
void MainWindow::setupPasscode() {
auto animated = (_main || _intro);
auto bg = animated ? grabInner() : QPixmap();
if (_passcode) {
_passcode->stop_show();
_passcode.destroyDelayed();
}
_passcode.create(bodyWidget());
updateControlsGeometry();
@ -446,10 +437,6 @@ void MainWindow::updateConnectingStatus() {
}
}
IntroWidget *MainWindow::introWidget() {
return _intro;
}
MainWidget *MainWindow::mainWidget() {
return _main;
}
@ -650,6 +637,8 @@ void MainWindow::setInnerFocus() {
_settings->setInnerFocus();
} else if (_main) {
_main->setInnerFocus();
} else if (_intro) {
_intro->setInnerFocus();
}
}
@ -834,7 +823,7 @@ void MainWindow::activate() {
}
}
void MainWindow::noIntro(IntroWidget *was) {
void MainWindow::noIntro(Intro::Widget *was) {
if (was == _intro) {
_intro = nullptr;
}

View File

@ -27,11 +27,14 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
class MediaView;
class PasscodeWidget;
class IntroWidget;
class MainWidget;
class LayerStackWidget;
class LayerWidget;
namespace Intro {
class Widget;
} // namespace Intro
namespace Local {
class ClearManager;
} // namespace Local
@ -99,7 +102,6 @@ public:
void mtpStateChanged(int32 dc, int32 state);
IntroWidget *introWidget();
MainWidget *mainWidget();
PasscodeWidget *passcodeWidget();
@ -112,7 +114,7 @@ public:
void activate();
void noIntro(IntroWidget *was);
void noIntro(Intro::Widget *was);
void noMain(MainWidget *was);
void noLayerStack(LayerStackWidget *was);
void layerFinishedHide(LayerStackWidget *was);
@ -239,7 +241,7 @@ private:
mtpRequestId _serviceHistoryRequest = 0;
ChildWidget<PasscodeWidget> _passcode = { nullptr };
ChildWidget<IntroWidget> _intro = { nullptr };
ChildWidget<Intro::Widget> _intro = { nullptr };
ChildWidget<MainWidget> _main = { nullptr };
ChildWidget<Settings::Widget> _settings = { nullptr };
ChildWidget<LayerStackWidget> _layerBg = { nullptr };

View File

@ -30,7 +30,6 @@ class HTTPConnection : public AbstractConnection {
Q_OBJECT
public:
HTTPConnection(QThread *thread);
void sendData(mtpBuffer &buffer) override;
@ -46,15 +45,13 @@ public:
QString transport() const override;
public slots:
public slots:
void requestFinished(QNetworkReply *reply);
static mtpBuffer handleResponse(QNetworkReply *reply);
static bool handleError(QNetworkReply *reply); // returnes "maybe bad key"
private:
enum Status {
WaitingHttp = 0,
UsingHttp,

View File

@ -754,7 +754,7 @@ void ping() {
}
void cancel(mtpRequestId requestId) {
if (!_started) return;
if (!_started || !requestId) return;
mtpMsgId msgId = 0;
requestsDelays.remove(requestId);

View File

@ -29,14 +29,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "styles/style_boxes.h"
#include "window/slide_animation.h"
#include "window/window_slide_animation.h"
PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent)
, _a_show(animation(this, &PasscodeWidget::step_show))
, _passcode(this, st::passcodeInput)
, _submit(this, lang(lng_passcode_submit), st::passcodeSubmit)
, _logout(this, lang(lng_passcode_logout)) {
_passcode->setEchoMode(QLineEdit::Password);
connect(_passcode, SIGNAL(changed()), this, SLOT(onChanged()));
connect(_passcode, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
@ -49,12 +48,12 @@ PasscodeWidget::PasscodeWidget(QWidget *parent) : TWidget(parent)
void PasscodeWidget::onSubmit() {
if (_passcode->text().isEmpty()) {
_passcode->notaBene();
_passcode->showError();
return;
}
if (!passcodeCanTry()) {
_error = lang(lng_flood_error);
_passcode->notaBene();
_passcode->showError();
update();
return;
}
@ -62,7 +61,8 @@ void PasscodeWidget::onSubmit() {
if (App::main()) {
if (Local::checkPasscode(_passcode->text().toUtf8())) {
cSetPasscodeBadTries(0);
App::wnd()->clearPasscode();
App::wnd()->clearPasscode(); // Destroys this widget.
return;
} else {
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
cSetPasscodeLastTry(getms(true));
@ -93,7 +93,7 @@ void PasscodeWidget::onSubmit() {
void PasscodeWidget::onError() {
_error = lang(lng_passcode_wrong);
_passcode->selectAll();
_passcode->notaBene();
_passcode->showError();
update();
}

View File

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
namespace Ui {
class FlatInput;
class PasswordInput;
class LinkButton;
class RoundButton;
} // namespace Ui
@ -56,7 +56,7 @@ private:
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_shadow;
ChildWidget<Ui::FlatInput> _passcode;
ChildWidget<Ui::PasswordInput> _passcode;
ChildWidget<Ui::RoundButton> _submit;
ChildWidget<Ui::LinkButton> _logout;
QString _error;

View File

@ -144,7 +144,7 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
void CloudPasswordState::paintEvent(QPaintEvent *e) {
Painter p(this);
auto text = st::linkFont->elided(_waitingConfirm, width() - _turnOff->width());
auto text = st::boxTextFont->elided(_waitingConfirm, width() - _turnOff->width() - st::boxTextFont->spacew);
if (!text.isEmpty()) {
p.setPen(st::windowFg);
p.setFont(st::boxTextFont);

View File

@ -149,7 +149,6 @@ emojiPanShowDuration: 200;
emojiPanDuration: 200;
emojiPanHover: windowBgOver;
emojiPanSlideDuration: 200;
emojiPanSlideDelta: 0; // between hide start and show start
emojiPanHeader: 42px;
emojiPanHeaderFont: semiboldFont;

View File

@ -73,8 +73,9 @@ void AbstractButton::mouseReleaseEvent(QMouseEvent *e) {
_modifiers = e->modifiers();
if (_clickedCallback) {
_clickedCallback();
} else {
emit clicked();
}
emit clicked();
} else {
leaveEvent(e);
}

View File

@ -269,6 +269,14 @@ FORCE_INLINE Shifted shifted(QColor color) {
return reshifted(components * alpha);
}
FORCE_INLINE uint32 getPremultiplied(QColor color) {
// Make it premultiplied.
auto alpha = static_cast<uint32>((color.alpha() & 0xFF) + 1);
auto components = Shifted(static_cast<uint32>(color.blue() & 0xFF) | (static_cast<uint32>(color.green() & 0xFF) << 16),
static_cast<uint32>(color.red() & 0xFF) | (static_cast<uint32>(255) << 16));
return unshifted(components * alpha);
}
FORCE_INLINE uint32 getAlpha(Shifted components) {
return (components.high & 0x00FF0000U) >> 16;
}
@ -344,6 +352,16 @@ FORCE_INLINE Shifted shifted(QColor color) {
return reshifted(components * alpha);
}
FORCE_INLINE uint32 getPremultiplied(QColor color) {
// Make it premultiplied.
auto alpha = static_cast<uint64>((color.alpha() & 0xFF) + 1);
auto components = static_cast<uint64>(color.blue() & 0xFF)
| (static_cast<uint64>(color.green() & 0xFF) << 16)
| (static_cast<uint64>(color.red() & 0xFF) << 32)
| (static_cast<uint64>(255) << 48);
return unshifted(components * alpha);
}
FORCE_INLINE uint32 getAlpha(Shifted components) {
return (components.value & 0x00FF000000000000ULL) >> 48;
}

View File

@ -40,7 +40,8 @@ void PeerAvatarButton::paintEvent(QPaintEvent *e) {
}
}
NewAvatarButton::NewAvatarButton(QWidget *parent, int size) : RippleButton(parent, st::defaultActiveButton.ripple) {
NewAvatarButton::NewAvatarButton(QWidget *parent, int size, QPoint position) : RippleButton(parent, st::defaultActiveButton.ripple)
, _position(position) {
resize(size, size);
}
@ -58,7 +59,7 @@ void NewAvatarButton::paintEvent(QPaintEvent *e) {
paintRipple(p, 0, 0, getms());
st::newGroupPhotoIcon.paint(p, st::newGroupPhotoIconPosition, width());
st::newGroupPhotoIcon.paint(p, _position, width());
}
void NewAvatarButton::setImage(const QImage &image) {

View File

@ -47,7 +47,7 @@ private:
class NewAvatarButton : public RippleButton {
public:
NewAvatarButton(QWidget *parent, int size);
NewAvatarButton(QWidget *parent, int size, QPoint position);
void setImage(const QImage &image);
@ -58,6 +58,7 @@ protected:
private:
QPixmap _image;
QPoint _position;
};

View File

@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/contactsbox.h"
#include "countries.h"
#include "styles/style_boxes.h"
#include "styles/style_intro.h"
namespace {
@ -89,50 +90,33 @@ QString findValidCode(QString fullCode) {
return "";
}
CountryInput::CountryInput(QWidget *parent, const style::countryInput &st) : QWidget(parent), _st(st), _active(false), _text(lang(lng_country_code)) {
CountryInput::CountryInput(QWidget *parent, const style::InputField &st) : TWidget(parent)
, _st(st)
, _text(lang(lng_country_code)) {
initCountries();
resize(_st.width, _st.height + _st.ptrSize.height());
QImage trImage(_st.ptrSize.width(), _st.ptrSize.height(), QImage::Format_ARGB32_Premultiplied);
{
static const QPoint trPoints[3] = {
QPoint(0, 0),
QPoint(_st.ptrSize.width(), 0),
QPoint(qCeil(trImage.width() / 2.), trImage.height())
};
QPainter p(&trImage);
p.setRenderHint(QPainter::Antialiasing);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(0, 0, trImage.width(), trImage.height(), Qt::transparent);
p.setPen(Qt::NoPen);
p.setBrush(_st.bgColor);
p.drawPolygon(trPoints, 3);
}
_arrow = App::pixmapFromImageInPlace(std_::move(trImage));
_inner = QRect(0, 0, _st.width, _st.height);
_arrowRect = QRect((st::introCountryCode.width - _arrow.width() - 1) / 2, _st.height, _arrow.width(), _arrow.height());
resize(_st.width, _st.height);
}
void CountryInput::paintEvent(QPaintEvent *e) {
QPainter p(this);
Painter p(this);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.setBrush(_st.bgColor);
p.setPen(Qt::NoPen);
p.drawRoundedRect(_inner, st::buttonRadius, st::buttonRadius);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
QRect r(rect().intersected(e->rect()));
if (_st.textBg->c.alphaF() > 0.) {
p.fillRect(r, _st.textBg);
}
if (_st.border) {
p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b);
}
p.drawPixmap(_arrowRect.x(), _arrowRect.top(), _arrow);
st::introCountryIcon.paint(p, width() - st::introCountryIcon.width() - st::introCountryIconPosition.x(), st::introCountryIconPosition.y(), width());
p.setFont(_st.font);
p.setPen(st::windowFg);
p.drawText(rect().marginsRemoved(_st.textMrg), _text, QTextOption(_st.align));
p.setPen(_st.textFg);
p.drawText(rect().marginsRemoved(_st.textMargins), _text, _st.textAlign);
}
void CountryInput::mouseMoveEvent(QMouseEvent *e) {
bool newActive = _inner.contains(e->pos()) || _arrowRect.contains(e->pos());
bool newActive = rect().contains(e->pos());
if (_active != newActive) {
_active = newActive;
setCursor(_active ? style::cur_pointer : style::cur_default);
@ -192,7 +176,7 @@ bool CountryInput::onChooseCountry(const QString &iso) {
}
void CountryInput::setText(const QString &newText) {
_text = _st.font->elided(newText, width() - _st.textMrg.left() - _st.textMrg.right());
_text = _st.font->elided(newText, width() - _st.textMargins.left() - _st.textMargins.right());
}
CountrySelectBox::CountrySelectBox() : ItemListBox(st::countriesScroll, st::boxWidth)

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/effects/rect_shadow.h"
#include "boxes/abstractbox.h"
#include "styles/style_intro.h"
#include "styles/style_widgets.h"
QString findValidCode(QString fullCode);
@ -30,11 +30,11 @@ namespace Ui {
class MultiSelect;
} // namespace Ui
class CountryInput : public QWidget {
class CountryInput : public TWidget {
Q_OBJECT
public:
CountryInput(QWidget *parent, const style::countryInput &st);
CountryInput(QWidget *parent, const style::InputField &st);
public slots:
void onChooseCode(const QString &code);
@ -53,10 +53,8 @@ protected:
private:
void setText(const QString &newText);
QPixmap _arrow;
QRect _inner, _arrowRect;
const style::countryInput &_st;
bool _active;
const style::InputField &_st;
bool _active = false;
QString _text;
};

View File

@ -0,0 +1,67 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/effects/slide_animation.h"
namespace Ui {
void SlideAnimation::setSnapshots(QPixmap leftSnapshot, QPixmap rightSnapshot) {
_leftSnapshot = std_::move(leftSnapshot);
_rightSnapshot = std_::move(rightSnapshot);
t_assert(!_leftSnapshot.isNull());
t_assert(!_rightSnapshot.isNull());
_leftSnapshot.setDevicePixelRatio(cRetinaFactor());
_rightSnapshot.setDevicePixelRatio(cRetinaFactor());
}
void SlideAnimation::paintFrame(Painter &p, int x, int y, int outerWidth, uint64 ms) {
auto dt = _animation.current(ms, 1.);
if (!animating()) return;
auto easeOut = anim::easeOutCirc(1., dt);
auto easeIn = anim::easeInCirc(1., dt);
auto arrivingAlpha = easeIn;
auto departingAlpha = 1. - easeOut;
auto leftCoord = (_slideLeft ? anim::interpolate(-_leftSnapshotWidth, 0, easeOut) : anim::interpolate(0, -_leftSnapshotWidth, easeIn));
auto leftAlpha = (_slideLeft ? arrivingAlpha : departingAlpha);
auto rightCoord = (_slideLeft ? anim::interpolate(0, _rightSnapshotWidth, easeIn) : anim::interpolate(_rightSnapshotWidth, 0, easeOut));
auto rightAlpha = (_slideLeft ? departingAlpha : arrivingAlpha);
if (_overflowHidden) {
auto leftWidth = (_leftSnapshotWidth + leftCoord);
if (leftWidth > 0) {
p.setOpacity(leftAlpha);
p.drawPixmap(x, y, leftWidth, _leftSnapshotHeight, _leftSnapshot, (_leftSnapshot.width() - leftWidth * cIntRetinaFactor()), 0, leftWidth * cIntRetinaFactor(), _leftSnapshot.height());
}
auto rightWidth = _rightSnapshotWidth - rightCoord;
if (rightWidth > 0) {
p.setOpacity(rightAlpha);
p.drawPixmap(x + rightCoord, y, _rightSnapshot, 0, 0, rightWidth * cIntRetinaFactor(), _rightSnapshot.height());
}
} else {
p.setOpacity(leftAlpha);
p.drawPixmap(x + leftCoord, y, _leftSnapshot);
p.setOpacity(rightAlpha);
p.drawPixmap(x + rightCoord, y, _rightSnapshot);
}
}
} // namespace Ui

View File

@ -0,0 +1,66 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class SlideAnimation {
public:
void setSnapshots(QPixmap leftSnapshot, QPixmap rightSnapshot);
void setOverflowHidden(bool hidden) {
_overflowHidden = hidden;
}
template <typename Lambda>
void start(bool slideLeft, Lambda &&updateCallback, float64 duration);
void paintFrame(Painter &p, int x, int y, int outerWidth, uint64 ms);
bool animating() const {
return _animation.animating();
}
private:
FloatAnimation _animation;
QPixmap _leftSnapshot;
QPixmap _rightSnapshot;
bool _slideLeft = false;
bool _overflowHidden = true;
int _leftSnapshotWidth = 0;
int _leftSnapshotHeight = 0;
int _rightSnapshotWidth = 0;
};
template <typename Lambda>
void SlideAnimation::start(bool slideLeft, Lambda &&updateCallback, float64 duration) {
_slideLeft = slideLeft;
if (_slideLeft) {
std_::swap_moveable(_leftSnapshot, _rightSnapshot);
}
_leftSnapshotWidth = _leftSnapshot.width() / cIntRetinaFactor();
_leftSnapshotHeight = _leftSnapshot.height() / cIntRetinaFactor();
_rightSnapshotWidth = _rightSnapshot.width() / cIntRetinaFactor();
_animation.start(std_::forward<Lambda>(updateCallback), 0., 1., duration);
}
} // namespace Ui

View File

@ -90,6 +90,7 @@ void FadeAnimation::fadeOut(int duration) {
void FadeAnimation::startAnimation(int duration) {
if (_cache.isNull()) {
_widget->showChildren();
_cache = myGrab(_widget);
_widget->hideChildren();
}

View File

@ -67,7 +67,7 @@ QPixmap myGrab(TWidget *target, QRect rect) {
myEnsureResized(target);
if (rect.isNull()) rect = target->rect();
QPixmap result(rect.size() * cRetinaFactor());
auto result = QPixmap(rect.size() * cRetinaFactor());
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
@ -75,7 +75,24 @@ QPixmap myGrab(TWidget *target, QRect rect) {
target->render(&result, QPoint(0, 0), rect, QWidget::DrawChildren | QWidget::IgnoreMask);
target->grabFinish();
return result;
return std_::move(result);
}
QImage myGrabImage(TWidget *target, QRect rect) {
myEnsureResized(target);
if (rect.isNull()) rect = target->rect();
auto result = QImage(rect.size() * cRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
if (!target->testAttribute(Qt::WA_OpaquePaintEvent)) {
result.fill(Qt::transparent);
}
target->grabStart();
target->render(&result, QPoint(0, 0), rect, QWidget::DrawChildren | QWidget::IgnoreMask);
target->grabFinish();
return std_::move(result);
}
void sendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton button, const QPoint &globalPoint) {

View File

@ -207,6 +207,7 @@ protected:
void myEnsureResized(QWidget *target);
QPixmap myGrab(TWidget *target, QRect rect = QRect());
QImage myGrabImage(TWidget *target, QRect rect = QRect());
class SingleDelayedCall : public QObject {
Q_OBJECT

View File

@ -255,6 +255,7 @@ void RoundButton::updateText() {
_secondaryTextWidth = _secondaryText.isEmpty() ? 0 : _st.font->width(_secondaryText);
resizeToText();
update();
}
void RoundButton::resizeToText() {

View File

@ -1798,199 +1798,6 @@ void FlatInput::notaBene() {
_a_appearance.start();
}
CountryCodeInput::CountryCodeInput(QWidget *parent, const style::FlatInput &st) : FlatInput(parent, st)
, _nosignal(false) {
}
void CountryCodeInput::startErasing(QKeyEvent *e) {
setFocus();
keyPressEvent(e);
}
void CountryCodeInput::codeSelected(const QString &code) {
QString wasText(getLastText()), newText = '+' + code;
setText(newText);
_nosignal = true;
correctValue(wasText, newText);
_nosignal = false;
emit changed();
}
void CountryCodeInput::correctValue(const QString &was, QString &now) {
QString newText, addToNumber;
int oldPos(cursorPosition()), newPos(-1), oldLen(now.length()), start = 0, digits = 5;
newText.reserve(oldLen + 1);
newText += '+';
if (oldLen && now[0] == '+') {
++start;
}
for (int i = start; i < oldLen; ++i) {
QChar ch(now[i]);
if (ch.isDigit()) {
if (!digits || !--digits) {
addToNumber += ch;
} else {
newText += ch;
}
}
if (i == oldPos) {
newPos = newText.length();
}
}
if (!addToNumber.isEmpty()) {
QString validCode = findValidCode(newText.mid(1));
addToNumber = newText.mid(1 + validCode.length()) + addToNumber;
newText = '+' + validCode;
}
if (newPos < 0 || newPos > newText.length()) {
newPos = newText.length();
}
if (newText != now) {
now = newText;
setText(newText);
updatePlaceholder();
if (newPos != oldPos) {
setCursorPosition(newPos);
}
}
if (!_nosignal && was != newText) {
emit codeChanged(newText.mid(1));
}
if (!addToNumber.isEmpty()) {
emit addedToNumber(addToNumber);
}
}
PhonePartInput::PhonePartInput(QWidget *parent, const style::FlatInput &st) : FlatInput(parent, st, lang(lng_phone_ph)) {
}
void PhonePartInput::paintEvent(QPaintEvent *e) {
FlatInput::paintEvent(e);
Painter p(this);
auto t = text();
if (!_pattern.isEmpty() && !t.isEmpty()) {
auto ph = placeholder().mid(t.size());
if (!ph.isEmpty()) {
p.setClipRect(rect());
auto phRect = placeholderRect();
int tw = phFont()->width(t);
if (tw < phRect.width()) {
phRect.setLeft(phRect.left() + tw);
phPrepare(p);
p.drawText(phRect, ph, style::al_left);
}
}
}
}
void PhonePartInput::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Backspace && text().isEmpty()) {
emit voidBackspace(e);
} else {
FlatInput::keyPressEvent(e);
}
}
void PhonePartInput::correctValue(const QString &was, QString &now) {
QString newText;
int oldPos(cursorPosition()), newPos(-1), oldLen(now.length()), digitCount = 0;
for (int i = 0; i < oldLen; ++i) {
if (now[i].isDigit()) {
++digitCount;
}
}
if (digitCount > MaxPhoneTailLength) digitCount = MaxPhoneTailLength;
bool inPart = !_pattern.isEmpty();
int curPart = -1, leftInPart = 0;
newText.reserve(oldLen);
for (int i = 0; i < oldLen; ++i) {
if (i == oldPos && newPos < 0) {
newPos = newText.length();
}
QChar ch(now[i]);
if (ch.isDigit()) {
if (!digitCount--) {
break;
}
if (inPart) {
if (leftInPart) {
--leftInPart;
} else {
newText += ' ';
++curPart;
inPart = curPart < _pattern.size();
leftInPart = inPart ? (_pattern.at(curPart) - 1) : 0;
++oldPos;
}
}
newText += ch;
} else if (ch == ' ' || ch == '-' || ch == '(' || ch == ')') {
if (inPart) {
if (leftInPart) {
} else {
newText += ch;
++curPart;
inPart = curPart < _pattern.size();
leftInPart = inPart ? _pattern.at(curPart) : 0;
}
} else {
newText += ch;
}
}
}
int32 newlen = newText.size();
while (newlen > 0 && newText.at(newlen - 1).isSpace()) {
--newlen;
}
if (newlen < newText.size()) newText = newText.mid(0, newlen);
if (newPos < 0) {
newPos = newText.length();
}
if (newText != now) {
now = newText;
setText(now);
updatePlaceholder();
setCursorPosition(newPos);
}
}
void PhonePartInput::addedToNumber(const QString &added) {
setFocus();
QString wasText(getLastText()), newText = added + wasText;
setText(newText);
setCursorPosition(added.length());
correctValue(wasText, newText);
updatePlaceholder();
}
void PhonePartInput::onChooseCode(const QString &code) {
_pattern = phoneNumberParse(code);
if (!_pattern.isEmpty() && _pattern.at(0) == code.size()) {
_pattern.pop_front();
} else {
_pattern.clear();
}
if (_pattern.isEmpty()) {
setPlaceholder(lang(lng_phone_ph));
} else {
QString ph;
ph.reserve(20);
for (int i = 0, l = _pattern.size(); i < l; ++i) {
ph.append(' ');
ph.append(QString(_pattern.at(i), QChar(0x2212)));
}
setPlaceholder(ph);
}
auto newText = getLastText();
correctValue(newText, newText);
setPlaceholderFast(!_pattern.isEmpty());
updatePlaceholder();
}
InputArea::InputArea(QWidget *parent, const style::InputArea &st, const QString &ph, const QString &val) : TWidget(parent)
, _maxLength(-1)
, _inner(this)
@ -3533,6 +3340,23 @@ MaskedInputField::MaskedInputField(QWidget *parent, const style::InputField &st,
updatePlaceholder();
}
void MaskedInputField::setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos) {
if (newPos < 0 || newPos > newText.size()) {
newPos = newText.size();
}
auto updateText = (newText != now);
if (updateText) {
now = newText;
setText(now);
updatePlaceholder();
}
auto updateCursorPosition = (newPos != nowCursor) || updateText;
if (updateCursorPosition) {
nowCursor = newPos;
setCursorPosition(nowCursor);
}
}
void MaskedInputField::customUpDown(bool custom) {
_customUpDown = custom;
}
@ -3770,9 +3594,6 @@ QRect MaskedInputField::placeholderRect() const {
return rect().marginsRemoved(_st.textMargins + _st.placeholderMargins);
}
void MaskedInputField::correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
}
void MaskedInputField::paintPlaceholder(Painter &p) {
bool drawPlaceholder = _placeholderVisible;
if (_a_placeholderShift.animating()) {
@ -3860,6 +3681,195 @@ void MaskedInputField::onCursorPositionChanged(int oldPosition, int position) {
_oldcursor = position;
}
CountryCodeInput::CountryCodeInput(QWidget *parent, const style::InputField &st) : MaskedInputField(parent, st)
, _nosignal(false) {
}
void CountryCodeInput::startErasing(QKeyEvent *e) {
setFocus();
keyPressEvent(e);
}
void CountryCodeInput::codeSelected(const QString &code) {
auto wasText = getLastText();
auto wasCursor = cursorPosition();
auto newText = '+' + code;
auto newCursor = newText.size();
setText(newText);
_nosignal = true;
correctValue(wasText, wasCursor, newText, newCursor);
_nosignal = false;
emit changed();
}
void CountryCodeInput::correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
QString newText, addToNumber;
int oldPos(nowCursor), newPos(-1), oldLen(now.length()), start = 0, digits = 5;
newText.reserve(oldLen + 1);
if (oldLen && now[0] == '+') {
if (start == oldPos) {
newPos = newText.length();
}
++start;
}
newText += '+';
for (int i = start; i < oldLen; ++i) {
if (i == oldPos) {
newPos = newText.length();
}
auto ch = now[i];
if (ch.isDigit()) {
if (!digits || !--digits) {
addToNumber += ch;
} else {
newText += ch;
}
}
}
if (!addToNumber.isEmpty()) {
auto validCode = findValidCode(newText.mid(1));
addToNumber = newText.mid(1 + validCode.length()) + addToNumber;
newText = '+' + validCode;
}
setCorrectedText(now, nowCursor, newText, newPos);
if (!_nosignal && was != newText) {
emit codeChanged(newText.mid(1));
}
if (!addToNumber.isEmpty()) {
emit addedToNumber(addToNumber);
}
}
PhonePartInput::PhonePartInput(QWidget *parent, const style::InputField &st) : MaskedInputField(parent, st, lang(lng_phone_ph)) {
}
void PhonePartInput::paintPlaceholder(Painter &p) {
auto t = getLastText();
if (!_pattern.isEmpty() && !t.isEmpty()) {
auto ph = placeholder().mid(t.size());
if (!ph.isEmpty()) {
p.setClipRect(rect());
auto phRect = placeholderRect();
int tw = phFont()->width(t);
if (tw < phRect.width()) {
phRect.setLeft(phRect.left() + tw);
placeholderPreparePaint(p);
p.drawText(phRect, ph, style::al_topleft);
}
}
} else {
MaskedInputField::paintPlaceholder(p);
}
}
void PhonePartInput::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Backspace && getLastText().isEmpty()) {
emit voidBackspace(e);
} else {
MaskedInputField::keyPressEvent(e);
}
}
void PhonePartInput::correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
QString newText;
int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = 0;
for (int i = 0; i < oldLen; ++i) {
if (now[i].isDigit()) {
++digitCount;
}
}
if (digitCount > MaxPhoneTailLength) digitCount = MaxPhoneTailLength;
bool inPart = !_pattern.isEmpty();
int curPart = -1, leftInPart = 0;
newText.reserve(oldLen);
for (int i = 0; i < oldLen; ++i) {
if (i == oldPos && newPos < 0) {
newPos = newText.length();
}
auto ch = now[i];
if (ch.isDigit()) {
if (!digitCount--) {
break;
}
if (inPart) {
if (leftInPart) {
--leftInPart;
} else {
newText += ' ';
++curPart;
inPart = curPart < _pattern.size();
leftInPart = inPart ? (_pattern.at(curPart) - 1) : 0;
++oldPos;
}
}
newText += ch;
} else if (ch == ' ' || ch == '-' || ch == '(' || ch == ')') {
if (inPart) {
if (leftInPart) {
} else {
newText += ch;
++curPart;
inPart = curPart < _pattern.size();
leftInPart = inPart ? _pattern.at(curPart) : 0;
}
} else {
newText += ch;
}
}
}
auto newlen = newText.size();
while (newlen > 0 && newText.at(newlen - 1).isSpace()) {
--newlen;
}
if (newlen < newText.size()) {
newText = newText.mid(0, newlen);
}
setCorrectedText(now, nowCursor, newText, newPos);
}
void PhonePartInput::addedToNumber(const QString &added) {
setFocus();
auto wasText = getLastText();
auto wasCursor = cursorPosition();
auto newText = added + wasText;
auto newCursor = newText.size();
setText(newText);
setCursorPosition(added.length());
correctValue(wasText, wasCursor, newText, newCursor);
updatePlaceholder();
}
void PhonePartInput::onChooseCode(const QString &code) {
_pattern = phoneNumberParse(code);
if (!_pattern.isEmpty() && _pattern.at(0) == code.size()) {
_pattern.pop_front();
} else {
_pattern.clear();
}
if (_pattern.isEmpty()) {
setPlaceholder(lang(lng_phone_ph));
} else {
QString ph;
ph.reserve(20);
for (int i = 0, l = _pattern.size(); i < l; ++i) {
ph.append(' ');
ph.append(QString(_pattern.at(i), QChar(0x2212)));
}
setPlaceholder(ph);
}
auto wasText = getLastText();
auto wasCursor = cursorPosition();
auto newText = getLastText();
auto newCursor = newText.size();
correctValue(wasText, wasCursor, newText, newCursor);
setPlaceholderFast(!_pattern.isEmpty());
updatePlaceholder();
}
PasswordInput::PasswordInput(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : MaskedInputField(parent, st, ph, val) {
setEchoMode(QLineEdit::Password);
}
@ -3873,29 +3883,22 @@ PortInput::PortInput(QWidget *parent, const style::InputField &st, const QString
void PortInput::correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
QString newText;
newText.reserve(now.size());
int32 newCursor = nowCursor;
for (int32 i = 0, l = now.size(); i < l; ++i) {
auto newPos = nowCursor;
for (auto i = 0, l = now.size(); i < l; ++i) {
if (now.at(i).isDigit()) {
newText.append(now.at(i));
} else if (i < nowCursor) {
--newCursor;
--newPos;
}
}
if (!newText.toInt()) {
newText = QString();
newCursor = 0;
newPos = 0;
} else if (newText.toInt() > 65535) {
newText = was;
newCursor = wasCursor;
}
if (newText != now) {
now = newText;
setText(newText);
}
if (newCursor != nowCursor) {
nowCursor = newCursor;
setCursorPosition(newCursor);
newPos = wasCursor;
}
setCorrectedText(now, nowCursor, newText, newPos);
}
UsernameInput::UsernameInput(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val, bool isLink) : MaskedInputField(parent, st, ph, val),
@ -3916,13 +3919,13 @@ void UsernameInput::paintPlaceholder(Painter &p) {
}
void UsernameInput::correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
QString newText;
int32 newCursor = nowCursor, from, len = now.size();
for (from = 0; from < len; ++from) {
auto newPos = nowCursor;
auto from = 0, len = now.size();
for (; from < len; ++from) {
if (!now.at(from).isSpace()) {
break;
}
if (newCursor > 0) --newCursor;
if (newPos > 0) --newPos;
}
len -= from;
if (len > MaxUsernameLength) len = MaxUsernameLength + (now.at(from) == '@' ? 1 : 0);
@ -3933,18 +3936,7 @@ void UsernameInput::correctValue(const QString &was, int32 wasCursor, QString &n
}
--len;
}
newText = now.mid(from, len);
if (newCursor > len) {
newCursor = len;
}
if (newText != now) {
now = newText;
setText(newText);
}
if (newCursor != nowCursor) {
nowCursor = newCursor;
setCursorPosition(newCursor);
}
setCorrectedText(now, nowCursor, now.mid(from, len), newPos);
}
PhoneInput::PhoneInput(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : MaskedInputField(parent, st, ph, val)
@ -3996,7 +3988,7 @@ void PhoneInput::paintPlaceholder(Painter &p) {
}
void PhoneInput::correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
QString digits(now);
auto digits = now;
digits.replace(QRegularExpression(qsl("[^\\d]")), QString());
_pattern = phoneNumberParse(digits);
@ -4075,16 +4067,10 @@ void PhoneInput::correctValue(const QString &was, int32 wasCursor, QString &now,
while (newlen > 0 && newText.at(newlen - 1).isSpace()) {
--newlen;
}
if (newlen < newText.size()) newText = newText.mid(0, newlen);
if (newPos < 0) {
newPos = newText.length();
}
if (newText != now) {
now = newText;
setText(newText);
updatePlaceholder();
setCursorPosition(newPos);
if (newlen < newText.size()) {
newText = newText.mid(0, newlen);
}
setCorrectedText(now, nowCursor, newText, newPos);
}
} // namespace Ui

View File

@ -114,7 +114,7 @@ public:
};
void setTagMimeProcessor(std_::unique_ptr<TagMimeProcessor> &&processor);
public slots:
public slots:
void onTouchTimer();
void onDocumentContentsChange(int position, int charsRemoved, int charsAdded);
@ -267,7 +267,7 @@ public:
return _oldtext;
}
public slots:
public slots:
void onTextChange(const QString &text);
void onTextEdited();
@ -330,52 +330,6 @@ private:
QPoint _touchStart;
};
class CountryCodeInput : public FlatInput {
Q_OBJECT
public:
CountryCodeInput(QWidget *parent, const style::FlatInput &st);
public slots:
void startErasing(QKeyEvent *e);
void codeSelected(const QString &code);
signals:
void codeChanged(const QString &code);
void addedToNumber(const QString &added);
protected:
void correctValue(const QString &was, QString &now) override;
private:
bool _nosignal;
};
class PhonePartInput : public FlatInput {
Q_OBJECT
public:
PhonePartInput(QWidget *parent, const style::FlatInput &st);
public slots:
void addedToNumber(const QString &added);
void onChooseCode(const QString &code);
signals:
void voidBackspace(QKeyEvent *e);
protected:
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void correctValue(const QString &was, QString &now) override;
private:
QVector<int> _pattern;
};
enum CtrlEnterSubmit {
CtrlEnterSubmitEnter,
CtrlEnterSubmitCtrlEnter,
@ -439,7 +393,7 @@ public:
_inner.clearFocus();
}
public slots:
public slots:
void onTouchTimer();
void onDocumentContentsChange(int position, int charsRemoved, int charsAdded);
@ -610,7 +564,7 @@ public:
_inner.setTextCursor(c);
}
public slots:
public slots:
void onTouchTimer();
void onDocumentContentsChange(int position, int charsRemoved, int charsAdded);
@ -715,20 +669,11 @@ private:
class MaskedInputField : public QLineEdit {
Q_OBJECT
T_WIDGET
T_WIDGET
public:
MaskedInputField(QWidget *parent, const style::InputField &st, const QString &placeholder = QString(), const QString &val = QString());
bool event(QEvent *e) override;
void touchEvent(QTouchEvent *e);
void paintEvent(QPaintEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
void focusOutEvent(QFocusEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
void showError();
bool setPlaceholder(const QString &ph);
@ -757,7 +702,7 @@ public:
updatePlaceholder();
}
public slots:
public slots:
void onTextChange(const QString &text);
void onCursorPositionChanged(int oldPosition, int position);
@ -773,6 +718,15 @@ signals:
void blurred();
protected:
bool event(QEvent *e) override;
void touchEvent(QTouchEvent *e);
void paintEvent(QPaintEvent *e) override;
void focusInEvent(QFocusEvent *e) override;
void focusOutEvent(QFocusEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
void enterEventHook(QEvent *e) {
return QLineEdit::enterEvent(e);
}
@ -780,7 +734,10 @@ protected:
return QLineEdit::leaveEvent(e);
}
virtual void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor);
virtual void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) {
}
void setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos);
virtual void paintPlaceholder(Painter &p);
style::font phFont() {
@ -828,6 +785,52 @@ private:
QPoint _touchStart;
};
class CountryCodeInput : public MaskedInputField {
Q_OBJECT
public:
CountryCodeInput(QWidget *parent, const style::InputField &st);
public slots:
void startErasing(QKeyEvent *e);
void codeSelected(const QString &code);
signals:
void codeChanged(const QString &code);
void addedToNumber(const QString &added);
protected:
void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) override;
private:
bool _nosignal;
};
class PhonePartInput : public MaskedInputField {
Q_OBJECT
public:
PhonePartInput(QWidget *parent, const style::InputField &st);
public slots:
void addedToNumber(const QString &added);
void onChooseCode(const QString &code);
signals:
void voidBackspace(QKeyEvent *e);
protected:
void keyPressEvent(QKeyEvent *e) override;
void correctValue(const QString &was, int32 wasCursor, QString &now, int32 &nowCursor) override;
void paintPlaceholder(Painter &p) override;
private:
QVector<int> _pattern;
};
class PasswordInput : public MaskedInputField {
public:
PasswordInput(QWidget *parent, const style::InputField &st, const QString &ph = QString(), const QString &val = QString());

View File

@ -43,6 +43,57 @@ TextParseOptions _labelMarkedOptions = {
} // namespace
CrossFadeAnimation::CrossFadeAnimation(const style::color &bg) : _bg(bg) {
}
void CrossFadeAnimation::addLine(Part was, Part now) {
_lines.push_back(Line(std_::move(was), std_::move(now)));
}
void CrossFadeAnimation::paintFrame(Painter &p, float64 positionReady, float64 alphaWas, float64 alphaNow) {
if (_lines.isEmpty()) return;
for_const (auto &line, _lines) {
paintLine(p, line, positionReady, alphaWas, alphaNow);
}
}
void CrossFadeAnimation::paintLine(Painter &p, const Line &line, float64 positionReady, float64 alphaWas, float64 alphaNow) {
auto &snapshotWas = line.was.snapshot;
auto &snapshotNow = line.now.snapshot;
t_assert(!snapshotWas.isNull() || !snapshotNow.isNull());
auto positionWas = line.was.position;
auto positionNow = line.now.position;
auto left = anim::interpolate(positionWas.x(), positionNow.x(), positionReady);
auto topDelta = (snapshotNow.height() / cIntRetinaFactor()) - (snapshotWas.height() / cIntRetinaFactor());
auto widthDelta = (snapshotNow.width() / cIntRetinaFactor()) - (snapshotWas.width() / cIntRetinaFactor());
auto topWas = anim::interpolate(positionWas.y(), positionNow.y() + topDelta, positionReady);
auto topNow = topWas - topDelta;
p.setOpacity(alphaWas);
if (!snapshotWas.isNull()) {
p.drawPixmap(left, topWas, snapshotWas);
if (topDelta > 0) {
p.fillRect(left, topWas - topDelta, snapshotWas.width() / cIntRetinaFactor(), topDelta, _bg);
}
}
if (widthDelta > 0) {
p.fillRect(left + (snapshotWas.width() / cIntRetinaFactor()), topNow, widthDelta, snapshotNow.height() / cIntRetinaFactor(), _bg);
}
p.setOpacity(alphaNow);
if (!snapshotNow.isNull()) {
p.drawPixmap(left, topNow, snapshotNow);
if (topDelta < 0) {
p.fillRect(left, topNow + topDelta, snapshotNow.width() / cIntRetinaFactor(), -topDelta, _bg);
}
}
if (widthDelta < 0) {
p.fillRect(left + (snapshotNow.width() / cIntRetinaFactor()), topWas, -widthDelta, snapshotWas.height() / cIntRetinaFactor(), _bg);
}
}
LabelSimple::LabelSimple(QWidget *parent, const style::LabelSimple &st, const QString &value) : TWidget(parent)
, _st(st) {
setText(value);
@ -548,6 +599,72 @@ void FlatLabel::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool a
update();
}
std_::unique_ptr<CrossFadeAnimation> FlatLabel::CrossFade(FlatLabel *from, FlatLabel *to, const style::color &bg, QPoint fromPosition, QPoint toPosition) {
auto result = std_::make_unique<CrossFadeAnimation>(bg);
struct Data {
QImage full;
QVector<int> lineWidths;
int lineHeight = 0;
int lineAddTop = 0;
};
auto prepareData = [&bg](FlatLabel *label) {
auto result = Data();
result.full = QImage(label->size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.full.setDevicePixelRatio(cRetinaFactor());
result.full.fill(bg->c);
Painter(&result.full).drawImage(0, 0, myGrabImage(label));
auto textWidth = label->width() - label->_st.margin.left() - label->_st.margin.right();
label->_text.countLineWidths(textWidth, &result.lineWidths);
result.lineHeight = label->_st.font->height;
auto addedHeight = (label->_tst.lineHeight - result.lineHeight);
if (addedHeight > 0) {
result.lineAddTop = addedHeight / 2;
result.lineHeight += addedHeight;
}
return std_::move(result);
};
auto was = prepareData(from);
auto now = prepareData(to);
auto maxLines = qMax(was.lineWidths.size(), now.lineWidths.size());
auto fillDataTill = [maxLines](Data &data) {
for (auto i = data.lineWidths.size(); i != maxLines; ++i) {
data.lineWidths.push_back(-1);
}
};
fillDataTill(was);
fillDataTill(now);
auto preparePart = [](FlatLabel *label, QPoint position, Data &data, int index, Data &other) {
auto result = CrossFadeAnimation::Part();
auto lineWidth = data.lineWidths[index];
if (lineWidth < 0) {
lineWidth = other.lineWidths[index];
}
auto fullWidth = data.full.width() / cIntRetinaFactor();
auto top = index * data.lineHeight + data.lineAddTop;
auto left = 0;
if (label->_st.align & Qt::AlignHCenter) {
left += (fullWidth - lineWidth) / 2;
} else if (label->_st.align & Qt::AlignRight) {
left += (fullWidth - lineWidth);
}
auto snapshotRect = data.full.rect().intersected(QRect(left * cIntRetinaFactor(), top * cIntRetinaFactor(), lineWidth * cIntRetinaFactor(), label->_st.font->height * cIntRetinaFactor()));
if (!snapshotRect.isEmpty()) {
result.snapshot = App::pixmapFromImageInPlace(data.full.copy(snapshotRect));
result.snapshot.setDevicePixelRatio(cRetinaFactor());
}
auto positionBase = position + label->pos();
result.position = positionBase + QPoint(label->_st.margin.left() + left, label->_st.margin.top() + top);
return std_::move(result);
};
for (int i = 0; i != maxLines; ++i) {
result->addLine(preparePart(from, fromPosition, was, i, now), preparePart(to, toPosition, now, i, was));
}
return std_::move(result);
}
Text::StateResult FlatLabel::dragActionUpdate() {
auto m = mapFromGlobal(_lastMousePos);
auto state = getTextState(m);

View File

@ -26,6 +26,37 @@ namespace Ui {
class PopupMenu;
class CrossFadeAnimation {
public:
CrossFadeAnimation(const style::color &bg);
struct Part {
QPixmap snapshot;
QPoint position;
};
void addLine(Part was, Part now);
void paintFrame(Painter &p, float64 dt) {
auto progress = anim::linear(1., dt);
paintFrame(p, progress, 1. - progress, progress);
}
void paintFrame(Painter &p, float64 positionReady, float64 alphaWas, float64 alphaNow);
private:
struct Line {
Line(Part was, Part now) : was(std_::move(was)), now(std_::move(now)) {
}
Part was;
Part now;
};
void paintLine(Painter &p, const Line &line, float64 positionReady, float64 alphaWas, float64 alphaNow);
const style::color &_bg;
QList<Line> _lines;
};
class LabelSimple : public TWidget {
public:
LabelSimple(QWidget *parent, const style::LabelSimple &st = st::defaultLabelSimple, const QString &value = QString());
@ -81,6 +112,8 @@ public:
void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override;
static std_::unique_ptr<CrossFadeAnimation> CrossFade(FlatLabel *from, FlatLabel *to, const style::color &bg, QPoint fromPosition = QPoint(), QPoint toPosition = QPoint());
protected:
void paintEvent(QPaintEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;

View File

@ -160,7 +160,7 @@ private:
class SplittedWidgetOther;
class ScrollArea : public QScrollArea {
Q_OBJECT
T_WIDGET
T_WIDGET
public:
ScrollArea(QWidget *parent, const style::FlatScroll &st = st::defaultFlatScroll, bool handleTouch = true);

View File

@ -21,7 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "ui/twidget.h"
#include "window/slide_animation.h"
#include "window/window_slide_animation.h"
namespace Window {

View File

@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "window/slide_animation.h"
#include "window/window_slide_animation.h"
#include "styles/style_window.h"

View File

@ -466,6 +466,8 @@
'<(src_loc)/ui/effects/ripple_animation.h',
'<(src_loc)/ui/effects/round_checkbox.cpp',
'<(src_loc)/ui/effects/round_checkbox.h',
'<(src_loc)/ui/effects/slide_animation.cpp',
'<(src_loc)/ui/effects/slide_animation.h',
'<(src_loc)/ui/effects/widget_fade_wrap.cpp',
'<(src_loc)/ui/effects/widget_fade_wrap.h',
'<(src_loc)/ui/effects/widget_slide_wrap.cpp',
@ -546,8 +548,8 @@
'<(src_loc)/window/player_wrap_widget.h',
'<(src_loc)/window/section_widget.cpp',
'<(src_loc)/window/section_widget.h',
'<(src_loc)/window/slide_animation.cpp',
'<(src_loc)/window/slide_animation.h',
'<(src_loc)/window/window_slide_animation.cpp',
'<(src_loc)/window/window_slide_animation.h',
'<(src_loc)/window/top_bar_widget.cpp',
'<(src_loc)/window/top_bar_widget.h',
'<(src_loc)/window/window_main_menu.cpp',