mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 06:35:14 +00:00
Allow searching USA by 'United States'.
This commit is contained in:
@@ -11,74 +11,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "countries.h"
|
||||
#include "data/data_countries.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_intro.h"
|
||||
|
||||
namespace {
|
||||
|
||||
typedef QList<const CountryInfo *> CountriesFiltered;
|
||||
typedef QVector<int> CountriesIds;
|
||||
typedef QHash<QChar, CountriesIds> CountriesByLetter;
|
||||
typedef QVector<QString> CountryNames;
|
||||
typedef QVector<CountryNames> CountriesNames;
|
||||
|
||||
CountriesByCode _countriesByCode;
|
||||
CountriesByISO2 _countriesByISO2;
|
||||
CountriesFiltered countriesFiltered, countriesAll, *countriesNow = &countriesAll;
|
||||
CountriesByLetter countriesByLetter;
|
||||
CountriesNames countriesNames;
|
||||
|
||||
QString lastValidISO;
|
||||
int countriesCount = sizeof(countries) / sizeof(countries[0]);
|
||||
|
||||
void initCountries() {
|
||||
if (!_countriesByCode.isEmpty()) return;
|
||||
|
||||
_countriesByCode.reserve(countriesCount);
|
||||
_countriesByISO2.reserve(countriesCount);
|
||||
for (int i = 0; i < countriesCount; ++i) {
|
||||
const CountryInfo *info(countries + i);
|
||||
_countriesByCode.insert(info->code, info);
|
||||
CountriesByISO2::const_iterator already = _countriesByISO2.constFind(info->iso2);
|
||||
if (already != _countriesByISO2.cend()) {
|
||||
QString badISO = info->iso2;
|
||||
(void)badISO;
|
||||
}
|
||||
_countriesByISO2.insert(info->iso2, info);
|
||||
}
|
||||
countriesAll.reserve(countriesCount);
|
||||
countriesFiltered.reserve(countriesCount);
|
||||
countriesNames.resize(countriesCount);
|
||||
}
|
||||
QString LastValidISO;
|
||||
|
||||
} // namespace
|
||||
|
||||
const CountriesByCode &countriesByCode() {
|
||||
initCountries();
|
||||
return _countriesByCode;
|
||||
}
|
||||
|
||||
const CountriesByISO2 &countriesByISO2() {
|
||||
initCountries();
|
||||
return _countriesByISO2;
|
||||
}
|
||||
|
||||
QString findValidCode(QString fullCode) {
|
||||
while (fullCode.length()) {
|
||||
CountriesByCode::const_iterator i = _countriesByCode.constFind(fullCode);
|
||||
if (i != _countriesByCode.cend()) {
|
||||
return (*i)->code;
|
||||
}
|
||||
fullCode = fullCode.mid(0, fullCode.length() - 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
CountryInput::CountryInput(QWidget *parent, const style::InputField &st) : TWidget(parent)
|
||||
, _st(st)
|
||||
, _text(tr::lng_country_code(tr::now)) {
|
||||
initCountries();
|
||||
resize(_st.width, _st.heightMin);
|
||||
|
||||
auto availableWidth = width() - _st.textMargins.left() - _st.textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
|
||||
@@ -163,10 +108,11 @@ void CountryInput::onChooseCode(const QString &code) {
|
||||
Ui::hideLayer();
|
||||
_chosenIso = QString();
|
||||
if (code.length()) {
|
||||
CountriesByCode::const_iterator i = _countriesByCode.constFind(code);
|
||||
if (i != _countriesByCode.cend()) {
|
||||
const CountryInfo *info = *i;
|
||||
_chosenIso = lastValidISO = info->iso2;
|
||||
const auto &byCode = Data::CountriesByCode();
|
||||
const auto i = byCode.constFind(code);
|
||||
if (i != byCode.cend()) {
|
||||
const auto info = *i;
|
||||
_chosenIso = LastValidISO = info->iso2;
|
||||
setText(QString::fromUtf8(info->name));
|
||||
} else {
|
||||
setText(tr::lng_bad_country_code(tr::now));
|
||||
@@ -180,12 +126,13 @@ void CountryInput::onChooseCode(const QString &code) {
|
||||
bool CountryInput::onChooseCountry(const QString &iso) {
|
||||
Ui::hideLayer();
|
||||
|
||||
CountriesByISO2::const_iterator i = _countriesByISO2.constFind(iso);
|
||||
const CountryInfo *info = (i == _countriesByISO2.cend()) ? 0 : (*i);
|
||||
const auto &byISO2 = Data::CountriesByISO2();
|
||||
const auto i = byISO2.constFind(iso);
|
||||
const auto info = (i != byISO2.cend()) ? (*i) : nullptr;
|
||||
|
||||
_chosenIso = QString();
|
||||
if (info) {
|
||||
_chosenIso = lastValidISO = info->iso2;
|
||||
_chosenIso = LastValidISO = info->iso2;
|
||||
setText(QString::fromUtf8(info->name));
|
||||
emit codeChanged(info->code);
|
||||
update();
|
||||
@@ -205,28 +152,7 @@ CountrySelectBox::CountrySelectBox(QWidget*)
|
||||
CountrySelectBox::CountrySelectBox(QWidget*, const QString &iso, Type type)
|
||||
: _type(type)
|
||||
, _select(this, st::contactsMultiSelect, tr::lng_country_ph()) {
|
||||
lastValidISO = iso;
|
||||
}
|
||||
|
||||
QString CountrySelectBox::NameByISO(const QString &iso) {
|
||||
if (_countriesByISO2.isEmpty()) {
|
||||
initCountries();
|
||||
}
|
||||
const auto i = _countriesByISO2.constFind(iso);
|
||||
return (i == _countriesByISO2.cend())
|
||||
? QString()
|
||||
: QString::fromUtf8((*i)->name);
|
||||
}
|
||||
|
||||
QString CountrySelectBox::ISOByPhone(const QString &phone) {
|
||||
if (_countriesByCode.isEmpty()) {
|
||||
initCountries();
|
||||
}
|
||||
const auto code = findValidCode(phone);
|
||||
const auto i = _countriesByCode.find(code);
|
||||
return (i == _countriesByCode.cend())
|
||||
? QString()
|
||||
: QString::fromUtf8((*i)->iso2);
|
||||
LastValidISO = iso;
|
||||
}
|
||||
|
||||
void CountrySelectBox::prepare() {
|
||||
@@ -295,45 +221,47 @@ CountrySelectBox::Inner::Inner(QWidget *parent, Type type)
|
||||
, _rowHeight(st::countryRowHeight) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
if (countriesNames.isEmpty()) {
|
||||
initCountries();
|
||||
const auto &byISO2 = Data::CountriesByISO2();
|
||||
|
||||
_list.reserve(byISO2.size());
|
||||
_namesList.reserve(byISO2.size());
|
||||
|
||||
const auto l = byISO2.constFind(LastValidISO);
|
||||
const auto lastValid = (l != byISO2.cend()) ? (*l) : nullptr;
|
||||
if (lastValid) {
|
||||
_list.emplace_back(lastValid);
|
||||
}
|
||||
|
||||
CountriesByISO2::const_iterator l = _countriesByISO2.constFind(lastValidISO);
|
||||
bool seenLastValid = false;
|
||||
int already = countriesAll.size();
|
||||
|
||||
countriesByLetter.clear();
|
||||
const CountryInfo *lastValid = (l == _countriesByISO2.cend()) ? 0 : (*l);
|
||||
for (int i = 0; i < countriesCount; ++i) {
|
||||
const CountryInfo *ins = lastValid ? (i ? (countries + i - (seenLastValid ? 0 : 1)) : lastValid) : (countries + i);
|
||||
if (lastValid && i && ins == lastValid) {
|
||||
seenLastValid = true;
|
||||
++ins;
|
||||
for (const auto &entry : Data::Countries()) {
|
||||
if (&entry != lastValid) {
|
||||
_list.emplace_back(&entry);
|
||||
}
|
||||
if (already > i) {
|
||||
countriesAll[i] = ins;
|
||||
} else {
|
||||
countriesAll.push_back(ins);
|
||||
}
|
||||
|
||||
auto namesList = QString::fromUtf8(ins->name).toLower().split(QRegularExpression("[\\s\\-]"), QString::SkipEmptyParts);
|
||||
auto &names = countriesNames[i];
|
||||
int l = namesList.size();
|
||||
names.resize(0);
|
||||
names.reserve(l);
|
||||
for (int j = 0, l = namesList.size(); j < l; ++j) {
|
||||
auto name = namesList[j].trimmed();
|
||||
if (!name.length()) continue;
|
||||
|
||||
auto ch = name[0];
|
||||
auto &v = countriesByLetter[ch];
|
||||
if (v.isEmpty() || v.back() != i) {
|
||||
v.push_back(i);
|
||||
}
|
||||
auto index = 0;
|
||||
for (const auto info : _list) {
|
||||
auto full = QString::fromUtf8(info->name)
|
||||
+ ' '
|
||||
+ (info->alternativeName
|
||||
? QString::fromUtf8(info->alternativeName)
|
||||
: QString());
|
||||
const auto namesList = std::move(full).toLower().split(
|
||||
QRegularExpression("[\\s\\-]"),
|
||||
QString::SkipEmptyParts);
|
||||
auto &names = _namesList.emplace_back();
|
||||
names.reserve(namesList.size());
|
||||
for (const auto &name : namesList) {
|
||||
const auto part = name.trimmed();
|
||||
if (part.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
names.push_back(name);
|
||||
const auto ch = part[0];
|
||||
auto &byLetter = _byLetter[ch];
|
||||
if (byLetter.empty() || byLetter.back() != index) {
|
||||
byLetter.push_back(index);
|
||||
}
|
||||
names.push_back(part);
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
_filter = qsl("a");
|
||||
@@ -345,51 +273,52 @@ void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
p.setClipRect(r);
|
||||
|
||||
int l = countriesNow->size();
|
||||
if (l) {
|
||||
if (r.intersects(QRect(0, 0, width(), st::countriesSkip))) {
|
||||
p.fillRect(r.intersected(QRect(0, 0, width(), st::countriesSkip)), st::countryRowBg);
|
||||
}
|
||||
int32 from = floorclamp(r.y() - st::countriesSkip, _rowHeight, 0, l);
|
||||
int32 to = ceilclamp(r.y() + r.height() - st::countriesSkip, _rowHeight, 0, l);
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
auto selected = (i == (_pressed >= 0 ? _pressed : _selected));
|
||||
auto y = st::countriesSkip + i * _rowHeight;
|
||||
|
||||
p.fillRect(0, y, width(), _rowHeight, selected ? st::countryRowBgOver : st::countryRowBg);
|
||||
if (_ripples.size() > i && _ripples[i]) {
|
||||
_ripples[i]->paint(p, 0, y, width());
|
||||
if (_ripples[i]->empty()) {
|
||||
_ripples[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto code = QString("+") + (*countriesNow)[i]->code;
|
||||
auto codeWidth = st::countryRowCodeFont->width(code);
|
||||
|
||||
auto name = QString::fromUtf8((*countriesNow)[i]->name);
|
||||
auto nameWidth = st::countryRowNameFont->width(name);
|
||||
auto availWidth = width() - st::countryRowPadding.left() - st::countryRowPadding.right() - codeWidth - st::boxLayerScroll.width;
|
||||
if (nameWidth > availWidth) {
|
||||
name = st::countryRowNameFont->elided(name, availWidth);
|
||||
nameWidth = st::countryRowNameFont->width(name);
|
||||
}
|
||||
|
||||
p.setFont(st::countryRowNameFont);
|
||||
p.setPen(st::countryRowNameFg);
|
||||
p.drawTextLeft(st::countryRowPadding.left(), y + st::countryRowPadding.top(), width(), name);
|
||||
|
||||
if (_type == Type::Phones) {
|
||||
p.setFont(st::countryRowCodeFont);
|
||||
p.setPen(selected ? st::countryRowCodeFgOver : st::countryRowCodeFg);
|
||||
p.drawTextLeft(st::countryRowPadding.left() + nameWidth + st::countryRowPadding.right(), y + st::countryRowPadding.top(), width(), code);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto &list = current();
|
||||
if (list.empty()) {
|
||||
p.fillRect(r, st::boxBg);
|
||||
p.setFont(st::noContactsFont);
|
||||
p.setPen(st::noContactsColor);
|
||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), tr::lng_country_none(tr::now), style::al_center);
|
||||
return;
|
||||
}
|
||||
const auto l = list.size();
|
||||
if (r.intersects(QRect(0, 0, width(), st::countriesSkip))) {
|
||||
p.fillRect(r.intersected(QRect(0, 0, width(), st::countriesSkip)), st::countryRowBg);
|
||||
}
|
||||
int32 from = floorclamp(r.y() - st::countriesSkip, _rowHeight, 0, l);
|
||||
int32 to = ceilclamp(r.y() + r.height() - st::countriesSkip, _rowHeight, 0, l);
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
auto selected = (i == (_pressed >= 0 ? _pressed : _selected));
|
||||
auto y = st::countriesSkip + i * _rowHeight;
|
||||
|
||||
p.fillRect(0, y, width(), _rowHeight, selected ? st::countryRowBgOver : st::countryRowBg);
|
||||
if (_ripples.size() > i && _ripples[i]) {
|
||||
_ripples[i]->paint(p, 0, y, width());
|
||||
if (_ripples[i]->empty()) {
|
||||
_ripples[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto code = QString("+") + list[i]->code;
|
||||
auto codeWidth = st::countryRowCodeFont->width(code);
|
||||
|
||||
auto name = QString::fromUtf8(list[i]->name);
|
||||
auto nameWidth = st::countryRowNameFont->width(name);
|
||||
auto availWidth = width() - st::countryRowPadding.left() - st::countryRowPadding.right() - codeWidth - st::boxLayerScroll.width;
|
||||
if (nameWidth > availWidth) {
|
||||
name = st::countryRowNameFont->elided(name, availWidth);
|
||||
nameWidth = st::countryRowNameFont->width(name);
|
||||
}
|
||||
|
||||
p.setFont(st::countryRowNameFont);
|
||||
p.setPen(st::countryRowNameFg);
|
||||
p.drawTextLeft(st::countryRowPadding.left(), y + st::countryRowPadding.top(), width(), name);
|
||||
|
||||
if (_type == Type::Phones) {
|
||||
p.setFont(st::countryRowCodeFont);
|
||||
p.setPen(selected ? st::countryRowCodeFgOver : st::countryRowCodeFg);
|
||||
p.drawTextLeft(st::countryRowPadding.left() + nameWidth + st::countryRowPadding.right(), y + st::countryRowPadding.top(), width(), code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +345,8 @@ void CountrySelectBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||
updateSelected(e->pos());
|
||||
|
||||
setPressed(_selected);
|
||||
if (_pressed >= 0 && _pressed < countriesNow->size()) {
|
||||
const auto &list = current();
|
||||
if (_pressed >= 0 && _pressed < list.size()) {
|
||||
if (_ripples.size() <= _pressed) {
|
||||
_ripples.reserve(_pressed + 1);
|
||||
while (_ripples.size() <= _pressed) {
|
||||
@@ -445,55 +375,54 @@ void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
}
|
||||
|
||||
void CountrySelectBox::Inner::updateFilter(QString filter) {
|
||||
auto words = TextUtilities::PrepareSearchWords(filter);
|
||||
const auto words = TextUtilities::PrepareSearchWords(filter);
|
||||
filter = words.isEmpty() ? QString() : words.join(' ');
|
||||
if (_filter != filter) {
|
||||
_filter = filter;
|
||||
|
||||
if (_filter.isEmpty()) {
|
||||
countriesNow = &countriesAll;
|
||||
} else {
|
||||
QChar first = _filter[0].toLower();
|
||||
CountriesIds &ids(countriesByLetter[first]);
|
||||
|
||||
QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
|
||||
|
||||
countriesFiltered.clear();
|
||||
for (CountriesIds::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
|
||||
int index = *i;
|
||||
CountryNames &names(countriesNames[index]);
|
||||
CountryNames::const_iterator nb = names.cbegin(), ne = names.cend(), ni;
|
||||
for (fi = fb; fi != fe; ++fi) {
|
||||
QString filterName(*fi);
|
||||
for (ni = nb; ni != ne; ++ni) {
|
||||
if (ni->startsWith(*fi)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ni == ne) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fi == fe) {
|
||||
countriesFiltered.push_back(countriesAll[index]);
|
||||
}
|
||||
}
|
||||
countriesNow = &countriesFiltered;
|
||||
}
|
||||
refresh();
|
||||
_selected = countriesNow->isEmpty() ? -1 : 0;
|
||||
update();
|
||||
if (_filter == filter) {
|
||||
return;
|
||||
}
|
||||
_filter = filter;
|
||||
|
||||
const auto findWord = [&](
|
||||
const std::vector<QString> &names,
|
||||
const QString &word) {
|
||||
for (const auto &name : names) {
|
||||
if (name.startsWith(word)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const auto hasAllWords = [&](const std::vector<QString> &names) {
|
||||
for (const auto &word : words) {
|
||||
if (!findWord(names, word)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!_filter.isEmpty()) {
|
||||
_filtered.clear();
|
||||
for (const auto index : _byLetter[_filter[0].toLower()]) {
|
||||
const auto &names = _namesList[index];
|
||||
if (hasAllWords(_namesList[index])) {
|
||||
_filtered.push_back(_list[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
_selected = current().empty() ? -1 : 0;
|
||||
update();
|
||||
}
|
||||
|
||||
void CountrySelectBox::Inner::selectSkip(int32 dir) {
|
||||
_mouseSelection = false;
|
||||
|
||||
const auto &list = current();
|
||||
int cur = (_selected >= 0) ? _selected : -1;
|
||||
cur += dir;
|
||||
if (cur <= 0) {
|
||||
_selected = countriesNow->isEmpty() ? -1 : 0;
|
||||
} else if (cur >= countriesNow->size()) {
|
||||
_selected = list.empty() ? -1 : 0;
|
||||
} else if (cur >= list.size()) {
|
||||
_selected = -1;
|
||||
} else {
|
||||
_selected = cur;
|
||||
@@ -511,21 +440,15 @@ void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) {
|
||||
}
|
||||
|
||||
void CountrySelectBox::Inner::chooseCountry() {
|
||||
QString result;
|
||||
if (_filter.isEmpty()) {
|
||||
if (_selected >= 0 && _selected < countriesAll.size()) {
|
||||
result = countriesAll[_selected]->iso2;
|
||||
}
|
||||
} else {
|
||||
if (_selected >= 0 && _selected < countriesFiltered.size()) {
|
||||
result = countriesFiltered[_selected]->iso2;
|
||||
}
|
||||
}
|
||||
emit countryChosen(result);
|
||||
const auto &list = current();
|
||||
emit countryChosen((_selected >= 0 && _selected < list.size())
|
||||
? QString(list[_selected]->iso2)
|
||||
: QString());
|
||||
}
|
||||
|
||||
void CountrySelectBox::Inner::refresh() {
|
||||
resize(width(), countriesNow->length() ? (countriesNow->length() * _rowHeight + st::countriesSkip) : st::noContactsHeight);
|
||||
const auto &list = current();
|
||||
resize(width(), list.empty() ? st::noContactsHeight : (list.size() * _rowHeight + st::countriesSkip));
|
||||
}
|
||||
|
||||
void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
|
||||
@@ -533,7 +456,8 @@ void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
|
||||
|
||||
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(QCursor::pos()));
|
||||
|
||||
auto selected = (in && localPos.y() >= st::countriesSkip && localPos.y() < st::countriesSkip + countriesNow->size() * _rowHeight) ? ((localPos.y() - st::countriesSkip) / _rowHeight) : -1;
|
||||
const auto &list = current();
|
||||
auto selected = (in && localPos.y() >= st::countriesSkip && localPos.y() < st::countriesSkip + list.size() * _rowHeight) ? ((localPos.y() - st::countriesSkip) / _rowHeight) : -1;
|
||||
if (_selected != selected) {
|
||||
updateSelectedRow();
|
||||
_selected = selected;
|
||||
@@ -541,6 +465,11 @@ void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
|
||||
}
|
||||
}
|
||||
|
||||
auto CountrySelectBox::Inner::current() const
|
||||
-> const std::vector<not_null<const Data::CountryInfo*>> & {
|
||||
return _filter.isEmpty() ? _list : _filtered;
|
||||
}
|
||||
|
||||
void CountrySelectBox::Inner::updateSelectedRow() {
|
||||
updateRow(_selected);
|
||||
}
|
||||
|
Reference in New Issue
Block a user