init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
This commit is contained in:
459
Telegram/SourceFiles/ui/widgets/fields/special_fields.cpp
Normal file
459
Telegram/SourceFiles/ui/widgets/fields/special_fields.cpp
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
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::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Space) {
|
||||
_spacePressed.fire({});
|
||||
} else {
|
||||
MaskedInputField::keyPressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
144
Telegram/SourceFiles/ui/widgets/fields/special_fields.h
Normal file
144
Telegram/SourceFiles/ui/widgets/fields/special_fields.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/fields/masked_input_field.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class CountryCodeInput : public MaskedInputField {
|
||||
public:
|
||||
CountryCodeInput(QWidget *parent, const style::InputField &st);
|
||||
|
||||
void startErasing(QKeyEvent *e);
|
||||
|
||||
[[nodiscard]] rpl::producer<QString> addedToNumber() const {
|
||||
return _addedToNumber.events();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<QString> codeChanged() const {
|
||||
return _codeChanged.events();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<> spacePressed() const {
|
||||
return _spacePressed.events();
|
||||
}
|
||||
|
||||
void codeSelected(const QString &code);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
|
||||
private:
|
||||
bool _nosignal = false;
|
||||
rpl::event_stream<QString> _addedToNumber;
|
||||
rpl::event_stream<QString> _codeChanged;
|
||||
rpl::event_stream<> _spacePressed;
|
||||
|
||||
};
|
||||
|
||||
class PhonePartInput : public MaskedInputField {
|
||||
public:
|
||||
using GroupsCallback = Fn<QVector<int>(const QString &)>;
|
||||
|
||||
PhonePartInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
GroupsCallback groupsCallback);
|
||||
|
||||
[[nodiscard]] auto frontBackspaceEvent() const
|
||||
-> rpl::producer<not_null<QKeyEvent*>> {
|
||||
return _frontBackspaceEvent.events();
|
||||
}
|
||||
|
||||
void addedToNumber(const QString &added);
|
||||
void chooseCode(const QString &code);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(QPainter &p) override;
|
||||
|
||||
private:
|
||||
void updatePattern(QVector<int> &&pattern);
|
||||
|
||||
QString _code;
|
||||
QString _lastDigits;
|
||||
QVector<int> _pattern;
|
||||
QString _additionalPlaceholder;
|
||||
rpl::event_stream<not_null<QKeyEvent*>> _frontBackspaceEvent;
|
||||
GroupsCallback _groupsCallback;
|
||||
|
||||
};
|
||||
|
||||
class UsernameInput : public MaskedInputField {
|
||||
public:
|
||||
UsernameInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val,
|
||||
const QString &linkPlaceholder);
|
||||
|
||||
void setLinkPlaceholder(const QString &placeholder);
|
||||
|
||||
protected:
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(QPainter &p) override;
|
||||
|
||||
private:
|
||||
QString _linkPlaceholder;
|
||||
|
||||
};
|
||||
|
||||
class PhoneInput : public MaskedInputField {
|
||||
public:
|
||||
using GroupsCallback = Fn<QVector<int>(const QString &)>;
|
||||
|
||||
PhoneInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &defaultValue,
|
||||
QString value,
|
||||
GroupsCallback groupsCallback);
|
||||
|
||||
void clearText();
|
||||
|
||||
protected:
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
void paintAdditionalPlaceholder(QPainter &p) override;
|
||||
|
||||
private:
|
||||
QString _defaultValue;
|
||||
QVector<int> _pattern;
|
||||
QString _additionalPlaceholder;
|
||||
|
||||
GroupsCallback _groupsCallback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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/time_part_input_with_placeholder.h"
|
||||
|
||||
#include "lang/lang_numbers_animation.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void TimePartWithPlaceholder::setPhrase(
|
||||
const tr::phrase<lngtag_count> &phrase) {
|
||||
_phrase = phrase;
|
||||
}
|
||||
|
||||
void TimePartWithPlaceholder::paintAdditionalPlaceholder(QPainter &p) {
|
||||
maybeUpdatePlaceholder();
|
||||
|
||||
p.setClipRect(rect());
|
||||
const auto phRect = placeholderRect();
|
||||
|
||||
if (_lastPlaceholder.width < phRect.width()) {
|
||||
placeholderAdditionalPrepare(p);
|
||||
p.drawText(
|
||||
phRect.translated(-_lastPlaceholder.leftOffset, 0),
|
||||
_lastPlaceholder.text,
|
||||
style::al_left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TimePartWithPlaceholder::maybeUpdatePlaceholder() {
|
||||
const auto displayedText = getDisplayedText();
|
||||
if (displayedText == _lastPlaceholder.displayedText) {
|
||||
return;
|
||||
}
|
||||
const auto count = displayedText.toUInt();
|
||||
const auto textWithOffset = _phrase(
|
||||
tr::now,
|
||||
lt_count,
|
||||
count,
|
||||
Ui::StringWithNumbers::FromString);
|
||||
_lastPlaceholder = {
|
||||
.width = phFont()->width(textWithOffset.text),
|
||||
.text = textWithOffset.text,
|
||||
.leftOffset = phFont()->width(
|
||||
textWithOffset.text.mid(0, textWithOffset.offset)),
|
||||
.displayedText = displayedText,
|
||||
};
|
||||
if (displayedText.size() > 1 && displayedText.startsWith(_zero)) {
|
||||
_lastPlaceholder.text.insert(textWithOffset.offset, _zero);
|
||||
}
|
||||
|
||||
const auto leftMargins = (width() - _lastPlaceholder.width) / 2
|
||||
+ _lastPlaceholder.leftOffset;
|
||||
setTextMargins({ leftMargins, 0, 0, 0 });
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/fields/time_part_input.h"
|
||||
|
||||
#include "lang_auto.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class TimePartWithPlaceholder final : public TimePart {
|
||||
public:
|
||||
using Ui::TimePart::TimePart;
|
||||
|
||||
void setPhrase(const tr::phrase<lngtag_count> &phrase);
|
||||
|
||||
protected:
|
||||
void paintAdditionalPlaceholder(QPainter &p) override;
|
||||
|
||||
private:
|
||||
void maybeUpdatePlaceholder();
|
||||
|
||||
const QChar _zero = QChar('0');
|
||||
tr::phrase<lngtag_count> _phrase;
|
||||
|
||||
struct {
|
||||
int width = 0;
|
||||
QString text;
|
||||
int leftOffset = 0;
|
||||
QString displayedText;
|
||||
} _lastPlaceholder;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
Reference in New Issue
Block a user