mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Improved style of input field for login code.
This commit is contained in:
parent
9ef0e5cf83
commit
ac8117a6d8
6 changed files with 389 additions and 93 deletions
|
@ -115,6 +115,12 @@ introPassword: introCountry;
|
||||||
introPasswordTop: 74px;
|
introPasswordTop: 74px;
|
||||||
introPasswordHintTop: 151px;
|
introPasswordHintTop: 151px;
|
||||||
|
|
||||||
|
introCodeDigitFont: font(20px);
|
||||||
|
introCodeDigitHeight: 50px;
|
||||||
|
introCodeDigitBorderWidth: 4px;
|
||||||
|
introCodeDigitSkip: 10px;
|
||||||
|
introCodeDigitAnimatioDuration: 120;
|
||||||
|
|
||||||
introPasswordHint: FlatLabel(introDescription) {
|
introPasswordHint: FlatLabel(introDescription) {
|
||||||
textFg: windowFg;
|
textFg: windowFg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "intro/intro_code.h"
|
#include "intro/intro_code.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "intro/intro_code_input.h"
|
||||||
#include "intro/intro_signup.h"
|
#include "intro/intro_signup.h"
|
||||||
#include "intro/intro_password_check.h"
|
#include "intro/intro_password_check.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
|
@ -26,67 +27,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Intro {
|
namespace Intro {
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
CodeInput::CodeInput(
|
|
||||||
QWidget *parent,
|
|
||||||
const style::InputField &st,
|
|
||||||
rpl::producer<QString> placeholder)
|
|
||||||
: Ui::MaskedInputField(parent, st, std::move(placeholder)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void CodeInput::setDigitsCountMax(int digitsCount) {
|
|
||||||
_digitsCountMax = digitsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CodeInput::correctValue(const QString &was, int wasCursor, QString &now, int &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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accumulate_min(digitCount, _digitsCountMax);
|
|
||||||
auto strict = (digitCount == _digitsCountMax);
|
|
||||||
|
|
||||||
newText.reserve(oldLen);
|
|
||||||
for (int i = 0; i < oldLen; ++i) {
|
|
||||||
QChar ch(now[i]);
|
|
||||||
if (ch.isDigit()) {
|
|
||||||
if (!digitCount--) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
newText += ch;
|
|
||||||
if (strict && !digitCount) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ch == '-') {
|
|
||||||
newText += ch;
|
|
||||||
}
|
|
||||||
if (i == oldPos) {
|
|
||||||
newPos = newText.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newPos < 0 || newPos > newText.size()) {
|
|
||||||
newPos = newText.size();
|
|
||||||
}
|
|
||||||
if (newText != now) {
|
|
||||||
now = newText;
|
|
||||||
setText(now);
|
|
||||||
startPlaceholderAnimation();
|
|
||||||
}
|
|
||||||
if (newPos != nowCursor) {
|
|
||||||
nowCursor = newPos;
|
|
||||||
setCursorPosition(nowCursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeWidget::CodeWidget(
|
CodeWidget::CodeWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Main::Account*> account,
|
not_null<Main::Account*> account,
|
||||||
not_null<Data*> data)
|
not_null<Data*> data)
|
||||||
: Step(parent, account, data)
|
: Step(parent, account, data)
|
||||||
, _noTelegramCode(this, tr::lng_code_no_telegram(tr::now), st::introLink)
|
, _noTelegramCode(this, tr::lng_code_no_telegram(tr::now), st::introLink)
|
||||||
, _code(this, st::introCode, tr::lng_code_ph())
|
, _code(this)
|
||||||
, _callTimer([=] { sendCall(); })
|
, _callTimer([=] { sendCall(); })
|
||||||
, _callStatus(getData()->callStatus)
|
, _callStatus(getData()->callStatus)
|
||||||
, _callTimeout(getData()->callTimeout)
|
, _callTimeout(getData()->callTimeout)
|
||||||
|
@ -97,7 +44,6 @@ CodeWidget::CodeWidget(
|
||||||
refreshLang();
|
refreshLang();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
connect(_code, &CodeInput::changed, [=] { codeChanged(); });
|
|
||||||
_noTelegramCode->addClickHandler([=] { noTelegramCode(); });
|
_noTelegramCode->addClickHandler([=] { noTelegramCode(); });
|
||||||
|
|
||||||
_code->setDigitsCountMax(getData()->codeLength);
|
_code->setDigitsCountMax(getData()->codeLength);
|
||||||
|
@ -111,9 +57,15 @@ CodeWidget::CodeWidget(
|
||||||
}) | rpl::flatten_latest());
|
}) | rpl::flatten_latest());
|
||||||
|
|
||||||
account->setHandleLoginCode([=](const QString &code) {
|
account->setHandleLoginCode([=](const QString &code) {
|
||||||
_code->setText(code);
|
_code->setCode(code);
|
||||||
submitCode();
|
_code->requestCode();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_code->codeCollected(
|
||||||
|
) | rpl::start_with_next([=](const QString &code) {
|
||||||
|
hideError();
|
||||||
|
submitCode(code);
|
||||||
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeWidget::refreshLang() {
|
void CodeWidget::refreshLang() {
|
||||||
|
@ -210,7 +162,7 @@ void CodeWidget::showCodeError(rpl::producer<QString> text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeWidget::setInnerFocus() {
|
void CodeWidget::setInnerFocus() {
|
||||||
_code->setFocusFast();
|
_code->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeWidget::activate() {
|
void CodeWidget::activate() {
|
||||||
|
@ -233,7 +185,7 @@ void CodeWidget::finished() {
|
||||||
|
|
||||||
cancelled();
|
cancelled();
|
||||||
_sentCode.clear();
|
_sentCode.clear();
|
||||||
_code->setText(QString());
|
_code->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeWidget::cancelled() {
|
void CodeWidget::cancelled() {
|
||||||
|
@ -267,6 +219,7 @@ void CodeWidget::checkRequest() {
|
||||||
|
|
||||||
void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) {
|
void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) {
|
||||||
stopCheck();
|
stopCheck();
|
||||||
|
_code->setEnabled(true);
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
finish(result);
|
finish(result);
|
||||||
}
|
}
|
||||||
|
@ -274,12 +227,16 @@ void CodeWidget::codeSubmitDone(const MTPauth_Authorization &result) {
|
||||||
void CodeWidget::codeSubmitFail(const MTP::Error &error) {
|
void CodeWidget::codeSubmitFail(const MTP::Error &error) {
|
||||||
if (MTP::IsFloodError(error)) {
|
if (MTP::IsFloodError(error)) {
|
||||||
stopCheck();
|
stopCheck();
|
||||||
|
_code->setEnabled(true);
|
||||||
|
_code->setFocus();
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
showCodeError(tr::lng_flood_error());
|
showCodeError(tr::lng_flood_error());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
stopCheck();
|
stopCheck();
|
||||||
|
_code->setEnabled(true);
|
||||||
|
_code->setFocus();
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
auto &err = error.type();
|
auto &err = error.type();
|
||||||
if (err == u"PHONE_NUMBER_INVALID"_q
|
if (err == u"PHONE_NUMBER_INVALID"_q
|
||||||
|
@ -303,11 +260,6 @@ void CodeWidget::codeSubmitFail(const MTP::Error &error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeWidget::codeChanged() {
|
|
||||||
hideError();
|
|
||||||
submitCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CodeWidget::sendCall() {
|
void CodeWidget::sendCall() {
|
||||||
if (_callStatus == CallStatus::Waiting) {
|
if (_callStatus == CallStatus::Waiting) {
|
||||||
if (--_callTimeout <= 0) {
|
if (--_callTimeout <= 0) {
|
||||||
|
@ -370,19 +322,13 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
|
||||||
|
|
||||||
void CodeWidget::submit() {
|
void CodeWidget::submit() {
|
||||||
if (getData()->codeByFragmentUrl.isEmpty()) {
|
if (getData()->codeByFragmentUrl.isEmpty()) {
|
||||||
submitCode();
|
_code->requestCode();
|
||||||
} else {
|
} else {
|
||||||
File::OpenUrl(getData()->codeByFragmentUrl);
|
File::OpenUrl(getData()->codeByFragmentUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeWidget::submitCode() {
|
void CodeWidget::submitCode(const QString &text) {
|
||||||
const auto text = QString(
|
|
||||||
_code->getLastText()
|
|
||||||
).remove(
|
|
||||||
TextUtilities::RegExpDigitsExclude()
|
|
||||||
).mid(0, getData()->codeLength);
|
|
||||||
|
|
||||||
if (_sentRequest
|
if (_sentRequest
|
||||||
|| _sentCode == text
|
|| _sentCode == text
|
||||||
|| text.size() != getData()->codeLength) {
|
|| text.size() != getData()->codeLength) {
|
||||||
|
@ -394,6 +340,7 @@ void CodeWidget::submitCode() {
|
||||||
_checkRequestTimer.callEach(1000);
|
_checkRequestTimer.callEach(1000);
|
||||||
|
|
||||||
_sentCode = text;
|
_sentCode = text;
|
||||||
|
_code->setEnabled(false);
|
||||||
getData()->pwdState = Core::CloudPasswordState();
|
getData()->pwdState = Core::CloudPasswordState();
|
||||||
_sentRequest = api().request(MTPauth_SignIn(
|
_sentRequest = api().request(MTPauth_SignIn(
|
||||||
MTP_flags(MTPauth_SignIn::Flag::f_phone_code),
|
MTP_flags(MTPauth_SignIn::Flag::f_phone_code),
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace Ui {
|
||||||
class RoundButton;
|
class RoundButton;
|
||||||
class LinkButton;
|
class LinkButton;
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
|
class CodeInput;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Intro {
|
namespace Intro {
|
||||||
|
@ -23,23 +24,6 @@ namespace details {
|
||||||
|
|
||||||
enum class CallStatus;
|
enum class CallStatus;
|
||||||
|
|
||||||
class CodeInput final : public Ui::MaskedInputField {
|
|
||||||
public:
|
|
||||||
CodeInput(
|
|
||||||
QWidget *parent,
|
|
||||||
const style::InputField &st,
|
|
||||||
rpl::producer<QString> placeholder);
|
|
||||||
|
|
||||||
void setDigitsCountMax(int digitsCount);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void correctValue(const QString &was, int wasCursor, QString &now, int &nowCursor) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _digitsCountMax = 5;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class CodeWidget final : public Step {
|
class CodeWidget final : public Step {
|
||||||
public:
|
public:
|
||||||
CodeWidget(
|
CodeWidget(
|
||||||
|
@ -65,7 +49,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void noTelegramCode();
|
void noTelegramCode();
|
||||||
void codeChanged();
|
|
||||||
void sendCall();
|
void sendCall();
|
||||||
void checkRequest();
|
void checkRequest();
|
||||||
|
|
||||||
|
@ -85,14 +68,14 @@ private:
|
||||||
void noTelegramCodeDone(const MTPauth_SentCode &result);
|
void noTelegramCodeDone(const MTPauth_SentCode &result);
|
||||||
void noTelegramCodeFail(const MTP::Error &result);
|
void noTelegramCodeFail(const MTP::Error &result);
|
||||||
|
|
||||||
void submitCode();
|
void submitCode(const QString &text);
|
||||||
|
|
||||||
void stopCheck();
|
void stopCheck();
|
||||||
|
|
||||||
object_ptr<Ui::LinkButton> _noTelegramCode;
|
object_ptr<Ui::LinkButton> _noTelegramCode;
|
||||||
mtpRequestId _noTelegramCodeRequestId = 0;
|
mtpRequestId _noTelegramCodeRequestId = 0;
|
||||||
|
|
||||||
object_ptr<CodeInput> _code;
|
object_ptr<Ui::CodeInput> _code;
|
||||||
QString _sentCode;
|
QString _sentCode;
|
||||||
mtpRequestId _sentRequest = 0;
|
mtpRequestId _sentRequest = 0;
|
||||||
|
|
||||||
|
|
308
Telegram/SourceFiles/intro/intro_code_input.cpp
Normal file
308
Telegram/SourceFiles/intro/intro_code_input.cpp
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
// This file is part of Desktop App Toolkit,
|
||||||
|
// a set of libraries for developing nice desktop applications.
|
||||||
|
//
|
||||||
|
// For license and copyright information please follow this link:
|
||||||
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||||
|
//
|
||||||
|
#include "intro/intro_code_input.h"
|
||||||
|
|
||||||
|
#include "ui/abstract_button.h"
|
||||||
|
#include "ui/effects/shake_animation.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
|
#include "ui/text/text_entity.h"
|
||||||
|
#include "styles/style_intro.h"
|
||||||
|
#include "styles/style_layers.h" // boxRadius
|
||||||
|
|
||||||
|
#include <QtCore/QRegularExpression>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kDigitNone = int(-1);
|
||||||
|
|
||||||
|
[[nodiscard]] int Circular(int left, int right) {
|
||||||
|
return ((left % right) + right) % right;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shaker final {
|
||||||
|
public:
|
||||||
|
explicit Shaker(not_null<Ui::RpWidget*> widget);
|
||||||
|
|
||||||
|
void shake();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<Ui::RpWidget*> _widget;
|
||||||
|
Ui::Animations::Simple _animation;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Shaker::Shaker(not_null<Ui::RpWidget*> widget)
|
||||||
|
: _widget(widget) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shaker::shake() {
|
||||||
|
if (_animation.animating()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_animation.start(DefaultShakeCallback([=, x = _widget->x()](int shift) {
|
||||||
|
_widget->moveToLeft(x + shift, _widget->y());
|
||||||
|
}), 0., 1., st::shakeDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class CodeDigit final : public Ui::AbstractButton {
|
||||||
|
public:
|
||||||
|
explicit CodeDigit(not_null<Ui::RpWidget*> widget);
|
||||||
|
|
||||||
|
void setDigit(int digit);
|
||||||
|
[[nodiscard]] int digit() const;
|
||||||
|
|
||||||
|
void setBorderColor(const QBrush &brush);
|
||||||
|
void shake();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Shaker _shaker;
|
||||||
|
Ui::Animations::Simple _animation;
|
||||||
|
int _dataDigit = kDigitNone;
|
||||||
|
int _viewDigit = kDigitNone;
|
||||||
|
|
||||||
|
QPen _borderPen;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
CodeDigit::CodeDigit(not_null<Ui::RpWidget*> widget)
|
||||||
|
: Ui::AbstractButton(widget)
|
||||||
|
, _shaker(this) {
|
||||||
|
setBorderColor(st::windowBgRipple);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeDigit::setDigit(int digit) {
|
||||||
|
if ((_dataDigit == digit) && _animation.animating()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_dataDigit = digit;
|
||||||
|
if (_viewDigit != digit) {
|
||||||
|
constexpr auto kDuration = st::introCodeDigitAnimatioDuration;
|
||||||
|
_animation.stop();
|
||||||
|
if (digit == kDigitNone) {
|
||||||
|
_animation.start([=](float64 value) {
|
||||||
|
update();
|
||||||
|
if (!value) {
|
||||||
|
_viewDigit = digit;
|
||||||
|
}
|
||||||
|
}, 1., 0., kDuration);
|
||||||
|
} else {
|
||||||
|
_viewDigit = digit;
|
||||||
|
_animation.start([=] { update(); }, 0., 1., kDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CodeDigit::digit() const {
|
||||||
|
return _dataDigit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeDigit::setBorderColor(const QBrush &brush) {
|
||||||
|
_borderPen = QPen(brush, st::introCodeDigitBorderWidth);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeDigit::shake() {
|
||||||
|
_shaker.shake();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeDigit::paintEvent(QPaintEvent *e) {
|
||||||
|
auto p = QPainter(this);
|
||||||
|
|
||||||
|
auto clipPath = QPainterPath();
|
||||||
|
clipPath.addRoundedRect(rect(), st::boxRadius, st::boxRadius);
|
||||||
|
p.setClipPath(clipPath);
|
||||||
|
|
||||||
|
p.fillRect(rect(), st::windowBgOver);
|
||||||
|
p.strokePath(clipPath, _borderPen);
|
||||||
|
|
||||||
|
if (_viewDigit == kDigitNone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto hiding = (_dataDigit == kDigitNone);
|
||||||
|
const auto progress = _animation.value(1.);
|
||||||
|
|
||||||
|
if (hiding) {
|
||||||
|
p.setOpacity(progress * progress);
|
||||||
|
const auto center = rect().center();
|
||||||
|
p.setTransform(QTransform()
|
||||||
|
.translate(center.x(), center.y())
|
||||||
|
.scale(progress, progress)
|
||||||
|
.translate(-center.x(), -center.y()));
|
||||||
|
} else {
|
||||||
|
p.setOpacity(progress);
|
||||||
|
constexpr auto kSlideDistanceRatio = 0.2;
|
||||||
|
const auto distance = rect().height() * kSlideDistanceRatio;
|
||||||
|
p.translate(0, (distance * (1. - progress)));
|
||||||
|
}
|
||||||
|
p.setFont(st::introCodeDigitFont);
|
||||||
|
p.setPen(st::windowFg);
|
||||||
|
p.drawText(rect(), QString::number(_viewDigit), style::al_center);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeInput::CodeInput(QWidget *parent)
|
||||||
|
: Ui::RpWidget(parent) {
|
||||||
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::setDigitsCountMax(int digitsCount) {
|
||||||
|
_digitsCountMax = digitsCount;
|
||||||
|
|
||||||
|
_digits.clear();
|
||||||
|
_currentIndex = 0;
|
||||||
|
|
||||||
|
constexpr auto kWidthRatio = 0.8;
|
||||||
|
const auto digitWidth = st::introCodeDigitHeight * kWidthRatio;
|
||||||
|
const auto padding = Margins(st::introCodeDigitSkip);
|
||||||
|
resize(
|
||||||
|
padding.left()
|
||||||
|
+ digitWidth * digitsCount
|
||||||
|
+ st::introCodeDigitSkip * (digitsCount - 1)
|
||||||
|
+ padding.right(),
|
||||||
|
st::introCodeDigitHeight);
|
||||||
|
|
||||||
|
for (auto i = 0; i < digitsCount; i++) {
|
||||||
|
const auto widget = Ui::CreateChild<CodeDigit>(this);
|
||||||
|
widget->setPointerCursor(false);
|
||||||
|
widget->setClickedCallback([=] { unfocusAll(_currentIndex = i); });
|
||||||
|
widget->resize(digitWidth, st::introCodeDigitHeight);
|
||||||
|
widget->moveToLeft(
|
||||||
|
padding.left() + (digitWidth + st::introCodeDigitSkip) * i,
|
||||||
|
0);
|
||||||
|
_digits.emplace_back(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::setCode(QString code) {
|
||||||
|
using namespace TextUtilities;
|
||||||
|
code = code.remove(RegExpDigitsExclude()).mid(0, _digitsCountMax);
|
||||||
|
for (int i = 0; i < _digits.size(); i++) {
|
||||||
|
if (i >= code.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_digits[i]->setDigit(code.at(i).digitValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::requestCode() {
|
||||||
|
const auto result = collectDigits();
|
||||||
|
if (result.size() == _digitsCountMax) {
|
||||||
|
_codeCollected.fire_copy(result);
|
||||||
|
} else {
|
||||||
|
findEmptyAndPerform([&](int i) { _digits[i]->shake(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> CodeInput::codeCollected() const {
|
||||||
|
return _codeCollected.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::clear() {
|
||||||
|
for (const auto &digit : _digits) {
|
||||||
|
digit->setDigit(kDigitNone);
|
||||||
|
}
|
||||||
|
unfocusAll(_currentIndex = 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::showError() {
|
||||||
|
clear();
|
||||||
|
for (const auto &digit : _digits) {
|
||||||
|
digit->shake();
|
||||||
|
digit->setBorderColor(st::activeLineFgError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::focusInEvent(QFocusEvent *e) {
|
||||||
|
unfocusAll(_currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::focusOutEvent(QFocusEvent *e) {
|
||||||
|
unfocusAll(kDigitNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::paintEvent(QPaintEvent *e) {
|
||||||
|
auto p = QPainter(this);
|
||||||
|
p.fillRect(rect(), st::windowBg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::keyPressEvent(QKeyEvent *e) {
|
||||||
|
const auto key = e->key();
|
||||||
|
if (key == Qt::Key_Down || key == Qt::Key_Right || key == Qt::Key_Space) {
|
||||||
|
_currentIndex = Circular(_currentIndex + 1, _digits.size());
|
||||||
|
unfocusAll(_currentIndex);
|
||||||
|
} else if (key == Qt::Key_Up || key == Qt::Key_Left) {
|
||||||
|
_currentIndex = Circular(_currentIndex - 1, _digits.size());
|
||||||
|
unfocusAll(_currentIndex);
|
||||||
|
} else if (key >= Qt::Key_0 && key <= Qt::Key_9) {
|
||||||
|
const auto index = int(key - Qt::Key_0);
|
||||||
|
_digits[_currentIndex]->setDigit(index);
|
||||||
|
_currentIndex = Circular(_currentIndex + 1, _digits.size());
|
||||||
|
if (!_currentIndex) {
|
||||||
|
const auto result = collectDigits();
|
||||||
|
if (result.size() == _digitsCountMax) {
|
||||||
|
_codeCollected.fire_copy(result);
|
||||||
|
_currentIndex = _digits.size() - 1;
|
||||||
|
} else {
|
||||||
|
findEmptyAndPerform([&](int i) { _currentIndex = i; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unfocusAll(_currentIndex);
|
||||||
|
} else if (key == Qt::Key_Delete) {
|
||||||
|
_digits[_currentIndex]->setDigit(kDigitNone);
|
||||||
|
} else if (key == Qt::Key_Backspace) {
|
||||||
|
const auto wasDigit = _digits[_currentIndex]->digit();
|
||||||
|
_digits[_currentIndex]->setDigit(kDigitNone);
|
||||||
|
_currentIndex = std::clamp(_currentIndex - 1, 0, int(_digits.size()));
|
||||||
|
if (wasDigit == kDigitNone) {
|
||||||
|
_digits[_currentIndex]->setDigit(kDigitNone);
|
||||||
|
}
|
||||||
|
unfocusAll(_currentIndex);
|
||||||
|
} else if (key == Qt::Key_Enter || key == Qt::Key_Return) {
|
||||||
|
requestCode();
|
||||||
|
} else if (key >= Qt::Key_A && key <= Qt::Key_Z) {
|
||||||
|
_digits[_currentIndex]->shake();
|
||||||
|
} else if (key == Qt::Key_Home || key == Qt::Key_PageUp) {
|
||||||
|
unfocusAll(_currentIndex = 0);
|
||||||
|
} else if (key == Qt::Key_End || key == Qt::Key_PageDown) {
|
||||||
|
unfocusAll(_currentIndex = (_digits.size() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CodeInput::collectDigits() const {
|
||||||
|
auto result = QString();
|
||||||
|
for (const auto &digit : _digits) {
|
||||||
|
if (digit->digit() != kDigitNone) {
|
||||||
|
result += QString::number(digit->digit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::unfocusAll(int except) {
|
||||||
|
for (auto i = 0; i < _digits.size(); i++) {
|
||||||
|
const auto focused = (i == except);
|
||||||
|
_digits[i]->setBorderColor(focused
|
||||||
|
? st::windowActiveTextFg
|
||||||
|
: st::windowBgRipple);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeInput::findEmptyAndPerform(const Fn<void(int)> &callback) {
|
||||||
|
for (auto i = 0; i < _digits.size(); i++) {
|
||||||
|
if (_digits[i]->digit() == kDigitNone) {
|
||||||
|
callback(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ui
|
49
Telegram/SourceFiles/intro/intro_code_input.h
Normal file
49
Telegram/SourceFiles/intro/intro_code_input.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// This file is part of Desktop App Toolkit,
|
||||||
|
// a set of libraries for developing nice desktop applications.
|
||||||
|
//
|
||||||
|
// For license and copyright information please follow this link:
|
||||||
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
class CodeDigit;
|
||||||
|
|
||||||
|
class CodeInput final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
CodeInput(QWidget *parent);
|
||||||
|
|
||||||
|
void setDigitsCountMax(int digitsCount);
|
||||||
|
|
||||||
|
void setCode(QString code);
|
||||||
|
|
||||||
|
void requestCode();
|
||||||
|
[[nodiscard]] rpl::producer<QString> codeCollected() const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void showError();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void focusInEvent(QFocusEvent *e) override;
|
||||||
|
void focusOutEvent(QFocusEvent *e) override;
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] QString collectDigits() const;
|
||||||
|
|
||||||
|
void unfocusAll(int except);
|
||||||
|
void findEmptyAndPerform(const Fn<void(int)> &callback);
|
||||||
|
|
||||||
|
int _digitsCountMax = 0;
|
||||||
|
std::vector<not_null<CodeDigit*>> _digits;
|
||||||
|
int _currentIndex = 0;
|
||||||
|
|
||||||
|
rpl::event_stream<QString> _codeCollected;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -117,6 +117,9 @@ PRIVATE
|
||||||
info/boosts/giveaway/select_countries_box.cpp
|
info/boosts/giveaway/select_countries_box.cpp
|
||||||
info/boosts/giveaway/select_countries_box.h
|
info/boosts/giveaway/select_countries_box.h
|
||||||
|
|
||||||
|
intro/intro_code_input.cpp
|
||||||
|
intro/intro_code_input.h
|
||||||
|
|
||||||
layout/abstract_layout_item.cpp
|
layout/abstract_layout_item.cpp
|
||||||
layout/abstract_layout_item.h
|
layout/abstract_layout_item.h
|
||||||
layout/layout_mosaic.cpp
|
layout/layout_mosaic.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue