| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "ui/widgets/fields/special_fields.h"
- #include "lang/lang_keys.h"
- #include "countries/countries_instance.h" // Countries::ValidPhoneCode
- #include "styles/style_widgets.h"
- #include <QtCore/QRegularExpression>
- namespace Ui {
- namespace {
- constexpr auto kMaxUsernameLength = 32;
- // Rest of the phone number, without country code (seen 12 at least),
- // need more for service numbers.
- constexpr auto kMaxPhoneTailLength = 32;
- // Max length of country phone code.
- constexpr auto kMaxPhoneCodeLength = 4;
- } // namespace
- CountryCodeInput::CountryCodeInput(
- QWidget *parent,
- const style::InputField &st)
- : MaskedInputField(parent, st) {
- }
- 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 = int(newText.size());
- setText(newText);
- _nosignal = true;
- correctValue(wasText, wasCursor, newText, newCursor);
- _nosignal = false;
- changed();
- }
- void CountryCodeInput::correctValue(
- const QString &was,
- int wasCursor,
- QString &now,
- int &nowCursor) {
- QString newText, addToNumber;
- int oldPos(nowCursor);
- int newPos(-1);
- int oldLen(now.length());
- int start = 0;
- int 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 = Countries::Instance().validPhoneCode(newText.mid(1));
- addToNumber = newText.mid(1 + validCode.length()) + addToNumber;
- newText = '+' + validCode;
- }
- setCorrectedText(now, nowCursor, newText, newPos);
- if (!_nosignal && was != newText) {
- _codeChanged.fire(newText.mid(1));
- }
- if (!addToNumber.isEmpty()) {
- _addedToNumber.fire_copy(addToNumber);
- }
- }
- PhonePartInput::PhonePartInput(
- QWidget *parent,
- const style::InputField &st,
- PhonePartInput::GroupsCallback groupsCallback)
- : MaskedInputField(parent, st/*, tr::lng_phone_ph(tr::now)*/)
- , _groupsCallback(std::move(groupsCallback)) {
- }
- void PhonePartInput::paintAdditionalPlaceholder(QPainter &p) {
- if (!_pattern.isEmpty()) {
- auto t = getDisplayedText();
- auto ph = _additionalPlaceholder.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);
- placeholderAdditionalPrepare(p);
- p.drawText(phRect, ph, style::al_topleft);
- }
- }
- }
- }
- void PhonePartInput::keyPressEvent(QKeyEvent *e) {
- if (e->key() == Qt::Key_Backspace && cursorPosition() == 0) {
- _frontBackspaceEvent.fire_copy(e);
- } else {
- MaskedInputField::keyPressEvent(e);
- }
- }
- void PhonePartInput::correctValue(
- const QString &was,
- int wasCursor,
- QString &now,
- int &nowCursor) {
- if (!now.isEmpty() && (_lastDigits != now)) {
- _lastDigits = now;
- _lastDigits.replace(TextUtilities::RegExpDigitsExclude(), QString());
- updatePattern(_groupsCallback(_code + _lastDigits));
- }
- 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 > kMaxPhoneTailLength) {
- digitCount = kMaxPhoneTailLength;
- }
- 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 {
- ++curPart;
- inPart = curPart < _pattern.size();
- // Don't add an extra space to the end.
- if (inPart) {
- newText += ' ';
- }
- 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 = int(newText.size());
- setText(newText);
- setCursorPosition(added.length());
- correctValue(wasText, wasCursor, newText, newCursor);
- startPlaceholderAnimation();
- }
- void PhonePartInput::chooseCode(const QString &code) {
- _code = code;
- updatePattern(_groupsCallback(_code));
- auto wasText = getLastText();
- auto wasCursor = cursorPosition();
- auto newText = getLastText();
- auto newCursor = int(newText.size());
- correctValue(wasText, wasCursor, newText, newCursor);
- startPlaceholderAnimation();
- update();
- }
- void PhonePartInput::updatePattern(QVector<int> &&pattern) {
- if (_pattern == pattern) {
- return;
- }
- _pattern = std::move(pattern);
- if (!_pattern.isEmpty() && _pattern.at(0) == _code.size()) {
- _pattern.pop_front();
- } else {
- _pattern.clear();
- }
- _additionalPlaceholder = QString();
- if (!_pattern.isEmpty()) {
- _additionalPlaceholder.reserve(20);
- for (const auto &part : _pattern) {
- _additionalPlaceholder.append(' ');
- _additionalPlaceholder.append(QString(part, QChar(0x2212)));
- }
- }
- setPlaceholderHidden(!_additionalPlaceholder.isEmpty());
- }
- UsernameInput::UsernameInput(
- QWidget *parent,
- const style::InputField &st,
- rpl::producer<QString> placeholder,
- const QString &val,
- const QString &linkPlaceholder)
- : MaskedInputField(parent, st, std::move(placeholder), val) {
- setLinkPlaceholder(linkPlaceholder);
- }
- void UsernameInput::setLinkPlaceholder(const QString &placeholder) {
- _linkPlaceholder = placeholder;
- if (!_linkPlaceholder.isEmpty()) {
- setTextMargins(style::margins(_st.textMargins.left() + _st.style.font->width(_linkPlaceholder), _st.textMargins.top(), _st.textMargins.right(), _st.textMargins.bottom()));
- setPlaceholderHidden(true);
- }
- }
- void UsernameInput::paintAdditionalPlaceholder(QPainter &p) {
- if (!_linkPlaceholder.isEmpty()) {
- p.setFont(_st.style.font);
- p.setPen(_st.placeholderFg);
- p.drawText(QRect(_st.textMargins.left(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), _linkPlaceholder, style::al_topleft);
- }
- }
- void UsernameInput::correctValue(
- const QString &was,
- int wasCursor,
- QString &now,
- int &nowCursor) {
- auto newPos = nowCursor;
- auto from = 0, len = int(now.size());
- for (; from < len; ++from) {
- if (!now.at(from).isSpace()) {
- break;
- }
- if (newPos > 0) --newPos;
- }
- len -= from;
- if (len > kMaxUsernameLength) {
- len = kMaxUsernameLength + (now.at(from) == '@' ? 1 : 0);
- }
- for (int32 to = from + len; to > from;) {
- --to;
- if (!now.at(to).isSpace()) {
- break;
- }
- --len;
- }
- setCorrectedText(now, nowCursor, now.mid(from, len), newPos);
- }
- PhoneInput::PhoneInput(
- QWidget *parent,
- const style::InputField &st,
- rpl::producer<QString> placeholder,
- const QString &defaultValue,
- QString value,
- PhoneInput::GroupsCallback groupsCallback)
- : MaskedInputField(parent, st, std::move(placeholder), value)
- , _defaultValue(defaultValue)
- , _groupsCallback(std::move(groupsCallback)) {
- if (value.isEmpty()) {
- clearText();
- } else {
- auto pos = int(value.size());
- correctValue(QString(), 0, value, pos);
- }
- }
- void PhoneInput::focusInEvent(QFocusEvent *e) {
- MaskedInputField::focusInEvent(e);
- setSelection(cursorPosition(), cursorPosition());
- }
- void PhoneInput::clearText() {
- auto value = _defaultValue;
- setText(value);
- auto pos = int(value.size());
- correctValue(QString(), 0, value, pos);
- }
- void PhoneInput::paintAdditionalPlaceholder(QPainter &p) {
- if (!_pattern.isEmpty()) {
- auto t = getDisplayedText();
- auto ph = _additionalPlaceholder.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);
- placeholderAdditionalPrepare(p);
- p.drawText(phRect, ph, style::al_topleft);
- }
- }
- }
- }
- void PhoneInput::correctValue(
- const QString &was,
- int wasCursor,
- QString &now,
- int &nowCursor) {
- auto digits = now;
- digits.replace(TextUtilities::RegExpDigitsExclude(), QString());
- _pattern = _groupsCallback(digits);
- QString newPlaceholder;
- if (_pattern.isEmpty()) {
- newPlaceholder = QString();
- } else if (_pattern.size() == 1 && _pattern.at(0) == digits.size()) {
- newPlaceholder = QString(_pattern.at(0) + 2, ' ') + tr::lng_contact_phone(tr::now);
- } else {
- newPlaceholder.reserve(20);
- for (int i = 0, l = _pattern.size(); i < l; ++i) {
- if (i) {
- newPlaceholder.append(' ');
- } else {
- newPlaceholder.append('+');
- }
- newPlaceholder.append(i ? QString(_pattern.at(i), QChar(0x2212)) : digits.mid(0, _pattern.at(i)));
- }
- }
- if (_additionalPlaceholder != newPlaceholder) {
- _additionalPlaceholder = newPlaceholder;
- setPlaceholderHidden(!_additionalPlaceholder.isEmpty());
- update();
- }
- QString newText;
- int oldPos(nowCursor), newPos(-1), oldLen(now.length()), digitCount = qMin(digits.size(), kMaxPhoneCodeLength + kMaxPhoneTailLength);
- bool inPart = !_pattern.isEmpty(), plusFound = false;
- int curPart = 0, leftInPart = inPart ? _pattern.at(curPart) : 0;
- newText.reserve(oldLen + 1);
- newText.append('+');
- 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 {
- ++curPart;
- inPart = curPart < _pattern.size();
- // Don't add an extra space to the end.
- if (inPart) {
- newText += ' ';
- }
- 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;
- }
- } else if (ch == '+') {
- plusFound = true;
- }
- }
- if (!plusFound && newText == u"+"_q) {
- newText = QString();
- newPos = 0;
- }
- int32 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);
- }
- } // namespace Ui
|