1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "ui/countryinput.h"
9
10 #include "lang/lang_keys.h"
11 #include "ui/widgets/scroll_area.h"
12 #include "ui/widgets/multi_select.h"
13 #include "ui/effects/ripple_animation.h"
14 #include "ui/boxes/country_select_box.h"
15 #include "countries/countries_instance.h"
16 #include "styles/style_layers.h"
17 #include "styles/style_boxes.h"
18 #include "styles/style_intro.h"
19
CountryInput(QWidget * parent,const style::InputField & st)20 CountryInput::CountryInput(QWidget *parent, const style::InputField &st)
21 : RpWidget(parent)
22 , _st(st)
23 , _text(tr::lng_country_code(tr::now)) {
24 resize(_st.width, _st.heightMin);
25
26 auto placeholderFont = _st.placeholderFont->f;
27 placeholderFont.setStyleStrategy(QFont::PreferMatch);
28 //auto metrics = QFontMetrics(placeholderFont);
29 auto placeholder = QString();// metrics.elidedText(tr::lng_country_fake_ph(tr::now), Qt::ElideRight, availableWidth);
30 if (!placeholder.isNull()) {
31 _placeholderPath.addText(
32 0,
33 QFontMetrics(placeholderFont).ascent(),
34 placeholderFont,
35 placeholder);
36 }
37 }
38
paintEvent(QPaintEvent * e)39 void CountryInput::paintEvent(QPaintEvent *e) {
40 Painter p(this);
41
42 QRect r(rect().intersected(e->rect()));
43 if (_st.textBg->c.alphaF() > 0.) {
44 p.fillRect(r, _st.textBg);
45 }
46 if (_st.border) {
47 p.fillRect(
48 0,
49 height() - _st.border,
50 width(),
51 _st.border,
52 _st.borderFg);
53 }
54
55 st::introCountryIcon.paint(
56 p,
57 width()
58 - st::introCountryIcon.width()
59 - st::introCountryIconPosition.x(),
60 st::introCountryIconPosition.y(),
61 width());
62
63 p.setFont(_st.font);
64 p.setPen(_st.textFg);
65 p.drawText(rect().marginsRemoved(_st.textMargins), _text, _st.textAlign);
66 if (!_placeholderPath.isEmpty()) {
67 auto placeholderShiftDegree = 1.;
68 p.save();
69 p.setClipRect(r);
70
71 const auto placeholderTop = anim::interpolate(
72 0,
73 _st.placeholderShift,
74 placeholderShiftDegree);
75
76 auto r = QRect(rect() - (_st.textMargins + _st.placeholderMargins));
77 r.moveTop(r.top() + placeholderTop);
78 if (rtl()) {
79 r.moveLeft(width() - r.left() - r.width());
80 }
81
82 const auto placeholderScale = 1.
83 - (1. - _st.placeholderScale) * placeholderShiftDegree;
84 auto placeholderFg = anim::color(
85 _st.placeholderFg,
86 _st.placeholderFgActive,
87 0.);
88 placeholderFg = anim::color(
89 placeholderFg,
90 _st.placeholderFgError,
91 0.);
92
93 PainterHighQualityEnabler hq(p);
94 p.setPen(Qt::NoPen);
95 p.setBrush(placeholderFg);
96 p.translate(r.topLeft());
97 p.scale(placeholderScale, placeholderScale);
98 p.drawPath(_placeholderPath);
99
100 p.restore();
101 }
102 }
103
mouseMoveEvent(QMouseEvent * e)104 void CountryInput::mouseMoveEvent(QMouseEvent *e) {
105 bool newActive = rect().contains(e->pos());
106 if (_active != newActive) {
107 _active = newActive;
108 setCursor(_active ? style::cur_pointer : style::cur_default);
109 }
110 }
111
mousePressEvent(QMouseEvent * e)112 void CountryInput::mousePressEvent(QMouseEvent *e) {
113 mouseMoveEvent(e);
114 if (_active) {
115 const auto box = Ui::show(Box<Ui::CountrySelectBox>());
116 box->entryChosen(
117 ) | rpl::start_with_next([=](
118 const Ui::CountrySelectBox::Entry &entry) {
119 if (box) {
120 box->closeBox();
121 }
122
123 const auto &list = Countries::Instance().list();
124 const auto infoIt = ranges::find(
125 list,
126 entry.iso2,
127 &Countries::Info::iso2);
128 if (infoIt == end(list)) {
129 return;
130 }
131 const auto info = *infoIt;
132 const auto it = ranges::find(
133 info.codes,
134 entry.code,
135 &Countries::CallingCodeInfo::callingCode);
136 if (it != end(info.codes)) {
137 chooseCountry(
138 &info,
139 std::distance(begin(info.codes), it));
140 }
141 }, lifetime());
142 }
143 }
144
enterEventHook(QEnterEvent * e)145 void CountryInput::enterEventHook(QEnterEvent *e) {
146 setMouseTracking(true);
147 }
148
leaveEventHook(QEvent * e)149 void CountryInput::leaveEventHook(QEvent *e) {
150 setMouseTracking(false);
151 _active = false;
152 setCursor(style::cur_default);
153 }
154
onChooseCode(const QString & code)155 void CountryInput::onChooseCode(const QString &code) {
156 Ui::hideLayer();
157 _chosenIso = QString();
158 if (code.length()) {
159 const auto &byCode = Countries::Instance().byCode();
160 const auto i = byCode.constFind(code);
161 if (i != byCode.cend()) {
162 const auto info = *i;
163 _chosenIso = info->iso2;
164 setText(info->name);
165 } else {
166 setText(tr::lng_bad_country_code(tr::now));
167 }
168 } else {
169 setText(tr::lng_country_code(tr::now));
170 }
171 update();
172 }
173
chooseCountry(const QString & iso)174 bool CountryInput::chooseCountry(const QString &iso) {
175 const auto &byISO2 = Countries::Instance().byISO2();
176 const auto i = byISO2.constFind(iso);
177 const auto info = (i != byISO2.cend()) ? (*i) : nullptr;
178
179 _chosenIso = QString();
180 if (info) {
181 chooseCountry(info, 0);
182 return true;
183 }
184 return false;
185 }
186
chooseCountry(not_null<const Countries::Info * > info,int codeIndex)187 void CountryInput::chooseCountry(
188 not_null<const Countries::Info*> info,
189 int codeIndex) {
190 _chosenIso = info->iso2;
191 setText(info->name);
192 _codeChanged.fire_copy(info->codes[codeIndex].callingCode);
193 update();
194 }
195
codeChanged() const196 rpl::producer<QString> CountryInput::codeChanged() const {
197 return _codeChanged.events();
198 }
199
setText(const QString & newText)200 void CountryInput::setText(const QString &newText) {
201 _text = _st.font->elided(
202 newText,
203 width() - _st.textMargins.left() - _st.textMargins.right());
204 }
205