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